in src/main/c/Posix/PosixHelperFunctions.c [551:794]
void searchForComPorts(serialPortVector* comPorts)
{
// Set up the necessary local variables
struct stat pathInfo = { 0 };
stringVector physicalPortPrefixes = { NULL, 0, 0 };
int fileNameMaxLength = 0, portDevPathMaxLength = 128, maxLineSize = 256;
char *fileName = NULL, *portDevPath = (char*)malloc(portDevPathMaxLength);
char *line = (char*)malloc(maxLineSize), *friendlyName = (char*)malloc(256);
char *portLocation = (char*)malloc(128), *interfaceDescription = (char*)malloc(256);
char *serialNumber = (char*)malloc(128), *manufacturer = (char*)malloc(128);
char *driverName = (char*)malloc(64);
// Retrieve the list of /dev/ prefixes for all physical serial ports
retrievePhysicalPortPrefixes(&physicalPortPrefixes);
// Iterate through the system TTY directory
DIR *directoryIterator = opendir("/sys/class/tty/");
if (directoryIterator)
{
struct dirent *directoryEntry = readdir(directoryIterator);
while (directoryEntry)
{
// Ensure that the file name buffer is large enough
if ((128 + strlen(directoryEntry->d_name)) > fileNameMaxLength)
{
fileNameMaxLength = 128 + strlen(directoryEntry->d_name);
fileName = (char*)realloc(fileName, fileNameMaxLength);
}
// Check if the entry represents a valid serial port
strcpy(fileName, "/sys/class/tty/");
strcat(fileName, directoryEntry->d_name);
char *basePathEnd = fileName + strlen(fileName);
sprintf(basePathEnd, "/device/driver");
char isSerialPort = (stat(fileName, &pathInfo) == 0) && S_ISDIR(pathInfo.st_mode);
sprintf(basePathEnd, "/dev");
isSerialPort = isSerialPort && (stat(fileName, &pathInfo) == 0) && S_ISREG(pathInfo.st_mode);
sprintf(basePathEnd, "/uevent");
isSerialPort = isSerialPort && (stat(fileName, &pathInfo) == 0) && S_ISREG(pathInfo.st_mode);
if (!isSerialPort)
{
directoryEntry = readdir(directoryIterator);
continue;
}
// Determine the /dev/ path to the device
isSerialPort = 0;
FILE *input = fopen(fileName, "r");
if (input)
{
while (fgets(line, maxLineSize, input))
if (strstr(line, "DEVNAME=") == line)
{
isSerialPort = 1;
strcpy(portDevPath, "/dev/");
strcat(portDevPath, line + 8);
portDevPath[strcspn(portDevPath, "\r\n")] = '\0';
}
fclose(input);
}
if (!isSerialPort)
{
directoryEntry = readdir(directoryIterator);
continue;
}
// Check if the device is a physical serial port
char isPhysical = 0;
int physicalPortNumber = 0;
for (int i = 0; !isPhysical && (i < physicalPortPrefixes.length); ++i)
if (strstr(portDevPath, physicalPortPrefixes.strings[i]) == portDevPath)
{
isPhysical = 1;
physicalPortNumber = atoi(portDevPath + strlen(physicalPortPrefixes.strings[i]));
}
// Determine the subsystem and bus location of the port
int vendorID = -1, productID = -1;
sprintf(basePathEnd, "/device/subsystem");
if (isUsbSerialSubsystem(fileName))
{
sprintf(basePathEnd, "/device/../");
basePathEnd += 11;
}
else
{
sprintf(basePathEnd, "/device/");
basePathEnd += 8;
}
getUsbDetails(fileName, basePathEnd, &vendorID, &productID, serialNumber, manufacturer, driverName);
sprintf(basePathEnd, "../");
getPortLocation(fileName, portLocation, isPhysical ? physicalPortNumber : -1);
// Check if the port has already been enumerated
serialPort *port = fetchPort(comPorts, portDevPath);
if (port)
{
if (isPhysical)
replaceDetails(port, port->friendlyName, port->portDescription, portLocation, port->serialNumber, port->manufacturer, port->deviceDriver, port->vendorID, port->productID, port->isSymlink);
else
{
// See if the device has a registered friendly name
friendlyName[0] = '\0';
sprintf(basePathEnd, "../product");
FILE *input = fopen(fileName, "rb");
if (input)
{
fgets(friendlyName, 256, input);
friendlyName[strcspn(friendlyName, "\r\n")] = '\0';
fclose(input);
}
if (friendlyName[0] == '\0')
assignFriendlyName(portDevPath, friendlyName);
// Attempt to read the bus-reported device description
interfaceDescription[0] = '\0';
sprintf(basePathEnd, "interface");
input = fopen(fileName, "rb");
if (input)
{
fgets(interfaceDescription, 256, input);
interfaceDescription[strcspn(interfaceDescription, "\r\n")] = '\0';
fclose(input);
}
if (interfaceDescription[0] == '\0')
strcpy(interfaceDescription, friendlyName);
replaceDetails(port, friendlyName, interfaceDescription, portLocation, serialNumber, manufacturer, driverName, vendorID, productID, 0);
}
// Continue port enumeration
directoryEntry = readdir(directoryIterator);
port->enumerated = 1;
continue;
}
// Retrieve all available port details based on its type
if (isPhysical)
{
// Probe the physical port to see if it actually exists
int fd = open(portDevPath, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd >= 0)
{
struct serial_struct serialInfo = { 0 };
if ((ioctl(fd, TIOCGSERIAL, &serialInfo) == 0) && (serialInfo.type != PORT_UNKNOWN))
{
// Add the port to the list of available ports
strcpy(friendlyName, "Physical Port ");
strcat(friendlyName, directoryEntry->d_name+3);
pushBack(comPorts, portDevPath, friendlyName, friendlyName, portLocation, "Unknown", "Unknown", driverName, -1, -1, 0);
}
close(fd);
}
}
else // Emulated serial port
{
// See if the device has a registered friendly name
friendlyName[0] = '\0';
sprintf(basePathEnd, "../product");
FILE *input = fopen(fileName, "rb");
if (input)
{
fgets(friendlyName, 256, input);
friendlyName[strcspn(friendlyName, "\r\n")] = '\0';
fclose(input);
}
if (friendlyName[0] == '\0')
assignFriendlyName(portDevPath, friendlyName);
// Attempt to read the bus-reported device description
interfaceDescription[0] = '\0';
sprintf(basePathEnd, "interface");
input = fopen(fileName, "rb");
if (input)
{
fgets(interfaceDescription, 256, input);
interfaceDescription[strcspn(interfaceDescription, "\r\n")] = '\0';
fclose(input);
}
if (interfaceDescription[0] == '\0')
strcpy(interfaceDescription, friendlyName);
// Add the port to the list of available ports
pushBack(comPorts, portDevPath, friendlyName, interfaceDescription, portLocation, serialNumber, manufacturer, driverName, vendorID, productID, 0);
}
// Read next TTY directory entry
directoryEntry = readdir(directoryIterator);
}
closedir(directoryIterator);
}
// Search through the system DEV directory for Bluetooth and PTY devices and symlinks
directoryIterator = opendir("/dev/");
if (directoryIterator)
{
struct dirent *directoryEntry = readdir(directoryIterator);
while (directoryEntry)
{
// Ensure that the file name buffer is large enough
if ((16 + strlen(directoryEntry->d_name)) > fileNameMaxLength)
{
fileNameMaxLength = 16 + strlen(directoryEntry->d_name);
fileName = (char*)realloc(fileName, fileNameMaxLength);
}
// Check if the entry represents a valid serial port
strcpy(fileName, "/dev/");
strcat(fileName, directoryEntry->d_name);
if (isPtyDevice(fileName))
pushBack(comPorts, fileName, "PTY Device", "Pseudo-Terminal Device", "0-0", "Unknown", "Unknown", "Unknown", -1, -1, 0);
else if (isBluetoothDevice(fileName))
pushBack(comPorts, fileName, "Bluetooth Device", "Bluetooth Device", "0-0", "Unknown", "Unknown", "Unknown", -1, -1, 0);
else
{
// Copy symlinked port details from its existing TTY enumeration
char *symlink = isSymlinkedTtyDevice(fileName);
if (symlink)
{
const serialPort *port = fetchPort(comPorts, symlink);
if (port)
pushBack(comPorts, fileName, port->friendlyName, port->portDescription, port->portLocation, port->serialNumber, port->manufacturer, port->deviceDriver, port->vendorID, port->productID, 1);
free(symlink);
}
}
// Read next TTY directory entry
directoryEntry = readdir(directoryIterator);
}
closedir(directoryIterator);
}
// Clean up dynamically allocated memory
freeStringVector(&physicalPortPrefixes);
if (fileName)
free(fileName);
free(driverName);
free(interfaceDescription);
free(serialNumber);
free(manufacturer);
free(portLocation);
free(friendlyName);
free(portDevPath);
free(line);
}