/*++ Author: Doron J. Holan (doronh), 1-22-1998 --*/ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #define GROWTH_VALUE 1024 #define BITS_INA_BYTE 8 typedef struct _DB_INFO { HANDLE RegChangedEvent; HANDLE AccessMutex; HKEY DBKey; PBYTE Ports; ULONG PortsLength; } DB_INFO, * PDB_INFO; #define HandleToDBInfo(h) ((PDB_INFO) (h)) #define IsEventSignalled(hevent) (WaitForSingleObject(hevent, 0) == WAIT_OBJECT_0) #define SanityCheckComNumber(num) { if (num > COMDB_MAX_PORTS_ARBITRATED) return ERROR_INVALID_PARAMETER; } #define SanityCheckDBInfo(dbi) { if ((HANDLE) dbi == INVALID_HANDLE_VALUE) return ERROR_INVALID_PARAMETER; } const TCHAR szMutexName[] = _T("ComPortNumberDatabaseMutexObject"); const TCHAR szComDBName[] = _T("ComDB"); const TCHAR szComDBMerge[] = _T("ComDB Merge"); const TCHAR szComDBPath[] = _T("System\\CurrentControlSet\\Control\\COM Name Arbiter"); const TCHAR szComDBPathOld[] = _T("System\\CurrentControlSet\\Services\\Serial"); #ifdef malloc #undef malloc #endif #define malloc(size) LocalAlloc(LPTR, (size)) #ifdef free #undef free #endif #define free LocalFree VOID DestroyDBInfo( PDB_INFO DBInfo ) { if (DBInfo->AccessMutex && DBInfo->AccessMutex != INVALID_HANDLE_VALUE) { CloseHandle(DBInfo->AccessMutex); } if (DBInfo->RegChangedEvent && DBInfo->RegChangedEvent != INVALID_HANDLE_VALUE) { CloseHandle(DBInfo->RegChangedEvent); } if (DBInfo->DBKey && DBInfo->DBKey != (HKEY) INVALID_HANDLE_VALUE) { RegCloseKey(DBInfo->DBKey); } if (DBInfo->Ports) { free(DBInfo->Ports); } free(DBInfo); } LONG CreationFailure ( PHCOMDB PHComDB, PDB_INFO DBInfo ) { if (DBInfo->AccessMutex != 0) ReleaseMutex(DBInfo->AccessMutex); DestroyDBInfo(DBInfo); *PHComDB = (HCOMDB) INVALID_HANDLE_VALUE; return ERROR_ACCESS_DENIED; } VOID RegisterForNotification( PDB_INFO DBInfo ) { ResetEvent(DBInfo->RegChangedEvent); if (RegNotifyChangeKeyValue(DBInfo->DBKey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, DBInfo->RegChangedEvent, TRUE) != ERROR_SUCCESS) { // // Can't get a notification of when the DB is changed so close the handle // and we must update the DB at every access no matter what // CloseHandle(DBInfo->RegChangedEvent); DBInfo->RegChangedEvent = INVALID_HANDLE_VALUE; } } BOOL ResizeDatabase( PDB_INFO DBInfo, ULONG NumberPorts ) { PBYTE newPorts = NULL; ULONG newPortsLength; if (DBInfo->Ports) { newPortsLength = NumberPorts / BITS_INA_BYTE; newPorts = (PBYTE) malloc(newPortsLength * sizeof(BYTE)); if (newPorts) { memcpy(newPorts, DBInfo->Ports, DBInfo->PortsLength); free(DBInfo->Ports); DBInfo->Ports = newPorts; DBInfo->PortsLength = newPortsLength; return TRUE; } else { return FALSE; } } else { // // Just alloc and be done with it // DBInfo->PortsLength = NumberPorts / BITS_INA_BYTE; DBInfo->Ports = (PBYTE) malloc(DBInfo->PortsLength * sizeof(BYTE)); return DBInfo->Ports ? TRUE : FALSE; } } LONG WINAPI ComDBOpen ( PHCOMDB PHComDB ) /*++ Routine Description: Opens name data base, and returns a handle to be used in future calls. Arguments: None. Return Value: INVALID_HANDLE_VALUE if the call fails, otherwise a valid handle If INVALID_HANDLE_VALUE, call GetLastError() to get details (??) --*/ { PDB_INFO dbInfo = malloc(sizeof(DB_INFO)); DWORD type, size, disposition = 0x0; BOOLEAN migrated = FALSE; LONG res; BYTE merge[COMDB_MIN_PORTS_ARBITRATED / BITS_INA_BYTE /* 32 */]; if (dbInfo == 0) { *PHComDB = (HCOMDB) INVALID_HANDLE_VALUE; return ERROR_ACCESS_DENIED; } dbInfo->AccessMutex = CreateMutex(NULL, FALSE, szMutexName); if (dbInfo->AccessMutex == 0) { return CreationFailure(PHComDB, dbInfo); } // // Enter the mutex so we can guarantee only one thread pounding on the reg // key at once // WaitForSingleObject(dbInfo->AccessMutex, INFINITE); if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szComDBPath, 0, (TCHAR *) NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_NOTIFY, (LPSECURITY_ATTRIBUTES) NULL, &dbInfo->DBKey, &disposition) != ERROR_SUCCESS) { // // Try again w/out notification caps // if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szComDBPath, 0, (TCHAR *) NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, (LPSECURITY_ATTRIBUTES) NULL, &dbInfo->DBKey, &disposition) != ERROR_SUCCESS) { return CreationFailure(PHComDB, dbInfo); } dbInfo->RegChangedEvent = INVALID_HANDLE_VALUE; } else { dbInfo->RegChangedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (dbInfo->RegChangedEvent == 0) { dbInfo->RegChangedEvent = INVALID_HANDLE_VALUE; } } if (disposition == REG_CREATED_NEW_KEY) { // // Must migrate the previous values from the old com db path // HKEY hOldDB; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szComDBPathOld, 0, KEY_ALL_ACCESS, &hOldDB) == ERROR_SUCCESS && RegQueryValueEx(hOldDB, szComDBName, 0, &type, NULL, &dbInfo->PortsLength) == ERROR_SUCCESS) { // // The old value is still there, get its contents, copy it to the // new location and delete the old value // migrated = TRUE; ResizeDatabase(dbInfo, dbInfo->PortsLength * BITS_INA_BYTE); size = dbInfo->PortsLength; res = RegQueryValueEx(hOldDB, szComDBName, 0, &type, (PBYTE) dbInfo->Ports, &size); RegDeleteValue(hOldDB, szComDBName); // // The value does not exist, write it out // if (RegSetValueEx(dbInfo->DBKey, szComDBName, 0, REG_BINARY, dbInfo->Ports, dbInfo->PortsLength) != ERROR_SUCCESS) { RegCloseKey(hOldDB); return CreationFailure(PHComDB, dbInfo); } RegCloseKey(hOldDB); } } // // If we haven't migrated values from the old path, then either create a // new chunk or read in values previously written // if (!migrated) { res = RegQueryValueEx(dbInfo->DBKey, szComDBName, 0, &type, NULL, &dbInfo->PortsLength); if (res == ERROR_FILE_NOT_FOUND) { ResizeDatabase(dbInfo, COMDB_MIN_PORTS_ARBITRATED); // // The value does not exist, write it out // res = RegSetValueEx(dbInfo->DBKey, szComDBName, 0, REG_BINARY, dbInfo->Ports, dbInfo->PortsLength); if (res != ERROR_SUCCESS) { return CreationFailure(PHComDB, dbInfo); } } else if (res == ERROR_MORE_DATA || res != ERROR_SUCCESS || type != REG_BINARY) { return CreationFailure(PHComDB, dbInfo); } else if (res == ERROR_SUCCESS) { ResizeDatabase(dbInfo, dbInfo->PortsLength * BITS_INA_BYTE); size = dbInfo->PortsLength; res = RegQueryValueEx(dbInfo->DBKey, szComDBName, 0, &type, (PBYTE) dbInfo->Ports, &size); } } size = sizeof(merge); if (RegQueryValueEx(dbInfo->DBKey, szComDBMerge, 0, &type, (PBYTE) merge, &size) == ERROR_SUCCESS && size <= dbInfo->PortsLength) { int i; for (i = 0 ; i < COMDB_MIN_PORTS_ARBITRATED / BITS_INA_BYTE; i++) { dbInfo->Ports[i] |= merge[i]; } RegDeleteValue(dbInfo->DBKey, szComDBMerge); RegSetValueEx(dbInfo->DBKey, szComDBName, 0, REG_BINARY, dbInfo->Ports, dbInfo->PortsLength); } if (dbInfo->RegChangedEvent != INVALID_HANDLE_VALUE) { RegisterForNotification(dbInfo); } ReleaseMutex(dbInfo->AccessMutex); // // All done! phew... // *PHComDB = (HCOMDB) dbInfo; return ERROR_SUCCESS; } LONG WINAPI ComDBClose ( HCOMDB HComDB ) /*++ Routine Description: frees a handle to the database returned from OpenComPortDataBase Arguments: Handle returned from OpenComPortDataBase. Return Value: None --*/ { PDB_INFO dbInfo = HandleToDBInfo(HComDB); SanityCheckDBInfo(dbInfo); DestroyDBInfo(dbInfo); return ERROR_SUCCESS; } BOOL EnterDB( PDB_INFO DBInfo ) { BOOL eventSignalled = FALSE; LONG res; DWORD type, size; WaitForSingleObject(DBInfo->AccessMutex, INFINITE); if (DBInfo->RegChangedEvent == INVALID_HANDLE_VALUE || (eventSignalled = IsEventSignalled(DBInfo->RegChangedEvent))) { size = 0; res = RegQueryValueEx(DBInfo->DBKey, szComDBName, 0, &type, 0, &size); // // Couldn't update the DB ... fail // if (res != ERROR_SUCCESS || type != REG_BINARY) { ReleaseMutex(DBInfo->AccessMutex); return FALSE; } if (size != DBInfo->PortsLength) { ResizeDatabase(DBInfo, size * BITS_INA_BYTE); } RegQueryValueEx(DBInfo->DBKey, szComDBName, 0, &type, DBInfo->Ports, &size); // // Reregister the notification with the registry // if (eventSignalled) { RegisterForNotification(DBInfo); } } return TRUE; } LONG LeaveDB( PDB_INFO DBInfo, BOOL CommitChanges ) { LONG retVal = ERROR_SUCCESS; if (CommitChanges) { if (RegSetValueEx(DBInfo->DBKey, szComDBName, 0, REG_BINARY, DBInfo->Ports, DBInfo->PortsLength) != ERROR_SUCCESS) { retVal = ERROR_CANTWRITE; } // // The setting of the value in the reg signals the event...but we don't // need to resync w/the reg off of this change b/c it is our own! Instead // reset the event and rereg for the event // if (DBInfo->RegChangedEvent != INVALID_HANDLE_VALUE) { RegisterForNotification(DBInfo); } } ReleaseMutex(DBInfo->AccessMutex); return retVal; } VOID GetByteAndMask( PDB_INFO DBInfo, DWORD ComNumber, PBYTE *Byte, PBYTE Mask ) { ComNumber--; *Byte = DBInfo->Ports + (ComNumber / BITS_INA_BYTE); *Mask = 1 << (ComNumber % BITS_INA_BYTE); } LONG WINAPI ComDBGetCurrentPortUsage ( HCOMDB HComDB, PBYTE Buffer, DWORD BufferSize, ULONG ReportType, LPDWORD MaxPortsReported ) /*++ Handle requests that require no synch w/DB first. --*/ { PDB_INFO dbInfo = HandleToDBInfo(HComDB); PBYTE curSrc, curDest, endDest; BYTE mask; SanityCheckDBInfo(dbInfo); if (!EnterDB(dbInfo)) { return ERROR_NOT_CONNECTED; } if (Buffer == 0) { if (!MaxPortsReported) { LeaveDB(dbInfo, FALSE); return ERROR_INVALID_PARAMETER; } else { *MaxPortsReported = dbInfo->PortsLength * BITS_INA_BYTE; return LeaveDB(dbInfo, FALSE); } } if (ReportType == CDB_REPORT_BITS) { if (BufferSize > dbInfo->PortsLength) { BufferSize = dbInfo->PortsLength; } memcpy(Buffer, dbInfo->Ports, BufferSize); if (MaxPortsReported) { *MaxPortsReported = BufferSize * BITS_INA_BYTE; } } else if (ReportType == CDB_REPORT_BYTES) { if (BufferSize > dbInfo->PortsLength * BITS_INA_BYTE) { BufferSize = dbInfo->PortsLength * BITS_INA_BYTE; } curSrc = dbInfo->Ports; endDest = Buffer + BufferSize; curDest = Buffer; for (mask = 1; curDest != endDest; curDest++) { *curDest = (*curSrc & mask) ? 1 : 0; if (mask & 0x80) { mask = 0x1; curSrc++; } else mask <<= 1; } } else { LeaveDB(dbInfo, FALSE); return ERROR_INVALID_PARAMETER; } return LeaveDB(dbInfo, FALSE); } LONG WINAPI ComDBClaimNextFreePort ( HCOMDB HComDB, LPDWORD ComNumber ) /*++ Routine Description: returns the first free COMx value Arguments: Handle returned from OpenComPortDataBase. Return Value: returns ERROR_SUCCESS if successful. or other ERROR_ if not if successful, then ComNumber will be that next free com value and claims it in the database --*/ { PDB_INFO dbInfo = HandleToDBInfo(HComDB); DWORD num; BOOL commit = FALSE; PBYTE curSrc, srcEnd; BYTE mask; LONG ret; SanityCheckDBInfo(dbInfo); if (!EnterDB(dbInfo)) { return ERROR_NOT_CONNECTED; } curSrc = dbInfo->Ports; srcEnd = curSrc + dbInfo->PortsLength; for (num = 3, mask = 0x4; curSrc != srcEnd; num++) { if (!(*curSrc & mask)) { *ComNumber = num; *curSrc |= mask; commit = TRUE; break; } else if (mask & 0x80) { mask = 0x1; curSrc++; } else { mask <<= 1; } } if (curSrc == srcEnd && !commit && num < COMDB_MAX_PORTS_ARBITRATED) { // DB entirely full ResizeDatabase(dbInfo, ((num / GROWTH_VALUE) + 1) * GROWTH_VALUE); *ComNumber = num; GetByteAndMask(dbInfo, num, &curSrc, &mask); *curSrc |= mask; commit = TRUE; } ret = LeaveDB(dbInfo, commit); if (!commit) { ret = ERROR_NO_LOG_SPACE; } return ret; } LONG WINAPI ComDBClaimPort ( HCOMDB HComDB, DWORD ComNumber, BOOL ForceClaim, PBOOL Forced ) /*++ Routine Description: Attempts to claim a com name in the database Arguments: DataBaseHandle - returned from OpenComPortDataBase. ComNumber - The port value to be claimed Force - If TRUE, will force the port to be claimed even if in use already Return Value: returns ERROR_SUCCESS if port name was not already claimed, or if it was claimed and Force was TRUE. ERROR_SHARING_VIOLATION if port name is use and Force is false --*/ { PDB_INFO dbInfo = HandleToDBInfo(HComDB); PBYTE curByte; BYTE mask; BOOL commit = TRUE; LONG res; ULONG newSize; BOOL f; if (!(Forced)) { Forced = &f; } SanityCheckComNumber(ComNumber); SanityCheckDBInfo(dbInfo); if (!EnterDB(dbInfo)) { return ERROR_NOT_CONNECTED; } if (ComNumber > dbInfo->PortsLength * BITS_INA_BYTE) { ResizeDatabase(dbInfo, ((ComNumber / GROWTH_VALUE) + 1) * GROWTH_VALUE); } GetByteAndMask(dbInfo, ComNumber, &curByte, &mask); if (*curByte & mask) { commit = FALSE; if (ForceClaim) { if (Forced) *Forced = TRUE; } else { res = LeaveDB(dbInfo, commit); if (res == ERROR_SUCCESS) { return ERROR_SHARING_VIOLATION; } else { return res; } } } else { if (Forced) *Forced = FALSE; *curByte |= mask; } return LeaveDB(dbInfo, commit); } LONG WINAPI ComDBReleasePort ( HCOMDB HComDB, DWORD ComNumber ) /*++ Routine Description: un-claims the port in the database Arguments: DatabaseHandle - returned from OpenComPortDataBase. ComNumber - port to be unclaimed in database Return Value: returns ERROR_SUCCESS if successful. or other ERROR_ if not --*/ { PDB_INFO dbInfo = HandleToDBInfo(HComDB); PBYTE byte; BYTE mask; SanityCheckDBInfo(dbInfo); if (!EnterDB(dbInfo)) { return ERROR_NOT_CONNECTED; } if (ComNumber > dbInfo->PortsLength * BITS_INA_BYTE) { LeaveDB(dbInfo, FALSE); return ERROR_INVALID_PARAMETER; } GetByteAndMask(dbInfo, ComNumber, &byte, &mask); *byte &= ~mask; return LeaveDB(dbInfo, TRUE); } LONG WINAPI ComDBResizeDatabase ( HCOMDB HComDB, DWORD NewSize ) /*++ Routine Description: Resizes the database to the new size. To get the current size, call ComDBGetCurrentPortUsage with a Buffer == NULL. Arguments: DatabaseHandle - returned from OpenComPortDataBase. NewSize - must be a multiple of 1024, with a max of 4096 Return Value: returns ERROR_SUCCESS if successful ERROR_BAD_LENGTH if NewSize is not greater than the current size or NewSize is greater than COMDB_MAX_PORTS_ARBITRATED --*/ { PDB_INFO dbInfo = HandleToDBInfo(HComDB); BOOL commit = FALSE; SanityCheckDBInfo(dbInfo); if (NewSize % GROWTH_VALUE) { return ERROR_INVALID_PARAMETER; } if (!EnterDB(dbInfo)) { return ERROR_NOT_CONNECTED; } if (NewSize > COMDB_MAX_PORTS_ARBITRATED || dbInfo->PortsLength * BITS_INA_BYTE >= NewSize) { LeaveDB(dbInfo, FALSE); return ERROR_BAD_LENGTH; } ResizeDatabase(dbInfo, NewSize); return LeaveDB(dbInfo, TRUE); }