/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#include	<windows.h>
#include	<setupapi.h>

#include	<stdio.h>
#include	<stdlib.h>

#include	"serial_port.h"
#include	"memory_debug.h"
#include	"utils.h"

int	serial_port_list_ready = 0;
SERIAL_PORT_INFO *	serial_port_list = 0;


#define MAX_NAME_PORTS 7
#define RegDisposition_OpenExisting (0x00000001)   // open key only if exists
#define CM_REGISTRY_HARDWARE        (0x00000000)

typedef DWORD
	(WINAPI *OpenDevNodeKeyT)(DWORD, DWORD, DWORD, DWORD, PHKEY, DWORD);
static OpenDevNodeKeyT OpenDevNodeKey = NULL;

HANDLE  BeginEnumeratePorts(VOID)
{
	BOOL guidTest = FALSE;
	DWORD RequiredSize=0;
	HDEVINFO DeviceInfoSet;
	char* buf;

	guidTest = SetupDiClassGuidsFromNameA(
		"Ports",0,0,&RequiredSize);
	if ( RequiredSize < 1 )
		return (HANDLE)-1;
	
	buf = (char*)malloc(RequiredSize*sizeof(GUID));
	
	guidTest = SetupDiClassGuidsFromNameA(
		"Ports",(GUID*)buf,RequiredSize*sizeof(GUID),&RequiredSize);
	
	if ( !guidTest )
		return (HANDLE)-1;
	
	
	DeviceInfoSet = SetupDiGetClassDevs(
		(GUID*)buf,NULL,NULL,DIGCF_PRESENT);
	if ( DeviceInfoSet == INVALID_HANDLE_VALUE )
		return (HANDLE)-1;
	
	free(buf);
	
	return DeviceInfoSet;
}

BOOL EnumeratePortsNext(HANDLE DeviceInfoSet, LPTSTR lpBuffer)
{
	static HINSTANCE CfgMan;
	
	int res1;
	char DevName[MAX_NAME_PORTS]={0};
	static int numDev=0;
	int numport;
	
	SP_DEVINFO_DATA DeviceInfoData = {0};
	DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
	
	
	if ( !DeviceInfoSet || !lpBuffer )
		return -1;
	if ( !OpenDevNodeKey ) {
		CfgMan = LoadLibrary("cfgmgr32");
		if ( !CfgMan )
			return FALSE;
		OpenDevNodeKey = (OpenDevNodeKeyT)
			GetProcAddress(CfgMan,"CM_Open_DevNode_Key");
		if ( !OpenDevNodeKey ) {
			FreeLibrary(CfgMan);
			return FALSE;
		}
	}
	
	while( TRUE ) {
		HKEY KeyDevice;
		DWORD len;
		res1=SetupDiEnumDeviceInfo(
			DeviceInfoSet,numDev,&DeviceInfoData);
		
		if ( !res1 ) {
			SetupDiDestroyDeviceInfoList(DeviceInfoSet);
			FreeLibrary(CfgMan);
			OpenDevNodeKey = NULL;
			return FALSE;
		}
		
		
		res1 = OpenDevNodeKey(DeviceInfoData.DevInst,KEY_QUERY_VALUE,0,
			RegDisposition_OpenExisting,&KeyDevice,CM_REGISTRY_HARDWARE);
		if ( res1 != ERROR_SUCCESS )
			return FALSE;
		len = MAX_NAME_PORTS;
		
		res1 = RegQueryValueEx(
			KeyDevice,    // handle of key to query
			"portname",    // address of name of value to query
			NULL,    // reserved
			NULL,    // address of buffer for value type
			(unsigned char*)DevName,    // address of data buffer
			&len     // address of data buffer size
		);
		
		RegCloseKey(KeyDevice);
		if ( res1 != ERROR_SUCCESS )
			return FALSE;
		numDev++;
		if ( memicmp(DevName, "com", 3) )
			continue;
		numport = atoi(DevName+3);
		if ( numport > 0 && numport <= 256) {
			strcpy(lpBuffer,DevName);
			return TRUE;
		}
		
		FreeLibrary(CfgMan);
		OpenDevNodeKey = NULL;
		return FALSE;
	}
}

BOOL  EndEnumeratePorts(HANDLE DeviceInfoSet)
{
	if ( SetupDiDestroyDeviceInfoList(DeviceInfoSet) )
		return TRUE;
	else
		return FALSE;
}


SERIAL_PORT_INFO *
lookup_serial_port()
{
HANDLE devInfo;
char name[8];
	if ( serial_port_list_ready )
		return serial_port_list;
	serial_port_list_ready = 1;
	
	devInfo = BeginEnumeratePorts();
	while ( EnumeratePortsNext(devInfo, name) ) {
		SERIAL_PORT_INFO *port = d_alloc(sizeof(SERIAL_PORT_INFO));
		port->next = serial_port_list;
		serial_port_list = port;
		
		port->info = port->name = copy_str(name);
	}
	EndEnumeratePorts(devInfo);
	return serial_port_list;
}

