diff --git a/src/main/c/Posix/PosixHelperFunctions.c b/src/main/c/Posix/PosixHelperFunctions.c index e550364..d197ee7 100644 --- a/src/main/c/Posix/PosixHelperFunctions.c +++ b/src/main/c/Posix/PosixHelperFunctions.c @@ -2,7 +2,7 @@ * PosixHelperFunctions.c * * Created on: Mar 10, 2015 - * Last Updated on: Dec 06, 2021 + * Last Updated on: Dec 16, 2021 * Author: Will Hedgecock * * Copyright (C) 2012-2021 Fazecast, Inc. @@ -35,7 +35,7 @@ #include "PosixHelperFunctions.h" // Common storage functionality -serialPort* pushBack(serialPortVector* vector, const char* key, const char* friendlyName, const char* description) +serialPort* pushBack(serialPortVector* vector, const char* key, const char* friendlyName, const char* description, const char* location) { // Allocate memory for the new SerialPort storage structure if (vector->capacity == vector->length) @@ -60,11 +60,13 @@ serialPort* pushBack(serialPortVector* vector, const char* key, const char* frie port->handle = -1; port->enumerated = 1; port->portPath = (char*)malloc(strlen(key) + 1); + port->portLocation = (char*)malloc(strlen(location) + 1); port->friendlyName = (char*)malloc(strlen(friendlyName) + 1); port->portDescription = (char*)malloc(strlen(description) + 1); // Store port strings strcpy(port->portPath, key); + strcpy(port->portLocation, location); strcpy(port->friendlyName, friendlyName); strcpy(port->portDescription, description); @@ -84,6 +86,7 @@ void removePort(serialPortVector* vector, serialPort* port) { // Clean up memory associated with the port free(port->portPath); + free(port->portLocation); free(port->friendlyName); free(port->portDescription); if (port->readBuffer) @@ -181,6 +184,137 @@ void getInterfaceDescription(const char* interfaceFile, char* interfaceDescripti } } +char getPortLocation(const char* portDirectory, char* portLocation) +{ + // Set location of busnum and devpath files + char isUSB = 1; + char* busnumFile = (char*)malloc(strlen(portDirectory) + 16); + strcpy(busnumFile, portDirectory); + strcat(busnumFile, "/busnum"); + char* devpathFile = (char*)malloc(strlen(portDirectory) + 16); + strcpy(devpathFile, portDirectory); + strcat(devpathFile, "/devpath"); + int portLocationLength = 0; + portLocation[0] = '\0'; + + // Read the bus number + FILE *input = fopen(busnumFile, "rb"); + if (input) + { + char ch = getc(input); + while ((ch != '\n') && (ch != EOF)) + { + portLocation[portLocationLength++] = ch; + ch = getc(input); + } + portLocation[portLocationLength++] = '-'; + fclose(input); + } + else + { + isUSB = 0; + portLocation[portLocationLength++] = '0'; + portLocation[portLocationLength++] = '-'; + } + + // Read the device path + input = fopen(devpathFile, "rb"); + if (input) + { + char ch = getc(input); + while ((ch != '\n') && (ch != EOF)) + { + portLocation[portLocationLength++] = ch; + ch = getc(input); + } + portLocation[portLocationLength] = '\0'; + fclose(input); + } + else + { + isUSB = 0; + portLocation[portLocationLength++] = '0'; + } + + // Clean up the dynamic memory + free(devpathFile); + free(busnumFile); + return isUSB; +} + +char driverGetPortLocation(char topLevel, const char *fullPathToSearch, const char *deviceName, char* portLocation, char searchBackwardIteration) +{ + // Open the linux USB device directory + char isUSB = 0; + DIR *directoryIterator = opendir(fullPathToSearch); + if (!directoryIterator) + return isUSB; + + if (!searchBackwardIteration) + { + // Read all sub-directories in the current directory + struct dirent *directoryEntry = readdir(directoryIterator); + while (directoryEntry && !isUSB) + { + // Check if entry is a sub-directory + if ((topLevel || (directoryEntry->d_type == DT_DIR)) && (directoryEntry->d_name[0] != '.')) + { + // Set up the next directory to search + char* nextDirectory = (char*)malloc(strlen(fullPathToSearch) + strlen(directoryEntry->d_name) + 5); + strcpy(nextDirectory, fullPathToSearch); + strcat(nextDirectory, directoryEntry->d_name); + + // Only process directories that match the device name + if (strcmp(directoryEntry->d_name, deviceName) == 0) + { + strcat(nextDirectory, "/.."); + isUSB = driverGetPortLocation(0, nextDirectory, deviceName, portLocation, 1); + } + else + { + // Search for more serial ports within the directory + strcat(nextDirectory, "/"); + isUSB = driverGetPortLocation(0, nextDirectory, deviceName, portLocation, 0); + } + free(nextDirectory); + } + directoryEntry = readdir(directoryIterator); + } + } + else + { + // Read all files in the current directory + char hasBusnum = 0, hasDevpath = 0; + struct dirent *directoryEntry = readdir(directoryIterator); + while (directoryEntry) + { + // Check if entry is a regular file with the expected name + if (directoryEntry->d_type == DT_REG) + { + if (strcmp(directoryEntry->d_name, "busnum") == 0) + hasBusnum = 1; + else if (strcmp(directoryEntry->d_name, "devpath") == 0) + hasDevpath = 1; + } + directoryEntry = readdir(directoryIterator); + } + + // Check if the current directory has the required information files + if ((!hasBusnum || !hasDevpath || !(isUSB = getPortLocation(fullPathToSearch, portLocation))) && (searchBackwardIteration < 10)) + { + char* nextDirectory = (char*)malloc(strlen(fullPathToSearch) + 5); + strcpy(nextDirectory, fullPathToSearch); + strcat(nextDirectory, "/.."); + isUSB = driverGetPortLocation(0, nextDirectory, deviceName, portLocation, searchBackwardIteration + 1); + free(nextDirectory); + } + } + + // Close the directory + closedir(directoryIterator); + return isUSB; +} + void recursiveSearchForComPorts(serialPortVector* comPorts, const char* fullPathToSearch) { // Open the directory @@ -215,11 +349,16 @@ void recursiveSearchForComPorts(serialPortVector* comPorts, const char* fullPath else { // See if device has a registered friendly name + char* portLocation = (char*)malloc(128); char* friendlyName = (char*)malloc(256); char* productFile = (char*)malloc(strlen(fullPathToSearch) + strlen(directoryEntry->d_name) + 30); strcpy(productFile, fullPathToSearch); strcat(productFile, directoryEntry->d_name); - strcat(productFile, "/device/../product"); + strcat(productFile, "/device/.."); + char isUSB = getPortLocation(productFile, portLocation); + if (!isUSB) + isUSB = driverGetPortLocation(1, "/sys/bus/usb/devices/", directoryEntry->d_name, portLocation, 0); + strcat(productFile, "/product"); getFriendlyName(productFile, friendlyName); if (friendlyName[0] == '\0') // Must be a physical (or emulated) port { @@ -240,14 +379,14 @@ void recursiveSearchForComPorts(serialPortVector* comPorts, const char* fullPath { strcpy(friendlyName, "Bluetooth Port "); strcat(friendlyName, directoryEntry->d_name); - pushBack(comPorts, systemName, friendlyName, friendlyName); + pushBack(comPorts, systemName, friendlyName, friendlyName, portLocation); } else if (((strlen(directoryEntry->d_name) >= 6) && (directoryEntry->d_name[3] == 'A') && (directoryEntry->d_name[4] == 'M') && (directoryEntry->d_name[5] == 'A')) || ((ioctl(fd, TIOCGSERIAL, &serialInfo) == 0) && (serialInfo.type != PORT_UNKNOWN))) { strcpy(friendlyName, "Physical Port "); strcat(friendlyName, directoryEntry->d_name+3); - pushBack(comPorts, systemName, friendlyName, friendlyName); + pushBack(comPorts, systemName, friendlyName, friendlyName, portLocation); } close(fd); } @@ -270,7 +409,7 @@ void recursiveSearchForComPorts(serialPortVector* comPorts, const char* fullPath } if (interfaceDescription[0] == '\0') strcpy(interfaceDescription, friendlyName); - pushBack(comPorts, systemName, friendlyName, interfaceDescription); + pushBack(comPorts, systemName, friendlyName, interfaceDescription, portLocation); // Clean up memory free(interfaceFile); @@ -295,7 +434,7 @@ void recursiveSearchForComPorts(serialPortVector* comPorts, const char* fullPath } if (interfaceDescription[0] == '\0') strcpy(interfaceDescription, friendlyName); - pushBack(comPorts, systemName, friendlyName, interfaceDescription); + pushBack(comPorts, systemName, friendlyName, interfaceDescription, portLocation); // Clean up memory free(interfaceFile); @@ -305,6 +444,7 @@ void recursiveSearchForComPorts(serialPortVector* comPorts, const char* fullPath // Clean up memory free(productFile); free(friendlyName); + free(portLocation); } // Clean up memory @@ -361,7 +501,7 @@ void driverBasedSearchForComPorts(serialPortVector* comPorts, const char* fullPa strcat(friendlyName, serialLine); // Add the port to the list - pushBack(comPorts, systemName, friendlyName, friendlyName); + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0"); // Clean up memory free(friendlyName); @@ -405,14 +545,17 @@ void lastDitchSearchForComPorts(serialPortVector* comPorts) else { // Set static friendly name + char* portLocation = (char*)malloc(128); char* friendlyName = (char*)malloc(256); strcpy(friendlyName, "USB-Based Serial Port"); // Add the port to the list - pushBack(comPorts, systemName, friendlyName, friendlyName); + char isUSB = driverGetPortLocation(1, "/sys/bus/usb/devices/", directoryEntry->d_name, portLocation, 0); + pushBack(comPorts, systemName, friendlyName, friendlyName, portLocation); // Clean up memory free(friendlyName); + free(portLocation); } // Clean up memory @@ -433,14 +576,17 @@ void lastDitchSearchForComPorts(serialPortVector* comPorts) else { // Set static friendly name + char* portLocation = (char*)malloc(128); char* friendlyName = (char*)malloc(256); strcpy(friendlyName, "Advantech Extended Serial Port"); // Add the port to the list - pushBack(comPorts, systemName, friendlyName, friendlyName); + char isUSB = driverGetPortLocation(1, "/sys/bus/usb/devices/", directoryEntry->d_name, portLocation, 0); + pushBack(comPorts, systemName, friendlyName, friendlyName, portLocation); // Clean up memory free(friendlyName); + free(portLocation); } // Clean up memory @@ -465,7 +611,7 @@ void lastDitchSearchForComPorts(serialPortVector* comPorts) strcpy(friendlyName, "Bluetooth-Based Serial Port"); // Add the port to the list - pushBack(comPorts, systemName, friendlyName, friendlyName); + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0"); // Clean up memory free(friendlyName); @@ -700,7 +846,7 @@ void searchForComPorts(serialPortVector* comPorts) struct stat fileStats; stat(systemName, &fileStats); if (!S_ISDIR(fileStats.st_mode)) - pushBack(comPorts, systemName, friendlyName, friendlyName); + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0"); // Clean up memory free(friendlyName); @@ -746,7 +892,7 @@ void searchForComPorts(serialPortVector* comPorts) struct stat fileStats; stat(systemName, &fileStats); if (!S_ISDIR(fileStats.st_mode)) - pushBack(comPorts, systemName, friendlyName, friendlyName); + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0"); // Clean up memory free(friendlyName); @@ -890,6 +1036,7 @@ void searchForComPorts(serialPortVector* comPorts) else { // Set static friendly name + char *location = (char*)malloc(256); char* friendlyName = (char*)malloc(256); if (directoryEntry->d_name[0] == 'c') strcpy(friendlyName, "Serial Port"); @@ -900,10 +1047,66 @@ void searchForComPorts(serialPortVector* comPorts) struct stat fileStats; stat(systemName, &fileStats); if (!S_ISDIR(fileStats.st_mode)) - pushBack(comPorts, systemName, friendlyName, friendlyName); + { + size_t bufferSize = 1024; + char *stdOutResult = (char*)malloc(bufferSize), *device = NULL; + snprintf(stdOutResult, bufferSize, "sysctl -a | grep \"ttyname: %s\"", directoryEntry->d_name + 3); + FILE *pipe = popen(stdOutResult, "r"); + if (pipe) + { + while (!device && fgets(stdOutResult, bufferSize, pipe)) + { + device = stdOutResult; + *(strstr(device, "ttyname:") - 1) = '\0'; + strcat(device, ".%location"); + } + pclose(pipe); + } + + // Add port to the list and clean up memory + if (device) + { + char *location = (char*)malloc(256), *temp = (char*)malloc(64); + snprintf(location, bufferSize, "sysctl -a | grep \"%s\"", device); + pipe = popen(location, "r"); + strcpy(location, "0-0"); + if (pipe) + { + while (fgets(stdOutResult, bufferSize, pipe)) + if (strstr(stdOutResult, "bus") && strstr(stdOutResult, "hubaddr") && strstr(stdOutResult, "port")) + { + char *cursor = strstr(stdOutResult, "bus=") + 4; + size_t length = (size_t)(strchr(cursor, ' ') - cursor); + memcpy(location, cursor, length); + location[length] = '\0'; + strcat(location, "-"); + cursor = strstr(stdOutResult, "hubaddr=") + 8; + length = (size_t)(strchr(cursor, ' ') - cursor); + memcpy(temp, cursor, length); + temp[length] = '\0'; + strcat(location, temp); + strcat(location, "."); + cursor = strstr(stdOutResult, "port=") + 5; + length = (size_t)(strchr(cursor, ' ') - cursor); + memcpy(temp, cursor, length); + temp[length] = '\0'; + strcat(location, temp); + break; + } + pclose(pipe); + } + pushBack(comPorts, systemName, friendlyName, friendlyName, location); + free(location); + free(temp); + } + else + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0"); + free(stdOutResult); + } // Clean up memory free(friendlyName); + free(location); } // Clean up memory @@ -942,31 +1145,25 @@ int setBaudRateCustom(int portFD, baud_rate baudRate) void searchForComPorts(serialPortVector* comPorts) { serialPort *port; - int numValues = 0; io_object_t serialPort; io_iterator_t serialPortIterator; - char friendlyName[1024], comPortCu[1024], comPortTty[1024], portDescription[1024]; + char friendlyName[1024], comPortCu[1024], comPortTty[1024]; + char portLocation[1024], portDescription[1024]; // Enumerate serial ports on machine IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(kIOSerialBSDServiceValue), &serialPortIterator); while ((serialPort = IOIteratorNext(serialPortIterator))) - { - ++numValues; - IOObjectRelease(serialPort); - } - IOIteratorReset(serialPortIterator); - for (int i = 0; i < numValues; ++i) { // Get serial port information + char isUSB = 0; friendlyName[0] = '\0'; - serialPort = IOIteratorNext(serialPortIterator); - io_registry_entry_t parent = 0; - io_registry_entry_t service = serialPort; + io_registry_entry_t parent = 0, service = serialPort; while (service) { if (IOObjectConformsTo(service, "IOUSBDevice")) { IORegistryEntryGetName(service, friendlyName); + isUSB = 1; break; } if (IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent) != KERN_SUCCESS) @@ -992,12 +1189,38 @@ void searchForComPorts(serialPortVector* comPorts) CFStringGetCString(comPortRef, comPortTty, sizeof(comPortTty), kCFStringEncodingUTF8); CFRelease(comPortRef); + // Get VID, PID, Serial Number, Bus Number, and Port Address + if (isUSB) + { + CFTypeRef propertyRef = IORegistryEntrySearchCFProperty(serialPort, kIOServicePlane, CFSTR("locationID"), kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); + if (propertyRef) + { + int locationID = 0; + char multiHub = 0, tempLocation[64]; + CFNumberGetValue(propertyRef, kCFNumberIntType, &locationID); + CFRelease(propertyRef); + snprintf(portLocation, sizeof(portLocation), "%d", (locationID >> 24) & 0x000000FF); + strcat(portLocation, "-"); + while (locationID & 0x00F00000) + { + if (multiHub) + strcat(portLocation, "."); + snprintf(tempLocation, sizeof(tempLocation), "%d", (locationID >> 20) & 0x0000000F); + strcat(portLocation, tempLocation); + locationID <<= 4; + multiHub = 1; + } + } + } + else + strcpy(portLocation, "0-0"); + // Check if callout port is already enumerated port = fetchPort(comPorts, comPortCu); if (port) port->enumerated = 1; else - pushBack(comPorts, comPortCu, friendlyName, friendlyName); + pushBack(comPorts, comPortCu, friendlyName, friendlyName, portLocation); // Check if dialin port is already enumerated port = fetchPort(comPorts, comPortTty); @@ -1005,7 +1228,7 @@ void searchForComPorts(serialPortVector* comPorts) if (port) port->enumerated = 1; else - pushBack(comPorts, comPortTty, friendlyName, friendlyName); + pushBack(comPorts, comPortTty, friendlyName, friendlyName, portLocation); IOObjectRelease(serialPort); } IOObjectRelease(serialPortIterator); diff --git a/src/main/c/Posix/PosixHelperFunctions.h b/src/main/c/Posix/PosixHelperFunctions.h index 78c51c6..fe4295f 100644 --- a/src/main/c/Posix/PosixHelperFunctions.h +++ b/src/main/c/Posix/PosixHelperFunctions.h @@ -2,7 +2,7 @@ * PosixHelperFunctions.h * * Created on: Mar 10, 2015 - * Last Updated on: Nov 30, 2021 + * Last Updated on: Dec 16, 2021 * Author: Will Hedgecock * * Copyright (C) 2012-2021 Fazecast, Inc. @@ -32,7 +32,7 @@ // Serial port data structure typedef struct serialPort { - char *portPath, *friendlyName, *portDescription, *readBuffer; + char *portPath, *friendlyName, *portDescription, *portLocation, *readBuffer; int errorLineNumber, errorNumber, handle, readBufferLength; volatile char enumerated, eventListenerRunning; short eventsMask; @@ -44,7 +44,7 @@ typedef struct serialPortVector serialPort **ports; int length, capacity; } serialPortVector; -serialPort* pushBack(serialPortVector* vector, const char* key, const char* friendlyName, const char* description); +serialPort* pushBack(serialPortVector* vector, const char* key, const char* friendlyName, const char* description, const char* location); serialPort* fetchPort(serialPortVector* vector, const char* key); void removePort(serialPortVector* vector, serialPort* port); diff --git a/src/main/c/Posix/SerialPort_Posix.c b/src/main/c/Posix/SerialPort_Posix.c index a903b7a..9536834 100644 --- a/src/main/c/Posix/SerialPort_Posix.c +++ b/src/main/c/Posix/SerialPort_Posix.c @@ -2,7 +2,7 @@ * SerialPort_Posix.c * * Created on: Feb 25, 2012 - * Last Updated on: Dec 07, 2021 + * Last Updated on: Dec 16, 2021 * Author: Will Hedgecock * * Copyright (C) 2012-2021 Fazecast, Inc. @@ -54,6 +54,7 @@ jfieldID disableConfigField; jfieldID isDtrEnabledField; jfieldID isRtsEnabledField; jfieldID autoFlushIOBuffersField; +jfieldID portLocationField; jfieldID baudRateField; jfieldID dataBitsField; jfieldID stopBitsField; @@ -76,7 +77,7 @@ jfieldID writeTimeoutField; jfieldID eventFlagsField; // List of available serial ports -serialPortVector serialPorts = { NULL, 0 }; +serialPortVector serialPorts = { NULL, 0, 0 }; JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts(JNIEnv *env, jclass serialComm) { @@ -115,6 +116,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP (*env)->SetObjectField(env, serialCommObject, portDescriptionField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portDescription)); (*env)->SetObjectField(env, serialCommObject, friendlyNameField, (*env)->NewStringUTF(env, serialPorts.ports[i]->friendlyName)); (*env)->SetObjectField(env, serialCommObject, comPortField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portPath)); + (*env)->SetObjectField(env, serialCommObject, portLocationField, (*env)->NewStringUTF(env, serialPorts.ports[i]->portLocation)); // Add new SerialComm object to array (*env)->SetObjectArrayElement(env, arrayObject, i, serialCommObject); @@ -133,6 +135,7 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_initializeLibrar comPortField = (*env)->GetFieldID(env, serialCommClass, "comPort", "Ljava/lang/String;"); friendlyNameField = (*env)->GetFieldID(env, serialCommClass, "friendlyName", "Ljava/lang/String;"); portDescriptionField = (*env)->GetFieldID(env, serialCommClass, "portDescription", "Ljava/lang/String;"); + portLocationField = (*env)->GetFieldID(env, serialCommClass, "portLocation", "Ljava/lang/String;"); eventListenerRunningField = (*env)->GetFieldID(env, serialCommClass, "eventListenerRunning", "Z"); disableConfigField = (*env)->GetFieldID(env, serialCommClass, "disableConfig", "Z"); isDtrEnabledField = (*env)->GetFieldID(env, serialCommClass, "isDtrEnabled", "Z"); @@ -195,7 +198,7 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative( if (!port) { // Create port representation and add to serial port listing - port = pushBack(&serialPorts, portName, "User-Specified Port", "User-Specified Port"); + port = pushBack(&serialPorts, portName, "User-Specified Port", "User-Specified Port", "0-0"); } if (!port || (port->handle > 0)) { diff --git a/src/main/java/com/fazecast/jSerialComm/SerialPort.java b/src/main/java/com/fazecast/jSerialComm/SerialPort.java index c1781fc..458acf0 100644 --- a/src/main/java/com/fazecast/jSerialComm/SerialPort.java +++ b/src/main/java/com/fazecast/jSerialComm/SerialPort.java @@ -2,7 +2,7 @@ * SerialPort.java * * Created on: Feb 25, 2012 - * Last Updated on: Dec 09, 2021 + * Last Updated on: Dec 15, 2021 * Author: Will Hedgecock * * Copyright (C) 2012-2021 Fazecast, Inc. @@ -492,7 +492,7 @@ public final class SerialPort private volatile byte xonStartChar = 17, xoffStopChar = 19; private volatile SerialPortDataListener userDataListener = null; private volatile SerialPortEventListener serialEventListener = null; - private volatile String comPort, friendlyName, portDescription; + private volatile String comPort, friendlyName, portDescription, portLocation; private volatile boolean eventListenerRunning = false, disableConfig = false, disableExclusiveLock = false; private volatile boolean rs485Mode = false, rs485ActiveHigh = true, rs485RxDuringTx = false, rs485EnableTermination = false; private volatile boolean isRtsEnabled = true, isDtrEnabled = true, autoFlushIOBuffers = false; @@ -1473,10 +1473,32 @@ public final class SerialPort *

* This will only be available for USB-connected devices that report a product description. * Otherwise, it will return the same value as {@link #getDescriptivePortName()}. - * + * * @return The port description as reported by the device itself. */ public final String getPortDescription() { return portDescription.trim(); } + + /** + * Gets the physical location of the port as a String in the form "BUS-[HUB1.HUB2.etc]PORT_NUMBER". + *

+ * "[HUB1.HUB2...]" is an optional field that refers to the hierarchy of USB hub numbers that a device + * might be plugged into. For example, a USB-to-Serial Converter plugged into the third port of a USB hub + * which is plugged into another USB hub which is plugged into a USB bus on a PC might have the port + * location "1-1.1.3". A device plugged directly into a PC-based serial or USB port might have a port + * location of "1-2". A virtual (non-physical) serial port might return a value of "0-0" since this + * port has no physical location. + *

+ * This method may be used to uniquely identify a device in the case where multiples of the same type + * of device are present on the same system. In this case, the operating system might assign each device + * to a different port number upon reboot; however, the port locations will remain the same as long + * as each device remains physically plugged into the same port. + *

+ * Note, if you manually specified the port location using {@link #getCommPort}, this method will + * always return "0-0". + * + * @return The physical port location in the form "BUS-[HUB1.HUB2.etc]PORT_NUMBER". + */ + public final String getPortLocation() { return portLocation; } /** * Gets the current baud rate of the serial port. diff --git a/src/main/resources/Android/arm64-v8a/libjSerialComm.so b/src/main/resources/Android/arm64-v8a/libjSerialComm.so index 63f155f..23ef4d1 100644 Binary files a/src/main/resources/Android/arm64-v8a/libjSerialComm.so and b/src/main/resources/Android/arm64-v8a/libjSerialComm.so differ diff --git a/src/main/resources/Android/armeabi-v7a/libjSerialComm.so b/src/main/resources/Android/armeabi-v7a/libjSerialComm.so index dce9acf..af0d368 100644 Binary files a/src/main/resources/Android/armeabi-v7a/libjSerialComm.so and b/src/main/resources/Android/armeabi-v7a/libjSerialComm.so differ diff --git a/src/main/resources/Android/x86/libjSerialComm.so b/src/main/resources/Android/x86/libjSerialComm.so index 5d140f0..f0a9bc7 100644 Binary files a/src/main/resources/Android/x86/libjSerialComm.so and b/src/main/resources/Android/x86/libjSerialComm.so differ diff --git a/src/main/resources/Android/x86_64/libjSerialComm.so b/src/main/resources/Android/x86_64/libjSerialComm.so index 3961ef4..db5e9ca 100644 Binary files a/src/main/resources/Android/x86_64/libjSerialComm.so and b/src/main/resources/Android/x86_64/libjSerialComm.so differ diff --git a/src/main/resources/FreeBSD/arm64/libjSerialComm.so b/src/main/resources/FreeBSD/arm64/libjSerialComm.so index 5f0dac1..373f4e0 100644 Binary files a/src/main/resources/FreeBSD/arm64/libjSerialComm.so and b/src/main/resources/FreeBSD/arm64/libjSerialComm.so differ diff --git a/src/main/resources/FreeBSD/x86/libjSerialComm.so b/src/main/resources/FreeBSD/x86/libjSerialComm.so index ea0622f..e64a7ee 100644 Binary files a/src/main/resources/FreeBSD/x86/libjSerialComm.so and b/src/main/resources/FreeBSD/x86/libjSerialComm.so differ diff --git a/src/main/resources/FreeBSD/x86_64/libjSerialComm.so b/src/main/resources/FreeBSD/x86_64/libjSerialComm.so index c910654..ff80670 100644 Binary files a/src/main/resources/FreeBSD/x86_64/libjSerialComm.so and b/src/main/resources/FreeBSD/x86_64/libjSerialComm.so differ diff --git a/src/main/resources/Linux/armv5/libjSerialComm.so b/src/main/resources/Linux/armv5/libjSerialComm.so index a3c1216..6270ba7 100644 Binary files a/src/main/resources/Linux/armv5/libjSerialComm.so and b/src/main/resources/Linux/armv5/libjSerialComm.so differ diff --git a/src/main/resources/Linux/armv6/libjSerialComm.so b/src/main/resources/Linux/armv6/libjSerialComm.so index 9ce4060..3ad663a 100644 Binary files a/src/main/resources/Linux/armv6/libjSerialComm.so and b/src/main/resources/Linux/armv6/libjSerialComm.so differ diff --git a/src/main/resources/Linux/armv6hf/libjSerialComm.so b/src/main/resources/Linux/armv6hf/libjSerialComm.so index d19c295..13ee4b9 100644 Binary files a/src/main/resources/Linux/armv6hf/libjSerialComm.so and b/src/main/resources/Linux/armv6hf/libjSerialComm.so differ diff --git a/src/main/resources/Linux/armv7/libjSerialComm.so b/src/main/resources/Linux/armv7/libjSerialComm.so index 369e11d..b1141da 100644 Binary files a/src/main/resources/Linux/armv7/libjSerialComm.so and b/src/main/resources/Linux/armv7/libjSerialComm.so differ diff --git a/src/main/resources/Linux/armv7hf/libjSerialComm.so b/src/main/resources/Linux/armv7hf/libjSerialComm.so index 9e131ce..95b9ce9 100644 Binary files a/src/main/resources/Linux/armv7hf/libjSerialComm.so and b/src/main/resources/Linux/armv7hf/libjSerialComm.so differ diff --git a/src/main/resources/Linux/armv8_32/libjSerialComm.so b/src/main/resources/Linux/armv8_32/libjSerialComm.so index 40937d6..7c5c3d0 100644 Binary files a/src/main/resources/Linux/armv8_32/libjSerialComm.so and b/src/main/resources/Linux/armv8_32/libjSerialComm.so differ diff --git a/src/main/resources/Linux/armv8_64/libjSerialComm.so b/src/main/resources/Linux/armv8_64/libjSerialComm.so index 32faea1..e148598 100644 Binary files a/src/main/resources/Linux/armv8_64/libjSerialComm.so and b/src/main/resources/Linux/armv8_64/libjSerialComm.so differ diff --git a/src/main/resources/Linux/ppc64le/libjSerialComm.so b/src/main/resources/Linux/ppc64le/libjSerialComm.so index d4b8dac..36a2cbc 100644 Binary files a/src/main/resources/Linux/ppc64le/libjSerialComm.so and b/src/main/resources/Linux/ppc64le/libjSerialComm.so differ diff --git a/src/main/resources/Linux/x86/libjSerialComm.so b/src/main/resources/Linux/x86/libjSerialComm.so index 6c17659..bd4a61d 100644 Binary files a/src/main/resources/Linux/x86/libjSerialComm.so and b/src/main/resources/Linux/x86/libjSerialComm.so differ diff --git a/src/main/resources/Linux/x86_64/libjSerialComm.so b/src/main/resources/Linux/x86_64/libjSerialComm.so index 31a9c41..37dd68a 100644 Binary files a/src/main/resources/Linux/x86_64/libjSerialComm.so and b/src/main/resources/Linux/x86_64/libjSerialComm.so differ diff --git a/src/main/resources/OSX/aarch64/libjSerialComm.jnilib b/src/main/resources/OSX/aarch64/libjSerialComm.jnilib index 2656413..195fe72 100755 Binary files a/src/main/resources/OSX/aarch64/libjSerialComm.jnilib and b/src/main/resources/OSX/aarch64/libjSerialComm.jnilib differ diff --git a/src/main/resources/OSX/x86/libjSerialComm.jnilib b/src/main/resources/OSX/x86/libjSerialComm.jnilib index c990187..ccb2f08 100644 Binary files a/src/main/resources/OSX/x86/libjSerialComm.jnilib and b/src/main/resources/OSX/x86/libjSerialComm.jnilib differ diff --git a/src/main/resources/OSX/x86_64/libjSerialComm.jnilib b/src/main/resources/OSX/x86_64/libjSerialComm.jnilib index 4643fb4..8138892 100755 Binary files a/src/main/resources/OSX/x86_64/libjSerialComm.jnilib and b/src/main/resources/OSX/x86_64/libjSerialComm.jnilib differ diff --git a/src/main/resources/Solaris/sparcv8plus_32/libjSerialComm.so b/src/main/resources/Solaris/sparcv8plus_32/libjSerialComm.so index b7985ef..d17fa04 100644 Binary files a/src/main/resources/Solaris/sparcv8plus_32/libjSerialComm.so and b/src/main/resources/Solaris/sparcv8plus_32/libjSerialComm.so differ diff --git a/src/main/resources/Solaris/sparcv9_64/libjSerialComm.so b/src/main/resources/Solaris/sparcv9_64/libjSerialComm.so index d8dc9a7..79c98e9 100644 Binary files a/src/main/resources/Solaris/sparcv9_64/libjSerialComm.so and b/src/main/resources/Solaris/sparcv9_64/libjSerialComm.so differ diff --git a/src/main/resources/Solaris/x86/libjSerialComm.so b/src/main/resources/Solaris/x86/libjSerialComm.so index 905cb50..0b55b86 100644 Binary files a/src/main/resources/Solaris/x86/libjSerialComm.so and b/src/main/resources/Solaris/x86/libjSerialComm.so differ diff --git a/src/main/resources/Solaris/x86_64/libjSerialComm.so b/src/main/resources/Solaris/x86_64/libjSerialComm.so index d2fc3d2..e1e5a91 100644 Binary files a/src/main/resources/Solaris/x86_64/libjSerialComm.so and b/src/main/resources/Solaris/x86_64/libjSerialComm.so differ diff --git a/src/test/c/Makefile b/src/test/c/Makefile index af2dbdf..44c4878 100644 --- a/src/test/c/Makefile +++ b/src/test/c/Makefile @@ -1,11 +1,15 @@ # Compiler tools, commands, and flags COMPILE := gcc +COMPILE_WIN := x86_64-w64-mingw32-gcc BUILD_DIR := build JDK_HOME := $(shell if [ "`uname`" = "Darwin" ]; then echo "`/usr/libexec/java_home`"; else echo "$$JDK_HOME"; fi) -INCLUDES := -I"../../main/c/Posix" -I"$(JDK_HOME)/include" -I"$(JDK_HOME)/include/linux" -I"$(JDK_HOME)/include/darwin" -I"$(JDK_HOME)/include/solaris" +INCLUDES := -I"../../main/c/Posix" -I"../../main/c/Windows" -I"$(JDK_HOME)/include" -I"$(JDK_HOME)/include/linux" -I"$(JDK_HOME)/include/darwin" -I"$(JDK_HOME)/include/solaris" CFLAGS := -fPIC -Os -flto -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 $(shell if [ "`uname`" != "Darwin" ]; then echo "-static-libgcc -fuse-linker-plugin"; fi) +CFLAGS_WIN := -Os -flto -static-libgcc -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 LDFLAGS := -Os -flto -fuse-linker-plugin $(shell if [ "`uname`" != "Darwin" ]; then echo "-static-libgcc -s"; fi) +LDFLAGS_WIN := -Os -flto -static-libgcc -fuse-linker-plugin -s LIBRARIES := $(shell if [ "`uname`" = "Darwin" ]; then echo "-framework Cocoa -framework IOKit"; else echo "-pthread"; fi) +LIBRARIES_WIN := -ladvapi32 -lsetupapi DELETE := @rm MKDIR := @mkdir -p COPY := @cp @@ -16,7 +20,7 @@ PRINT := @echo .PHONY: all clean .SUFFIXES: .SUFFIXES: .cpp .c .o .h -vpath %.c ../../main/c/Posix +vpath %.c ../../main/c/Posix ../../main/c/Windows # Default build target does nothing all : @@ -31,8 +35,18 @@ $(BUILD_DIR) : # Build rules for all tests testOpenClose : $(BUILD_DIR)/testOpenClose.o $(BUILD_DIR)/PosixHelperFunctions.o $(COMPILE) $(LDFLAGS) $(LIBRARIES) -o $@ $^ +testEnumeratePosix : $(BUILD_DIR)/testEnumeratePosix.o $(BUILD_DIR)/PosixHelperFunctions.o + $(COMPILE) $(LDFLAGS) $(LIBRARIES) -o $@ $^ +testEnumerateWindows : $(BUILD_DIR)/testEnumerateWindows.o $(BUILD_DIR)/WindowsHelperFunctions.o + $(COMPILE_WIN) $(LDFLAGS_WIN) $(LIBRARIES_WIN) -o $@ $^ # Suffix rules to get from *.c -> *.o +$(BUILD_DIR)/testEnumerateWindows.o : testEnumerateWindows.c + $(MKDIR) $(BUILD_DIR) + $(COMPILE_WIN) $(INCLUDES) $(CFLAGS_WIN) -c $< -o $@ +$(BUILD_DIR)/WindowsHelperFunctions.o : WindowsHelperFunctions.c + $(MKDIR) $(BUILD_DIR) + $(COMPILE_WIN) $(INCLUDES) $(CFLAGS_WIN) -c $< -o $@ $(BUILD_DIR)/%.o : %.c $(MKDIR) $(BUILD_DIR) $(COMPILE) $(INCLUDES) $(CFLAGS) -c $< -o $@ diff --git a/src/test/c/testEnumeratePosix.c b/src/test/c/testEnumeratePosix.c new file mode 100644 index 0000000..a59d103 --- /dev/null +++ b/src/test/c/testEnumeratePosix.c @@ -0,0 +1,784 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "PosixHelperFunctions.h" + +static serialPortVector comPorts = { NULL, 0, 0 }; + +#if defined(__linux__) + +#include +#include +#include + +void getDriverNameTest(const char* directoryToSearch, char* friendlyName) +{ + friendlyName[0] = '\0'; + + // Open the directory + DIR *directoryIterator = opendir(directoryToSearch); + if (!directoryIterator) + return; + + // Read all sub-directories in the current directory + struct dirent *directoryEntry = readdir(directoryIterator); + while (directoryEntry) + { + // Check if entry is a valid sub-directory + if (directoryEntry->d_name[0] != '.') + { + // Get the readable part of the driver name + strcpy(friendlyName, "USB-to-Serial Port ("); + char *startingPoint = strchr(directoryEntry->d_name, ':'); + if (startingPoint != NULL) + strcat(friendlyName, startingPoint+1); + else + strcat(friendlyName, directoryEntry->d_name); + strcat(friendlyName, ")"); + break; + } + directoryEntry = readdir(directoryIterator); + } + + // Close the directory + closedir(directoryIterator); +} + +void getFriendlyNameTest(const char* productFile, char* friendlyName) +{ + int friendlyNameLength = 0; + friendlyName[0] = '\0'; + + FILE *input = fopen(productFile, "rb"); + if (input) + { + char ch = getc(input); + while ((ch != '\n') && (ch != EOF)) + { + friendlyName[friendlyNameLength++] = ch; + ch = getc(input); + } + friendlyName[friendlyNameLength] = '\0'; + fclose(input); + } +} + +void getInterfaceDescriptionTest(const char* interfaceFile, char* interfaceDescription) +{ + int interfaceDescriptionLength = 0; + interfaceDescription[0] = '\0'; + + FILE *input = fopen(interfaceFile, "rb"); + if (input) + { + char ch = getc(input); + while ((ch != '\n') && (ch != EOF)) + { + interfaceDescription[interfaceDescriptionLength++] = ch; + ch = getc(input); + } + interfaceDescription[interfaceDescriptionLength] = '\0'; + fclose(input); + } +} + +char getPortLocationTest(const char* portDirectory, char* portLocation) +{ + // Set location of busnum and devpath files + char isUSB = 1; + char* busnumFile = (char*)malloc(strlen(portDirectory) + 16); + strcpy(busnumFile, portDirectory); + strcat(busnumFile, "/busnum"); + char* devpathFile = (char*)malloc(strlen(portDirectory) + 16); + strcpy(devpathFile, portDirectory); + strcat(devpathFile, "/devpath"); + int portLocationLength = 0; + portLocation[0] = '\0'; + + // Read the bus number + FILE *input = fopen(busnumFile, "rb"); + if (input) + { + char ch = getc(input); + while ((ch != '\n') && (ch != EOF)) + { + portLocation[portLocationLength++] = ch; + ch = getc(input); + } + portLocation[portLocationLength++] = '-'; + fclose(input); + } + else + { + isUSB = 0; + portLocation[portLocationLength++] = '0'; + portLocation[portLocationLength++] = '-'; + } + + // Read the device path + input = fopen(devpathFile, "rb"); + if (input) + { + char ch = getc(input); + while ((ch != '\n') && (ch != EOF)) + { + portLocation[portLocationLength++] = ch; + ch = getc(input); + } + portLocation[portLocationLength] = '\0'; + fclose(input); + } + else + { + isUSB = 0; + portLocation[portLocationLength++] = '0'; + } + + // Clean up the dynamic memory + free(devpathFile); + free(busnumFile); + return isUSB; +} + +char driverGetPortLocationTest(char topLevel, const char *fullPathToSearch, const char *deviceName, char* portLocation, char searchBackwardIteration) +{ + // Open the linux USB device directory + char isUSB = 0; + DIR *directoryIterator = opendir(fullPathToSearch); + if (!directoryIterator) + return isUSB; + + if (!searchBackwardIteration) + { + // Read all sub-directories in the current directory + struct dirent *directoryEntry = readdir(directoryIterator); + while (directoryEntry && !isUSB) + { + // Check if entry is a sub-directory + if ((topLevel || (directoryEntry->d_type == DT_DIR)) && (directoryEntry->d_name[0] != '.')) + { + // Set up the next directory to search + char* nextDirectory = (char*)malloc(strlen(fullPathToSearch) + strlen(directoryEntry->d_name) + 5); + strcpy(nextDirectory, fullPathToSearch); + strcat(nextDirectory, directoryEntry->d_name); + + // Only process directories that match the device name + if (strcmp(directoryEntry->d_name, deviceName) == 0) + { + strcat(nextDirectory, "/.."); + isUSB = driverGetPortLocationTest(0, nextDirectory, deviceName, portLocation, 1); + } + else + { + // Search for more serial ports within the directory + strcat(nextDirectory, "/"); + isUSB = driverGetPortLocationTest(0, nextDirectory, deviceName, portLocation, 0); + } + free(nextDirectory); + } + directoryEntry = readdir(directoryIterator); + } + } + else + { + // Read all files in the current directory + char hasBusnum = 0, hasDevpath = 0; + struct dirent *directoryEntry = readdir(directoryIterator); + while (directoryEntry) + { + // Check if entry is a regular file with the expected name + if (directoryEntry->d_type == DT_REG) + { + if (strcmp(directoryEntry->d_name, "busnum") == 0) + hasBusnum = 1; + else if (strcmp(directoryEntry->d_name, "devpath") == 0) + hasDevpath = 1; + } + directoryEntry = readdir(directoryIterator); + } + + // Check if the current directory has the required information files + if ((!hasBusnum || !hasDevpath || !(isUSB = getPortLocationTest(fullPathToSearch, portLocation))) && (searchBackwardIteration < 10)) + { + char* nextDirectory = (char*)malloc(strlen(fullPathToSearch) + 5); + strcpy(nextDirectory, fullPathToSearch); + strcat(nextDirectory, "/.."); + isUSB = driverGetPortLocationTest(0, nextDirectory, deviceName, portLocation, searchBackwardIteration + 1); + free(nextDirectory); + } + } + + // Close the directory + closedir(directoryIterator); + return isUSB; +} + +void recursiveSearchForComPortsTest(serialPortVector* comPorts, const char* fullPathToSearch) +{ + // Open the directory + DIR *directoryIterator = opendir(fullPathToSearch); + if (!directoryIterator) + return; + + // Read all sub-directories in the current directory + struct dirent *directoryEntry = readdir(directoryIterator); + while (directoryEntry) + { + // Check if entry is a sub-directory + if (directoryEntry->d_type == DT_DIR) + { + // Only process non-dot, non-virtual directories + if ((directoryEntry->d_name[0] != '.') && (strcmp(directoryEntry->d_name, "virtual") != 0)) + { + // See if the directory names a potential serial port + if ((strlen(directoryEntry->d_name) > 3) && + (((directoryEntry->d_name[0] == 't') && (directoryEntry->d_name[1] == 't') && (directoryEntry->d_name[2] == 'y')) || + ((directoryEntry->d_name[0] == 'r') && (directoryEntry->d_name[1] == 'f') && (directoryEntry->d_name[2] == 'c')))) + { + // Determine system name of port + char* systemName = (char*)malloc(256); + strcpy(systemName, "/dev/"); + strcat(systemName, directoryEntry->d_name); + + // Check if port is already enumerated + serialPort *port = fetchPort(comPorts, systemName); + if (port) + port->enumerated = 1; + else + { + // See if device has a registered friendly name + char* portLocation = (char*)malloc(128); + char* friendlyName = (char*)malloc(256); + char* productFile = (char*)malloc(strlen(fullPathToSearch) + strlen(directoryEntry->d_name) + 30); + strcpy(productFile, fullPathToSearch); + strcat(productFile, directoryEntry->d_name); + strcat(productFile, "/device/.."); + char isUSB = getPortLocationTest(productFile, portLocation); + if (!isUSB) + isUSB = driverGetPortLocationTest(1, "/sys/bus/usb/devices/", directoryEntry->d_name, portLocation, 0); + strcat(productFile, "/product"); + getFriendlyNameTest(productFile, friendlyName); + if (friendlyName[0] == '\0') // Must be a physical (or emulated) port + { + // See if this is a USB-to-Serial converter based on the driver loaded + strcpy(productFile, fullPathToSearch); + strcat(productFile, directoryEntry->d_name); + strcat(productFile, "/driver/module/drivers"); + getDriverNameTest(productFile, friendlyName); + if (friendlyName[0] == '\0') // Must be a physical port + { + // Ensure that the platform port is actually open + struct serial_struct serialInfo = { 0 }; + int fd = open(systemName, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (fd >= 0) + { + if ((strlen(directoryEntry->d_name) >= 6) && (directoryEntry->d_name[0] == 'r') && (directoryEntry->d_name[1] == 'f') && (directoryEntry->d_name[2] == 'c') && + (directoryEntry->d_name[3] == 'o') && (directoryEntry->d_name[4] == 'm') && (directoryEntry->d_name[5] == 'm')) + { + strcpy(friendlyName, "Bluetooth Port "); + strcat(friendlyName, directoryEntry->d_name); + pushBack(comPorts, systemName, friendlyName, friendlyName, portLocation); + } + else if (((strlen(directoryEntry->d_name) >= 6) && (directoryEntry->d_name[3] == 'A') && (directoryEntry->d_name[4] == 'M') && (directoryEntry->d_name[5] == 'A')) || + ((ioctl(fd, TIOCGSERIAL, &serialInfo) == 0) && (serialInfo.type != PORT_UNKNOWN))) + { + strcpy(friendlyName, "Physical Port "); + strcat(friendlyName, directoryEntry->d_name+3); + pushBack(comPorts, systemName, friendlyName, friendlyName, portLocation); + } + close(fd); + } + } + else + { + // Attempt to read from the device interface file + char* interfaceDescription = (char*)malloc(256); + char* interfaceFile = (char*)malloc(strlen(fullPathToSearch) + strlen(directoryEntry->d_name) + 30); + strcpy(interfaceFile, fullPathToSearch); + strcat(interfaceFile, directoryEntry->d_name); + strcat(interfaceFile, "/../interface"); + getInterfaceDescriptionTest(interfaceFile, interfaceDescription); + if (interfaceDescription[0] == '\0') + { + strcpy(interfaceFile, fullPathToSearch); + strcat(interfaceFile, directoryEntry->d_name); + strcat(interfaceFile, "/device/../interface"); + getInterfaceDescriptionTest(interfaceFile, interfaceDescription); + } + if (interfaceDescription[0] == '\0') + strcpy(interfaceDescription, friendlyName); + pushBack(comPorts, systemName, friendlyName, interfaceDescription, portLocation); + + // Clean up memory + free(interfaceFile); + free(interfaceDescription); + } + } + else + { + // Attempt to read from the device interface file + char* interfaceDescription = (char*)malloc(256); + char* interfaceFile = (char*)malloc(strlen(fullPathToSearch) + strlen(directoryEntry->d_name) + 30); + strcpy(interfaceFile, fullPathToSearch); + strcat(interfaceFile, directoryEntry->d_name); + strcat(interfaceFile, "/../interface"); + getInterfaceDescriptionTest(interfaceFile, interfaceDescription); + if (interfaceDescription[0] == '\0') + { + strcpy(interfaceFile, fullPathToSearch); + strcat(interfaceFile, directoryEntry->d_name); + strcat(interfaceFile, "/device/../interface"); + getInterfaceDescriptionTest(interfaceFile, interfaceDescription); + } + if (interfaceDescription[0] == '\0') + strcpy(interfaceDescription, friendlyName); + pushBack(comPorts, systemName, friendlyName, interfaceDescription, portLocation); + + // Clean up memory + free(interfaceFile); + free(interfaceDescription); + } + + // Clean up memory + free(productFile); + free(friendlyName); + free(portLocation); + } + + // Clean up memory + free(systemName); + } + else + { + // Search for more serial ports within the directory + char* nextDirectory = (char*)malloc(strlen(fullPathToSearch) + strlen(directoryEntry->d_name) + 5); + strcpy(nextDirectory, fullPathToSearch); + strcat(nextDirectory, directoryEntry->d_name); + strcat(nextDirectory, "/"); + recursiveSearchForComPortsTest(comPorts, nextDirectory); + free(nextDirectory); + } + } + } + directoryEntry = readdir(directoryIterator); + } + + // Close the directory + closedir(directoryIterator); +} + +void driverBasedSearchForComPortsTest(serialPortVector* comPorts, const char* fullPathToDriver, const char* fullBasePathToPort) +{ + // Search for unidentified physical serial ports + FILE *serialDriverFile = fopen(fullPathToDriver, "rb"); + if (serialDriverFile) + { + char* serialLine = (char*)malloc(128); + while (fgets(serialLine, 128, serialDriverFile)) + if (strstr(serialLine, "uart:") && (strstr(serialLine, "uart:unknown") == NULL)) + { + // Determine system name of port + *strchr(serialLine, ':') = '\0'; + char* systemName = (char*)malloc(256); + strcpy(systemName, fullBasePathToPort); + strcat(systemName, serialLine); + + // Check if port is already enumerated + serialPort *port = fetchPort(comPorts, systemName); + if (port) + port->enumerated = 1; + else + { + // Ensure that the port is valid and not a symlink + struct stat fileStats; + if ((access(systemName, F_OK) == 0) && (lstat(systemName, &fileStats) == 0) && !S_ISLNK(fileStats.st_mode)) + { + // Set static friendly name + char* friendlyName = (char*)malloc(256); + strcpy(friendlyName, "Physical Port "); + strcat(friendlyName, serialLine); + + // Add the port to the list + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0", 0); + + // Clean up memory + free(friendlyName); + } + } + + // Clean up memory + free(systemName); + } + free(serialLine); + fclose(serialDriverFile); + } +} + +void lastDitchSearchForComPortsTest(serialPortVector* comPorts) +{ + // Open the linux dev directory + DIR *directoryIterator = opendir("/dev/"); + if (!directoryIterator) + return; + + // Read all files in the current directory + struct dirent *directoryEntry = readdir(directoryIterator); + while (directoryEntry) + { + // See if the file names a potential serial port + if ((strlen(directoryEntry->d_name) >= 6) && (directoryEntry->d_name[0] == 't') && (directoryEntry->d_name[1] == 't') && (directoryEntry->d_name[2] == 'y') && + (((directoryEntry->d_name[3] == 'A') && (directoryEntry->d_name[4] == 'M') && (directoryEntry->d_name[5] == 'A')) || + ((directoryEntry->d_name[3] == 'A') && (directoryEntry->d_name[4] == 'C') && (directoryEntry->d_name[5] == 'M')) || + ((directoryEntry->d_name[3] == 'U') && (directoryEntry->d_name[4] == 'S') && (directoryEntry->d_name[5] == 'B')))) + { + // Determine system name of port + char* systemName = (char*)malloc(256); + strcpy(systemName, "/dev/"); + strcat(systemName, directoryEntry->d_name); + + // Check if port is already enumerated + serialPort *port = fetchPort(comPorts, systemName); + if (port) + port->enumerated = 1; + else + { + // Set static friendly name + char* friendlyName = (char*)malloc(256); + strcpy(friendlyName, "USB-Based Serial Port"); + + // Add the port to the list + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0", 1); + + // Clean up memory + free(friendlyName); + } + + // Clean up memory + free(systemName); + } + else if ((strlen(directoryEntry->d_name) >= 6) && (directoryEntry->d_name[0] == 't') && (directoryEntry->d_name[1] == 't') && (directoryEntry->d_name[2] == 'y') && + (directoryEntry->d_name[3] == 'A') && (directoryEntry->d_name[4] == 'P')) + { + // Determine system name of port + char* systemName = (char*)malloc(256); + strcpy(systemName, "/dev/"); + strcat(systemName, directoryEntry->d_name); + + // Check if port is already enumerated + serialPort *port = fetchPort(comPorts, systemName); + if (port) + port->enumerated = 1; + else + { + // Set static friendly name + char* friendlyName = (char*)malloc(256); + strcpy(friendlyName, "Advantech Extended Serial Port"); + + // Add the port to the list + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0", 0); + + // Clean up memory + free(friendlyName); + } + + // Clean up memory + free(systemName); + } + else if ((strlen(directoryEntry->d_name) >= 6) && (directoryEntry->d_name[0] == 'r') && (directoryEntry->d_name[1] == 'f') && (directoryEntry->d_name[2] == 'c') && + (directoryEntry->d_name[3] == 'o') && (directoryEntry->d_name[4] == 'm') && (directoryEntry->d_name[5] == 'm')) + { + // Determine system name of port + char* systemName = (char*)malloc(256); + strcpy(systemName, "/dev/"); + strcat(systemName, directoryEntry->d_name); + + // Check if port is already enumerated + serialPort *port = fetchPort(comPorts, systemName); + if (port) + port->enumerated = 1; + else + { + // Set static friendly name + char* friendlyName = (char*)malloc(256); + strcpy(friendlyName, "Bluetooth-Based Serial Port"); + + // Add the port to the list + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0", 0); + + // Clean up memory + free(friendlyName); + } + + // Clean up memory + free(systemName); + } + directoryEntry = readdir(directoryIterator); + } + + // Close the directory + closedir(directoryIterator); +} + +#elif defined(__FreeBSD__) + +void searchForComPorts(serialPortVector* comPorts) +{ + // Open the FreeBSD dev directory + DIR *directoryIterator = opendir("/dev/"); + if (directoryIterator) + { + // Read all files in the current directory + struct dirent *directoryEntry = readdir(directoryIterator); + while (directoryEntry) + { + // See if the file names a potential serial port + if ((strlen(directoryEntry->d_name) >= 4) && (directoryEntry->d_name[0] != '.') && + (((directoryEntry->d_name[0] == 't') && (directoryEntry->d_name[1] == 't') && (directoryEntry->d_name[2] == 'y') && (directoryEntry->d_name[3] != 'v')) || + ((directoryEntry->d_name[0] == 'c') && (directoryEntry->d_name[1] == 'u') && (directoryEntry->d_name[2] == 'a')))) + { + // Ensure that the file is not an init or a lock file + if ((strlen(directoryEntry->d_name) < 5) || + (memcmp(".init", directoryEntry->d_name + strlen(directoryEntry->d_name) - 5, 5) && + memcmp(".lock", directoryEntry->d_name + strlen(directoryEntry->d_name) - 5, 5))) + { + // Determine system name of port + char* systemName = (char*)malloc(256); + strcpy(systemName, "/dev/"); + strcat(systemName, directoryEntry->d_name); + + // Check if port is already enumerated + serialPort *port = fetchPort(comPorts, systemName); + if (port) + port->enumerated = 1; + else + { + // Set static friendly name + char *location = (char*)malloc(256); + char* friendlyName = (char*)malloc(256); + if (directoryEntry->d_name[0] == 'c') + strcpy(friendlyName, "Serial Port"); + else + strcpy(friendlyName, "Serial Port (Dial-In)"); + + // Add the port to the list if it is not a directory + struct stat fileStats; + stat(systemName, &fileStats); + if (!S_ISDIR(fileStats.st_mode)) + { + size_t bufferSize = 1024; + char *stdOutResult = (char*)malloc(bufferSize), *device = NULL; + snprintf(stdOutResult, bufferSize, "sysctl -a | grep \"ttyname: %s\"", directoryEntry->d_name + 3); + FILE *pipe = popen(stdOutResult, "r"); + if (pipe) + { + while (!device && fgets(stdOutResult, bufferSize, pipe)) + { + device = stdOutResult; + *(strstr(device, "ttyname:") - 1) = '\0'; + strcat(device, ".%location"); + } + pclose(pipe); + } + + // Add port to the list and clean up memory + if (device) + { + char *location = (char*)malloc(256), *temp = (char*)malloc(64); + snprintf(location, bufferSize, "sysctl -a | grep \"%s\"", device); + pipe = popen(location, "r"); + strcpy(location, "0-0"); + if (pipe) + { + while (fgets(stdOutResult, bufferSize, pipe)) + if (strstr(stdOutResult, "bus") && strstr(stdOutResult, "hubaddr") && strstr(stdOutResult, "port")) + { + char *cursor = strstr(stdOutResult, "bus=") + 4; + size_t length = (size_t)(strchr(cursor, ' ') - cursor); + memcpy(location, cursor, length); + location[length] = '\0'; + strcat(location, "-"); + cursor = strstr(stdOutResult, "hubaddr=") + 8; + length = (size_t)(strchr(cursor, ' ') - cursor); + memcpy(temp, cursor, length); + temp[length] = '\0'; + strcat(location, temp); + strcat(location, "."); + cursor = strstr(stdOutResult, "port=") + 5; + length = (size_t)(strchr(cursor, ' ') - cursor); + memcpy(temp, cursor, length); + temp[length] = '\0'; + strcat(location, temp); + break; + } + pclose(pipe); + } + pushBack(comPorts, systemName, friendlyName, friendlyName, location); + free(location); + free(temp); + } + else + pushBack(comPorts, systemName, friendlyName, friendlyName, "0-0"); + free(stdOutResult); + } + + // Clean up memory + free(friendlyName); + free(location); + } + + // Clean up memory + free(systemName); + } + } + directoryEntry = readdir(directoryIterator); + } + + // Close the directory + closedir(directoryIterator); + } +} + +#elif defined(__APPLE__) + +#include +#include +#include +#include +#include +#include +#include + +#if (MAC_OS_X_VERSION_MAX_ALLOWED < 120000) + #define kIOMainPortDefault kIOMasterPortDefault +#endif + +void enumeratePortsMac(serialPortVector *comPorts) +{ + serialPort *port; + io_object_t serialPort; + io_iterator_t serialPortIterator; + int vendor_id = 0, product_id = 0; + char friendlyName[1024], comPortCu[1024], comPortTty[1024]; + char portLocation[1024], portDescription[1024], serialNumber[1024] = "Unknown"; + + // Enumerate serial ports on machine + IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching(kIOSerialBSDServiceValue), &serialPortIterator); + while ((serialPort = IOIteratorNext(serialPortIterator))) + { + // Get serial port information + char isUSB = 0; + friendlyName[0] = '\0'; + io_registry_entry_t parent = 0, service = serialPort; + while (service) + { + if (IOObjectConformsTo(service, "IOUSBDevice")) + { + IORegistryEntryGetName(service, friendlyName); + isUSB = 1; + break; + } + if (IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent) != KERN_SUCCESS) + break; + if (service != serialPort) + IOObjectRelease(service); + service = parent; + } + if (service != serialPort) + IOObjectRelease(service); + + // Get serial port name and COM value + if (friendlyName[0] == '\0') + { + CFStringRef friendlyNameRef = (CFStringRef)IORegistryEntryCreateCFProperty(serialPort, CFSTR(kIOTTYDeviceKey), kCFAllocatorDefault, 0); + CFStringGetCString(friendlyNameRef, friendlyName, sizeof(friendlyName), kCFStringEncodingUTF8); + CFRelease(friendlyNameRef); + } + CFStringRef comPortRef = (CFStringRef)IORegistryEntryCreateCFProperty(serialPort, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + CFStringGetCString(comPortRef, comPortCu, sizeof(comPortCu), kCFStringEncodingUTF8); + CFRelease(comPortRef); + comPortRef = (CFStringRef)IORegistryEntryCreateCFProperty(serialPort, CFSTR(kIODialinDeviceKey), kCFAllocatorDefault, 0); + CFStringGetCString(comPortRef, comPortTty, sizeof(comPortTty), kCFStringEncodingUTF8); + CFRelease(comPortRef); + + // Get VID, PID, Serial Number, Bus Number, and Port Address + if (isUSB) + { + CFTypeRef propertyRef = IORegistryEntrySearchCFProperty(serialPort, kIOServicePlane, CFSTR("locationID"), kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); + if (propertyRef) + { + int locationID = 0; + char multiHub = 0, tempLocation[64]; + CFNumberGetValue(propertyRef, kCFNumberIntType, &locationID); + CFRelease(propertyRef); + snprintf(portLocation, sizeof(portLocation), "%d", (locationID >> 24) & 0x000000FF); + strcat(portLocation, "-"); + while (locationID & 0x00F00000) + { + if (multiHub) + strcat(portLocation, "."); + snprintf(tempLocation, sizeof(tempLocation), "%d", (locationID >> 20) & 0x0000000F); + strcat(portLocation, tempLocation); + locationID <<= 4; + multiHub = 1; + } + } + propertyRef = IORegistryEntrySearchCFProperty(serialPort, kIOServicePlane, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); + if (propertyRef) + { + CFStringGetCString(propertyRef, serialNumber, sizeof(serialNumber), kCFStringEncodingASCII); + CFRelease(propertyRef); + } + propertyRef = IORegistryEntrySearchCFProperty(serialPort, kIOServicePlane, CFSTR(kUSBVendorID), kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); + if (propertyRef) + { + CFNumberGetValue(propertyRef, kCFNumberIntType, &vendor_id); + CFRelease(propertyRef); + } + propertyRef = IORegistryEntrySearchCFProperty(serialPort, kIOServicePlane, CFSTR(kUSBProductID), kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); + if (propertyRef) + { + CFNumberGetValue(propertyRef, kCFNumberIntType, &product_id); + CFRelease(propertyRef); + } + } + else + strcpy(portLocation, "0-0"); + + // Add ports to enumerated list + pushBack(comPorts, comPortCu, friendlyName, friendlyName, portLocation); + strcat(friendlyName, " (Dial-In)"); + pushBack(comPorts, comPortTty, friendlyName, friendlyName, portLocation); + IOObjectRelease(serialPort); + } + IOObjectRelease(serialPortIterator); +} + +#endif + +int main(void) +{ + // Enumerate all serial ports +#if defined(__linux__) + recursiveSearchForComPortsTest(&comPorts, "/sys/devices/"); + driverBasedSearchForComPortsTest(&comPorts, "/proc/tty/driver/serial", "/dev/ttyS"); + driverBasedSearchForComPortsTest(&comPorts, "/proc/tty/driver/mvebu_serial", "/dev/ttyMV"); + lastDitchSearchForComPortsTest(&comPorts); +#elif defined(__APPLE__) + enumeratePortsMac(&comPorts); +#endif + + // Output all enumerated ports + for (int i = 0; i < comPorts.length; ++i) + { + serialPort *port = comPorts.ports[i]; + printf("%s: Description = %s, Location = %s\n", port->portPath, port->friendlyName, port->portLocation); + } + + return 0; +} diff --git a/src/test/c/testEnumerateWindows.c b/src/test/c/testEnumerateWindows.c new file mode 100644 index 0000000..8675e3c --- /dev/null +++ b/src/test/c/testEnumerateWindows.c @@ -0,0 +1,268 @@ +#define WINVER _WIN32_WINNT_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#define NTDDI_VERSION NTDDI_VISTA +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include "WindowsHelperFunctions.h" + +static serialPortVector serialPorts = { NULL, 0, 0 }; + +void getPortsWindows(void) +{ + HKEY keyHandle1, keyHandle2, keyHandle3, keyHandle4, keyHandle5; + DWORD numSubKeys1, numSubKeys2, numSubKeys3, numValues; + DWORD maxSubKeyLength1, maxSubKeyLength2, maxSubKeyLength3; + DWORD maxValueLength, maxComPortLength, valueLength, comPortLength, keyType; + DWORD subKeyLength1, subKeyLength2, subKeyLength3, friendlyNameLength; + + // Reset the enumerated flag on all non-open serial ports + for (int i = 0; i < serialPorts.length; ++i) + serialPorts.ports[i]->enumerated = (serialPorts.ports[i]->handle != INVALID_HANDLE_VALUE); + + // Enumerate serial ports on machine + if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &keyHandle1) == ERROR_SUCCESS) && + (RegQueryInfoKeyW(keyHandle1, NULL, NULL, NULL, NULL, NULL, NULL, &numValues, &maxValueLength, &maxComPortLength, NULL, NULL) == ERROR_SUCCESS)) + { + // Allocate memory + ++maxValueLength; + ++maxComPortLength; + WCHAR *valueName = (WCHAR*)malloc(maxValueLength*sizeof(WCHAR)); + WCHAR *comPort = (WCHAR*)malloc(maxComPortLength*sizeof(WCHAR)); + + // Iterate through all COM ports + for (DWORD i = 0; i < numValues; ++i) + { + // Get serial port name and COM value + valueLength = maxValueLength; + comPortLength = maxComPortLength; + memset(valueName, 0, valueLength*sizeof(WCHAR)); + memset(comPort, 0, comPortLength*sizeof(WCHAR)); + if ((RegEnumValueW(keyHandle1, i, valueName, &valueLength, NULL, &keyType, (BYTE*)comPort, &comPortLength) == ERROR_SUCCESS) && (keyType == REG_SZ)) + { + // Set port name and description + wchar_t* comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort; + wchar_t* descriptionString = wcsrchr(valueName, L'\\') ? (wcsrchr(valueName, L'\\') + 1) : valueName; + + // Check if port is already enumerated + serialPort *port = fetchPort(&serialPorts, comPortString); + if (port) + port->enumerated = 1; + else + pushBack(&serialPorts, comPortString, descriptionString, descriptionString, L"0-0"); + } + } + + // Clean up memory + free(valueName); + free(comPort); + } + RegCloseKey(keyHandle1); + + // Enumerate all devices on machine + if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Enum", 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &keyHandle1) == ERROR_SUCCESS) && + (RegQueryInfoKeyW(keyHandle1, NULL, NULL, NULL, &numSubKeys1, &maxSubKeyLength1, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)) + { + // Allocate memory + ++maxSubKeyLength1; + WCHAR *subKeyName1 = (WCHAR*)malloc(maxSubKeyLength1*sizeof(WCHAR)); + + // Enumerate sub-keys + for (DWORD i1 = 0; i1 < numSubKeys1; ++i1) + { + subKeyLength1 = maxSubKeyLength1; + if ((RegEnumKeyExW(keyHandle1, i1, subKeyName1, &subKeyLength1, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) && + (RegOpenKeyExW(keyHandle1, subKeyName1, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &keyHandle2) == ERROR_SUCCESS) && + (RegQueryInfoKeyW(keyHandle2, NULL, NULL, NULL, &numSubKeys2, &maxSubKeyLength2, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)) + { + // Allocate memory + ++maxSubKeyLength2; + WCHAR *subKeyName2 = (WCHAR*)malloc(maxSubKeyLength2*sizeof(WCHAR)); + + // Enumerate sub-keys + for (DWORD i2 = 0; i2 < numSubKeys2; ++i2) + { + subKeyLength2 = maxSubKeyLength2; + if ((RegEnumKeyExW(keyHandle2, i2, subKeyName2, &subKeyLength2, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) && + (RegOpenKeyExW(keyHandle2, subKeyName2, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &keyHandle3) == ERROR_SUCCESS) && + (RegQueryInfoKeyW(keyHandle3, NULL, NULL, NULL, &numSubKeys3, &maxSubKeyLength3, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)) + { + // Allocate memory + ++maxSubKeyLength3; + WCHAR *subKeyName3 = (WCHAR*)malloc(maxSubKeyLength3*sizeof(WCHAR)); + + // Enumerate sub-keys + for (DWORD i3 = 0; i3 < numSubKeys3; ++i3) + { + subKeyLength3 = maxSubKeyLength3; + if ((RegEnumKeyExW(keyHandle3, i3, subKeyName3, &subKeyLength3, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) && + (RegOpenKeyExW(keyHandle3, subKeyName3, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &keyHandle4) == ERROR_SUCCESS) && + (RegQueryInfoKeyW(keyHandle4, NULL, NULL, NULL, NULL, NULL, NULL, &numValues, NULL, &valueLength, NULL, NULL) == ERROR_SUCCESS)) + { + // Allocate memory + friendlyNameLength = valueLength + 1; + WCHAR *friendlyName = (WCHAR*)malloc(friendlyNameLength*sizeof(WCHAR)); + WCHAR *locationInfo = (WCHAR*)malloc(friendlyNameLength*sizeof(WCHAR)); + + if ((RegOpenKeyExW(keyHandle4, L"Device Parameters", 0, KEY_QUERY_VALUE, &keyHandle5) == ERROR_SUCCESS) && + (RegQueryInfoKeyW(keyHandle5, NULL, NULL, NULL, NULL, NULL, NULL, &numValues, NULL, &valueLength, NULL, NULL) == ERROR_SUCCESS)) + { + // Allocate memory + comPortLength = valueLength + 1; + WCHAR *comPort = (WCHAR*)malloc(comPortLength*sizeof(WCHAR)); + + // Attempt to get COM value and friendly port name + if ((RegQueryValueExW(keyHandle5, L"PortName", NULL, &keyType, (BYTE*)comPort, &comPortLength) == ERROR_SUCCESS) && (keyType == REG_SZ) && + (RegQueryValueExW(keyHandle4, L"FriendlyName", NULL, &keyType, (BYTE*)friendlyName, &friendlyNameLength) == ERROR_SUCCESS) && (keyType == REG_SZ) && + (RegQueryValueExW(keyHandle4, L"LocationInformation", NULL, &keyType, (BYTE*)locationInfo, &friendlyNameLength) == ERROR_SUCCESS) && (keyType == REG_SZ)) + { + // Set port name and description + wchar_t* comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort; + wchar_t* descriptionString = friendlyName; + + // Parse the port location + int hub = 0, port = 0, bufferLength = 128; + wchar_t *portLocation = (wchar_t*)malloc(bufferLength*sizeof(wchar_t)); + if (wcsstr(locationInfo, L"Port_#") && wcsstr(locationInfo, L"Hub_#")) + { + wchar_t *hubString = wcsrchr(locationInfo, L'#') + 1; + hub = _wtoi(hubString); + wchar_t *portString = wcschr(locationInfo, L'#') + 1; + if (portString) + { + hubString = wcschr(portString, L'.'); + if (hubString) + *hubString = L'\0'; + } + port = _wtoi(portString); + _snwprintf(portLocation, comPortLength, L"1-%d.%d", hub, port); + } + else + wcscpy(portLocation, L"0-0"); + + // Update friendly name if COM port is actually connected and present in the port list + for (int i = 0; i < serialPorts.length; ++i) + if (wcscmp(serialPorts.ports[i]->portPath, comPortString) == 0) + { + wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[i]->friendlyName, (wcslen(descriptionString)+1)*sizeof(wchar_t)); + if (newMemory) + { + serialPorts.ports[i]->friendlyName = newMemory; + wcscpy(serialPorts.ports[i]->friendlyName, descriptionString); + } + newMemory = (wchar_t*)realloc(serialPorts.ports[i]->portLocation, (wcslen(portLocation)+1)*sizeof(wchar_t)); + if (newMemory) + { + serialPorts.ports[i]->portLocation = newMemory; + wcscpy(serialPorts.ports[i]->portLocation, portLocation); + } + break; + } + + // Clean up memory + free(portLocation); + } + + // Clean up memory + free(comPort); + } + + // Clean up memory and close registry key + RegCloseKey(keyHandle5); + free(locationInfo); + free(friendlyName); + } + + // Close registry key + RegCloseKey(keyHandle4); + } + + // Clean up memory and close registry key + RegCloseKey(keyHandle3); + free(subKeyName3); + } + } + + // Clean up memory and close registry key + RegCloseKey(keyHandle2); + free(subKeyName2); + } + } + + // Clean up memory and close registry key + RegCloseKey(keyHandle1); + free(subKeyName1); + } + + // Attempt to locate any device-specified port descriptions + HDEVINFO devList = SetupDiGetClassDevsW(NULL, L"USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); + if (devList != INVALID_HANDLE_VALUE) + { + // Iterate through all USB-connected devices + DWORD devInterfaceIndex = 0; + DEVPROPTYPE devInfoPropType; + SP_DEVINFO_DATA devInfoData; + devInfoData.cbSize = sizeof(devInfoData); + WCHAR comPort[128]; + while (SetupDiEnumDeviceInfo(devList, devInterfaceIndex++, &devInfoData)) + { + // Fetch the corresponding COM port for this device + wchar_t* comPortString = NULL; + comPortLength = sizeof(comPort) / sizeof(WCHAR); + keyHandle5 = SetupDiOpenDevRegKey(devList, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE); + if ((keyHandle5 != INVALID_HANDLE_VALUE) && (RegQueryValueExW(keyHandle5, L"PortName", NULL, &keyType, (BYTE*)comPort, &comPortLength) == ERROR_SUCCESS) && (keyType == REG_SZ)) + comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort; + if (keyHandle5 != INVALID_HANDLE_VALUE) + RegCloseKey(keyHandle5); + + // Fetch the length of the "Bus-Reported Device Description" + if (comPortString && !SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, NULL, 0, &valueLength, 0) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) + { + // Allocate memory + ++valueLength; + WCHAR *portDescription = (WCHAR*)malloc(valueLength); + + // Retrieve the "Bus-Reported Device Description" + if (SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, (BYTE*)portDescription, valueLength, NULL, 0)) + { + // Update port description if COM port is actually connected and present in the port list + for (int i = 0; i < serialPorts.length; ++i) + if (wcscmp(serialPorts.ports[i]->portPath, comPortString) == 0) + { + wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[i]->portDescription, (wcslen(portDescription)+1)*sizeof(wchar_t)); + if (newMemory) + { + serialPorts.ports[i]->portDescription = newMemory; + wcscpy(serialPorts.ports[i]->portDescription, portDescription); + } + } + } + + // Clean up memory + free(portDescription); + } + devInfoData.cbSize = sizeof(devInfoData); + } + SetupDiDestroyDeviceInfoList(devList); + } +} + +int main(void) +{ + // Enumerate all serial ports + getPortsWindows(); + + // Output all enumerated ports + for (int i = 0; i < serialPorts.length; ++i) + { + serialPort *port = serialPorts.ports[i]; + printf("%ls: Description = %ls, Location = %ls\n", port->portPath, port->friendlyName, port->portLocation); + } + + return 0; +} diff --git a/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java b/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java index 4817917..c2315a3 100644 --- a/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java +++ b/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java @@ -85,13 +85,15 @@ public class SerialPortTest { System.out.println("\nUsing Library Version v" + SerialPort.getVersion()); SerialPort[] ports = SerialPort.getCommPorts(); - System.out.println("\nAvailable Ports (First Try):\n"); + System.out.println("\nAvailable Ports:\n"); for (int i = 0; i < ports.length; ++i) - System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + ": " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription()); + System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + ": " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation()); + System.out.println("Re-enumerating ports again in 5 seconds...\n"); + try { Thread.sleep(5000); } catch (Exception e) {} ports = SerialPort.getCommPorts(); - System.out.println("\nAvailable Ports (Second Try):\n"); + System.out.println("Available Ports:\n"); for (int i = 0; i < ports.length; ++i) - System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + ": " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription()); + System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + ": " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation()); SerialPort ubxPort; System.out.print("\nChoose your desired serial port or enter -1 to specify a port directly: "); int serialPortChoice = 0; @@ -147,6 +149,10 @@ public class SerialPortTest } catch (Exception e) { e.printStackTrace(); } System.out.println("\nSetting read timeout mode to semi-blocking with no timeout"); ubxPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 0, 0); + System.out.println("\nWaiting for available bytes to read..."); + while (ubxPort.bytesAvailable() == 0); + System.out.println("Available: " + ubxPort.bytesAvailable()); + System.out.println("Flushing read buffers: " + ubxPort.flushIOBuffers()); try { for (int i = 0; i < 3; ++i)