void searchForComPorts()

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