Add full functionality to retrieve physical port location

This commit is contained in:
Will Hedgecock 2021-12-16 19:43:59 -06:00
parent 2ce74d0e71
commit 35a5818281
32 changed files with 1362 additions and 42 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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))
{

View File

@ -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
* <p>
* 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".
* <p>
* "[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.
* <p>
* 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.
* <p>
* 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.

View File

@ -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 $@

View File

@ -0,0 +1,784 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "PosixHelperFunctions.h"
static serialPortVector comPorts = { NULL, 0, 0 };
#if defined(__linux__)
#include <linux/serial.h>
#include <asm/termios.h>
#include <asm/ioctls.h>
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 <AvailabilityMacros.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/serial/ioss.h>
#include <IOKit/usb/USBSpec.h>
#include <sys/ioctl.h>
#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;
}

View File

@ -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 <initguid.h>
#include <windows.h>
#include <delayimp.h>
#include <stdlib.h>
#include <string.h>
#include <setupapi.h>
#include <devpkey.h>
#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;
}

View File

@ -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)