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)