diff --git a/ResourceManager.cpp b/ResourceManager.cpp index c4da2edd5..17e9e56b9 100644 --- a/ResourceManager.cpp +++ b/ResourceManager.cpp @@ -390,6 +390,18 @@ std::vector ResourceManager::GetSerialPorts() } } +std::vector ResourceManager::GetUSBSerialPorts() +{ + if(IsLocalClient()) + { + return(find_usb_serial_ports()); + } + else + { + return(find_usb_serial_ports()); + } +} + std::vector ResourceManager::GetUSBDeviceInfo() { if(IsLocalClient()) diff --git a/ResourceManager.h b/ResourceManager.h index ad12b26b2..62da96cdb 100644 --- a/ResourceManager.h +++ b/ResourceManager.h @@ -25,6 +25,7 @@ #include "i2c_smbus.h" #include "ResourceManagerCallback.h" #include "filesystem.h" +#include "find_usb_serial_port.h" using json = nlohmann::json; @@ -82,6 +83,7 @@ public: std::vector& GetI2CBuses(); std::vector GetI2CBusInfo(); std::vector GetSerialPorts(); + std::vector GetUSBSerialPorts(); std::vector GetUSBDeviceInfo(); PluginManagerInterface* GetPluginManager(); ProfileManager* GetProfileManager(); diff --git a/qt/OpenRGBHardwareIDsDialog/OpenRGBHardwareIDsDialog.cpp b/qt/OpenRGBHardwareIDsDialog/OpenRGBHardwareIDsDialog.cpp index 6721f940e..a84c5efcd 100644 --- a/qt/OpenRGBHardwareIDsDialog/OpenRGBHardwareIDsDialog.cpp +++ b/qt/OpenRGBHardwareIDsDialog/OpenRGBHardwareIDsDialog.cpp @@ -90,9 +90,26 @@ int OpenRGBHardwareIDsDialog::show() strings.push_back(line); } + std::vector serial_device_info = ResourceManager::get()->GetUSBSerialPorts(); + QTreeWidgetItem* serial_top = new QTreeWidgetItem(ui->HardwareIdsList, {"USB Serial devices"}); + + strings.push_back("\n[ USB Serial devices ]"); + + for(SerialDeviceInfo device_info : serial_device_info) + { + char line[550]; + + snprintf(line, 550, "[%04X:%04X]", device_info.vendor_id, device_info.product_id); + new QTreeWidgetItem(serial_top, {line, QString::fromStdString(device_info.port_path), QString::fromStdString(device_info.usb_path)}); + + snprintf(line, 550, "[%04X:%04X] %s (%s)", device_info.vendor_id, device_info.product_id, device_info.port_path.c_str(), device_info.usb_path.c_str()); + strings.push_back(line); + } + i2c_top->setExpanded(true); hid_top->setExpanded(true); usb_top->setExpanded(true); + serial_top->setExpanded(true); return this->exec(); } diff --git a/serial_port/find_usb_serial_port.h b/serial_port/find_usb_serial_port.h index 9448b4d50..d15a0584b 100644 --- a/serial_port/find_usb_serial_port.h +++ b/serial_port/find_usb_serial_port.h @@ -19,3 +19,13 @@ #include std::vector find_usb_serial_port(unsigned short vid, unsigned short pid); + +struct SerialDeviceInfo +{ + unsigned short vendor_id; + unsigned short product_id; + std::string port_path; + std::string usb_path; +} ; + +std::vector find_usb_serial_ports(); diff --git a/serial_port/find_usb_serial_port_linux.cpp b/serial_port/find_usb_serial_port_linux.cpp index b5b339f23..c37a0c245 100644 --- a/serial_port/find_usb_serial_port_linux.cpp +++ b/serial_port/find_usb_serial_port_linux.cpp @@ -10,6 +10,7 @@ #include "find_usb_serial_port.h" +#include #include #include @@ -144,3 +145,117 @@ std::vector find_usb_serial_port(unsigned short vid, unsigned short return ret_vector; } /* find_usb_serial_port() */ + +std::vector find_usb_serial_ports() +{ + std::vector ret_vector; + DIR* dir; + char symlink_path[1024] = {0}; + struct dirent* ent; + + /*-----------------------------------------------------------------*\ + | Open /sys/class/tty | + \*-----------------------------------------------------------------*/ + dir = opendir("/sys/class/tty"); + + if(dir == NULL) + { + return ret_vector; + } + + /*-----------------------------------------------------------------*\ + | Loop through all symlinks in /sys/class/tty directory to find | + | paths with "usb" in them. These links should have the USB device | + | index which can be used to find the VID/PID | + \*-----------------------------------------------------------------*/ + ent = readdir(dir); + + while(ent != NULL) + { + if(ent->d_type == DT_LNK) + { + char tty_path[1024]; + strcpy(tty_path, "/sys/class/tty/"); + strcat(tty_path, ent->d_name); + + /*-----------------------------------------------------------------*\ + | readlink() does not null-terminate, so manually terminate it | + \*-----------------------------------------------------------------*/ + ssize_t link_path_size = readlink(tty_path, symlink_path, 1024); + if(link_path_size < 0 || link_path_size >= 1024) + { + /*-----------------------------------------------------------------*\ + | readlink failed or buffer too small, skip this device | + \*-----------------------------------------------------------------*/ + ent = readdir(dir); + continue; + } + symlink_path[link_path_size] = '\0'; + + char * usb_string = strstr(symlink_path, "usb"); + + if(usb_string != NULL) + { + char * usb_dev = strstr(usb_string, "/"); + usb_dev++; + char * usb_end = strstr(usb_dev, "/tty"); + *usb_end = '\0'; + + usb_end = strrchr(usb_dev, '/'); + *usb_end = '\0'; + + char usb_path[1024]; + + strcpy(usb_path, "/sys/bus/usb/devices/"); + strcat(usb_path, usb_dev); + + char vendor_path[1024]; + char product_path[1024]; + + strcpy(vendor_path, usb_path); + strcat(vendor_path, "/idVendor"); + + strcpy(product_path, usb_path); + strcat(product_path, "/idProduct"); + + std::ifstream vendor_file; + std::ifstream product_file; + std::string vendor_string; + std::string product_string; + + vendor_file.open(vendor_path); + product_file.open(product_path); + + std::getline(vendor_file, vendor_string); + std::getline(product_file, product_string); + + char* port_string = NULL; + for(int i = strlen(tty_path); i > 0; i--) + { + if(tty_path[i] == '/') + { + port_string = &tty_path[i + 1]; + break; + } + } + + SerialDeviceInfo entry; + entry.port_path = "/dev/"; + entry.port_path.append(port_string); + entry.usb_path = usb_path; + + entry.vendor_id = strtol(vendor_string.c_str(), NULL, 16); + entry.product_id = strtol(product_string.c_str(), NULL, 16); + + ret_vector.push_back(entry); + } + } + + ent = readdir(dir); + } + + + closedir(dir); + return ret_vector; + +} /* find_usb_serial_ports() */ diff --git a/serial_port/find_usb_serial_port_macos.cpp b/serial_port/find_usb_serial_port_macos.cpp index b8597a1ed..306c2439b 100644 --- a/serial_port/find_usb_serial_port_macos.cpp +++ b/serial_port/find_usb_serial_port_macos.cpp @@ -169,3 +169,116 @@ std::vector find_usb_serial_port(unsigned short vid, unsigned short \*-----------------------------------------------------*/ return(ret_vector); } + +std::vector find_usb_serial_ports() +{ + /*-----------------------------------------------------*\ + | Return variables | + \*-----------------------------------------------------*/ + std::vector ret_vector; + + /*-----------------------------------------------------*\ + | Execute command to list USB devices | + | | + | Top level entry lines in ioreg output start with | + | "+-o". Start the string with an extra newline so | + | that we can search for "\n+-0" to identify only hits | + | at the beginning of a line. | + \*-----------------------------------------------------*/ + std::string out_string = "\n" + exec("ioreg -r -c IOUSBHostDevice -l"); + + /*-----------------------------------------------------*\ + | Start position counter at 0 | + \*-----------------------------------------------------*/ + std::size_t pos = 0; + + /*-----------------------------------------------------*\ + | Loop through ioreg output, loop exits when "\n+-o" | + | string cannot be found. | + \*-----------------------------------------------------*/ + while(1) + { + /*-------------------------------------------------*\ + | Variables to store positions in string | + \*-------------------------------------------------*/ + std::size_t next_pos; + std::size_t vid_pos; + std::size_t pid_pos; + + /*-------------------------------------------------*\ + | Search for the next 2 iterations of "\n+-o" so | + | that we can check if hits are in between them | + \*-------------------------------------------------*/ + pos = out_string.find("\n+-o", pos); + next_pos = out_string.find("\n+-o", pos + 1); + + /*-------------------------------------------------*\ + | Search for the vendor and product ID strings in | + | and verify that they are between pos and next_pos | + \*-------------------------------------------------*/ + vid_pos = out_string.find(ID_VENDOR_STR, pos); + pid_pos = out_string.find(ID_PRODUCT_STR, pos); + + /*-------------------------------------------------*\ + | Verify that VID/PID matches are within this | + | device block by checking that their positions are | + | less than next_pos. If next_pos is invalid, | + | this is the last block, in which case check if | + | VID/PID positions are valid. | + \*-------------------------------------------------*/ + if(((vid_pos < next_pos) && (pid_pos < next_pos)) || ((pos == std::string::npos) && (vid_pos != std::string::npos) && (pid_pos != std::string::npos))) + { + /*---------------------------------------------*\ + | Variables to store positions in string | + \*---------------------------------------------*/ + std::size_t dev_pos; + std::size_t start_pos; + std::size_t end_pos; + + /*---------------------------------------------*\ + | Look for the IO callout device tag and then | + | get the start and end positions of its value | + \*---------------------------------------------*/ + dev_pos = out_string.find(IO_CALLOUT_STR, pos + 1); + start_pos = out_string.find("\"", dev_pos + sizeof(IO_CALLOUT_STR)) + 1; + end_pos = out_string.find("\"\n", start_pos); + + /*---------------------------------------------*\ + | Ensure the IO callout device tag is within | + | this device's section | + \*---------------------------------------------*/ + if(dev_pos < next_pos) + { + SerialDeviceInfo entry; + entry.port_path = out_string.substr(start_pos, end_pos-start_pos); + + std::string vendor_string = out_string.substr(vid_pos + strlen(ID_VENDOR_STR), 6); + std::string product_string = out_string.substr(vid_pos + strlen(ID_PRODUCT_STR), 6); + /*---------------------------------------------*\ + | This data SEEMS to be 10-based | + \*---------------------------------------------*/ + entry.vendor_id = strtol(vendor_string.c_str(), NULL, 10); + entry.product_id = strtol(product_string.c_str(), NULL, 10); + } + } + + /*-------------------------------------------------*\ + | If we've reached the end of the string, break out | + | of the loop | + \*-------------------------------------------------*/ + if(pos == std::string::npos) + { + break; + } + + /*-------------------------------------------------*\ + | Increment position | + \*-------------------------------------------------*/ + pos++; + } + + /*-----------------------------------------------------*\ + | Return vector of detected strings | + \*-----------------------------------------------------*/ + return(ret_vector); +} diff --git a/serial_port/find_usb_serial_port_win.cpp b/serial_port/find_usb_serial_port_win.cpp index ff7efb852..7f17fda2a 100644 --- a/serial_port/find_usb_serial_port_win.cpp +++ b/serial_port/find_usb_serial_port_win.cpp @@ -131,3 +131,101 @@ std::vector find_usb_serial_port(unsigned short vid, unsigned short return ret_vector; } /* find_usb_serial_port() */ + +std::vector find_usb_serial_ports() +{ + std::vector ret_vector; + HDEVINFO DeviceInfoSet; + DWORD DeviceIndex = 0; + SP_DEVINFO_DATA DeviceInfoData; + const char * DevEnum = "USB"; + char szBuffer[1024] = {0}; + DEVPROPTYPE ulPropertyType; + DWORD dwSize = 0; + + /*-----------------------------------------------------------------*\ + | SetupDiGetClassDevs returns a handle to a device information set | + \*-----------------------------------------------------------------*/ + DeviceInfoSet = SetupDiGetClassDevs( NULL, DevEnum, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); + + if (DeviceInfoSet == INVALID_HANDLE_VALUE) + { + return ret_vector; + } + + /*-----------------------------------------------------------------*\ + | Set up Device Info Data | + \*-----------------------------------------------------------------*/ + memset(&DeviceInfoData, 0, sizeof(SP_DEVINFO_DATA)); + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + /*-----------------------------------------------------------------*\ + | Receive information about an enumerated device | + \*-----------------------------------------------------------------*/ + while (SetupDiEnumDeviceInfo( DeviceInfoSet, DeviceIndex, &DeviceInfoData)) + { + DeviceIndex++; + + /*-------------------------------------------------------------*\ + | Retrieves a specified Plug and Play device property | + \*-------------------------------------------------------------*/ + if (SetupDiGetDeviceRegistryProperty (DeviceInfoSet, &DeviceInfoData, SPDRP_HARDWAREID, &ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) + { + HKEY hDeviceRegistryKey; + + /*-----------------------------------------------------*\ + | Check if the string for this device property matches | + | our expected device string (i.e. starts with USB ID) | + \*-----------------------------------------------------*/ + + if(strncmp(szBuffer, "USB\\VID_", 9) == 0) + { + hDeviceRegistryKey = SetupDiOpenDevRegKey(DeviceInfoSet, &DeviceInfoData,DICS_FLAG_GLOBAL, 0,DIREG_DEV, KEY_READ); + if (hDeviceRegistryKey == INVALID_HANDLE_VALUE) + { + break; + } + else + { + char pszPortName[BUFF_LEN]; + DWORD dwSize = sizeof(pszPortName); + DWORD dwType = 0; + + /*-----------------------------------------------------*\ + | Read in the name of the port | + \*-----------------------------------------------------*/ + if( (RegQueryValueEx(hDeviceRegistryKey,"PortName", NULL, &dwType, (LPBYTE) pszPortName, &dwSize) == ERROR_SUCCESS) && (dwType == REG_SZ)) + { + if(strncmp(pszPortName, "COM", 3) == 0) + { + char vid_str[5] = {0}; + char pid_str[5] = {0}; + strncpy(vid_str, szBuffer + 9, 4); + strncpy(pid_str, szBuffer + 18, 4); + + SerialDeviceInfo entry; + entry.port_path = pszPortName; + entry.usb_path = szBuffer; + + entry.vendor_id = (unsigned short)strtol(vid_str, NULL, 16); + entry.product_id = (unsigned short)strtol(pid_str, NULL, 16); + + ret_vector.push_back(entry); + } + } + + // Close the key now that we are finished with it + RegCloseKey(hDeviceRegistryKey); + } + } + } + } + + if (DeviceInfoSet) + { + SetupDiDestroyDeviceInfoList(DeviceInfoSet); + } + + return ret_vector; + +} /* find_usb_serial_ports() */