#include #include #include #include #include #include /** * @brief Structure to hold information about a discovered serial port. */ struct SerialPortInfo { std::string device; // e.g., /dev/ttyUSB0 std::string description; // e.g., FTDI - FT232R USB UART std::string hardware_id; // e.g., USB VID:PID=0403:6001 SER=A9010T9R }; // --- Helper Functions --- /** * @brief Safely retrieves a property value from a udev device. * @param dev The udev device pointer. * @param key The property key (e.g., "ID_VENDOR_ID"). * @return The property value as a string, or an empty string if not found. */ std::string get_udev_property(struct udev_device *dev, const char *key) { const char *value = udev_device_get_property_value(dev, key); return value ? value : ""; } /** * @brief Finds the parent USB device for a given device. * @param dev The starting udev device pointer. * @return A pointer to the parent USB device, or nullptr if not found. */ struct udev_device* get_parent_usb(struct udev_device *dev) { // Traverses up the device tree until a device with subsystem "usb" and devtype "usb_device" is found. return udev_device_get_parent_with_subsystem_devtype( dev, "usb", "usb_device" ); } // --- Core Logic --- /** * @brief Enumerates available serial ports on the system using libudev. * @return A vector of SerialPortInfo structs. */ std::vector list_serial_ports() { std::vector ports; struct udev *udev = nullptr; struct udev_enumerate *enumerate = nullptr; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *dev; // 1. Create the udev object udev = udev_new(); if (!udev) { std::cerr << "Error: Cannot create udev object." << std::endl; return ports; } // 2. Create an enumeration object enumerate = udev_enumerate_new(udev); if (!enumerate) { std::cerr << "Error: Cannot create enumerate object." << std::endl; udev_unref(udev); return ports; } // 3. Filter: Match devices in the 'tty' subsystem (includes ttyS, ttyUSB, ttyACM) udev_enumerate_add_match_subsystem(enumerate, "tty"); // 4. Scan devices udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); // 5. Iterate through the found devices udev_list_entry_foreach(dev_list_entry, devices) { const char *path = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(udev, path); if (!dev) continue; const char *devnode = udev_device_get_devnode(dev); if (devnode) { std::string node_name(devnode); // Filter: Only process common serial port names if (node_name.find("/dev/ttyS") == 0 || node_name.find("/dev/ttyUSB") == 0 || node_name.find("/dev/ttyACM") == 0) { SerialPortInfo info; info.device = node_name; info.description = "Serial Port"; info.hardware_id = "N/A"; // --- Try to get USB details (VID/PID, Description) --- struct udev_device *usb_parent = get_parent_usb(dev); if (usb_parent) { // It's a USB serial device, get its properties std::string vendor_id = get_udev_property(usb_parent, "ID_VENDOR_ID"); std::string model_id = get_udev_property(usb_parent, "ID_MODEL_ID"); std::string manufacturer = get_udev_property(usb_parent, "ID_VENDOR"); std::string product = get_udev_property(usb_parent, "ID_MODEL"); std::string serial_short = get_udev_property(usb_parent, "ID_SERIAL_SHORT"); // Build the Hardware ID string (VID:PID SER=...) if (!vendor_id.empty() && !model_id.empty()) { std::stringstream ss; ss << "USB VID:PID=" << vendor_id << ":" << model_id; if (!serial_short.empty()) { ss << " SER=" << serial_short; } info.hardware_id = ss.str(); } // Build the Description string if (!product.empty()) { info.description = !manufacturer.empty() ? (manufacturer + " - " + product) : product; } else { info.description = "USB Serial Device"; } } ports.push_back(info); } } udev_device_unref(dev); } // 6. Cleanup udev_enumerate_unref(enumerate); udev_unref(udev); return ports; } int main() { std::cout << "Available Serial Ports:\n"; std::cout << "------------------------------------------------------------------------\n"; std::vector ports = list_serial_ports(); if (ports.empty()) { std::cout << "No serial ports found.\n"; } else { for (const auto& port : ports) { std::cout << "Device: " << port.device << "\n"; std::cout << "Description: " << port.description << "\n"; std::cout << "Hardware ID: " << port.hardware_id << "\n"; std::cout << "------------------------------------------------------------------------\n"; } } return 0; }