aboutsummaryrefslogtreecommitdiffstats
path: root/source/serial/src/impl/list_ports
diff options
context:
space:
mode:
Diffstat (limited to 'source/serial/src/impl/list_ports')
-rw-r--r--source/serial/src/impl/list_ports/list_ports_linux.cc335
-rw-r--r--source/serial/src/impl/list_ports/list_ports_osx.cc286
-rw-r--r--source/serial/src/impl/list_ports/list_ports_win.cc152
3 files changed, 773 insertions, 0 deletions
diff --git a/source/serial/src/impl/list_ports/list_ports_linux.cc b/source/serial/src/impl/list_ports/list_ports_linux.cc
new file mode 100644
index 0000000..9779d5e
--- /dev/null
+++ b/source/serial/src/impl/list_ports/list_ports_linux.cc
@@ -0,0 +1,335 @@
+#if defined(__linux__)
+
+/*
+ * Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
+ * This software is made available under the terms of the MIT licence.
+ * A copy of the licence can be obtained from:
+ * http://opensource.org/licenses/MIT
+ */
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+#include <iostream>
+#include <fstream>
+#include <cstdio>
+#include <cstdarg>
+#include <cstdlib>
+
+#include <glob.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "serial/serial.h"
+
+using serial::PortInfo;
+using std::istringstream;
+using std::ifstream;
+using std::getline;
+using std::vector;
+using std::string;
+using std::cout;
+using std::endl;
+
+static vector<string> glob(const vector<string>& patterns);
+static string basename(const string& path);
+static string dirname(const string& path);
+static bool path_exists(const string& path);
+static string realpath(const string& path);
+static string usb_sysfs_friendly_name(const string& sys_usb_path);
+static vector<string> get_sysfs_info(const string& device_path);
+static string read_line(const string& file);
+static string usb_sysfs_hw_string(const string& sysfs_path);
+static string format(const char* format, ...);
+
+vector<string>
+glob(const vector<string>& patterns)
+{
+ vector<string> paths_found;
+
+ if(patterns.size() == 0)
+ return paths_found;
+
+ glob_t glob_results;
+
+ int glob_retval = glob(patterns[0].c_str(), 0, NULL, &glob_results);
+
+ vector<string>::const_iterator iter = patterns.begin();
+
+ while(++iter != patterns.end())
+ {
+ glob_retval = glob(iter->c_str(), GLOB_APPEND, NULL, &glob_results);
+ }
+
+ for(int path_index = 0; path_index < glob_results.gl_pathc; path_index++)
+ {
+ paths_found.push_back(glob_results.gl_pathv[path_index]);
+ }
+
+ globfree(&glob_results);
+
+ return paths_found;
+}
+
+string
+basename(const string& path)
+{
+ size_t pos = path.rfind("/");
+
+ if(pos == std::string::npos)
+ return path;
+
+ return string(path, pos+1, string::npos);
+}
+
+string
+dirname(const string& path)
+{
+ size_t pos = path.rfind("/");
+
+ if(pos == std::string::npos)
+ return path;
+ else if(pos == 0)
+ return "/";
+
+ return string(path, 0, pos);
+}
+
+bool
+path_exists(const string& path)
+{
+ struct stat sb;
+
+ if( stat(path.c_str(), &sb ) == 0 )
+ return true;
+
+ return false;
+}
+
+string
+realpath(const string& path)
+{
+ char* real_path = realpath(path.c_str(), NULL);
+
+ string result;
+
+ if(real_path != NULL)
+ {
+ result = real_path;
+
+ free(real_path);
+ }
+
+ return result;
+}
+
+string
+usb_sysfs_friendly_name(const string& sys_usb_path)
+{
+ unsigned int device_number = 0;
+
+ istringstream( read_line(sys_usb_path + "/devnum") ) >> device_number;
+
+ string manufacturer = read_line( sys_usb_path + "/manufacturer" );
+
+ string product = read_line( sys_usb_path + "/product" );
+
+ string serial = read_line( sys_usb_path + "/serial" );
+
+ if( manufacturer.empty() && product.empty() && serial.empty() )
+ return "";
+
+ return format("%s %s %s", manufacturer.c_str(), product.c_str(), serial.c_str() );
+}
+
+vector<string>
+get_sysfs_info(const string& device_path)
+{
+ string device_name = basename( device_path );
+
+ string friendly_name;
+
+ string hardware_id;
+
+ string sys_device_path = format( "/sys/class/tty/%s/device", device_name.c_str() );
+
+ if( device_name.compare(0,6,"ttyUSB") == 0 )
+ {
+ sys_device_path = dirname( dirname( realpath( sys_device_path ) ) );
+
+ if( path_exists( sys_device_path ) )
+ {
+ friendly_name = usb_sysfs_friendly_name( sys_device_path );
+
+ hardware_id = usb_sysfs_hw_string( sys_device_path );
+ }
+ }
+ else if( device_name.compare(0,6,"ttyACM") == 0 )
+ {
+ sys_device_path = dirname( realpath( sys_device_path ) );
+
+ if( path_exists( sys_device_path ) )
+ {
+ friendly_name = usb_sysfs_friendly_name( sys_device_path );
+
+ hardware_id = usb_sysfs_hw_string( sys_device_path );
+ }
+ }
+ else
+ {
+ // Try to read ID string of PCI device
+
+ string sys_id_path = sys_device_path + "/id";
+
+ if( path_exists( sys_id_path ) )
+ hardware_id = read_line( sys_id_path );
+ }
+
+ if( friendly_name.empty() )
+ friendly_name = device_name;
+
+ if( hardware_id.empty() )
+ hardware_id = "n/a";
+
+ vector<string> result;
+ result.push_back(friendly_name);
+ result.push_back(hardware_id);
+
+ return result;
+}
+
+string
+read_line(const string& file)
+{
+ ifstream ifs(file.c_str(), ifstream::in);
+
+ string line;
+
+ if(ifs)
+ {
+ getline(ifs, line);
+ }
+
+ return line;
+}
+
+string
+format(const char* format, ...)
+{
+ va_list ap;
+
+ size_t buffer_size_bytes = 256;
+
+ string result;
+
+ char* buffer = (char*)malloc(buffer_size_bytes);
+
+ if( buffer == NULL )
+ return result;
+
+ bool done = false;
+
+ unsigned int loop_count = 0;
+
+ while(!done)
+ {
+ va_start(ap, format);
+
+ int return_value = vsnprintf(buffer, buffer_size_bytes, format, ap);
+
+ if( return_value < 0 )
+ {
+ done = true;
+ }
+ else if( return_value >= buffer_size_bytes )
+ {
+ // Realloc and try again.
+
+ buffer_size_bytes = return_value + 1;
+
+ char* new_buffer_ptr = (char*)realloc(buffer, buffer_size_bytes);
+
+ if( new_buffer_ptr == NULL )
+ {
+ done = true;
+ }
+ else
+ {
+ buffer = new_buffer_ptr;
+ }
+ }
+ else
+ {
+ result = buffer;
+ done = true;
+ }
+
+ va_end(ap);
+
+ if( ++loop_count > 5 )
+ done = true;
+ }
+
+ free(buffer);
+
+ return result;
+}
+
+string
+usb_sysfs_hw_string(const string& sysfs_path)
+{
+ string serial_number = read_line( sysfs_path + "/serial" );
+
+ if( serial_number.length() > 0 )
+ {
+ serial_number = format( "SNR=%s", serial_number.c_str() );
+ }
+
+ string vid = read_line( sysfs_path + "/idVendor" );
+
+ string pid = read_line( sysfs_path + "/idProduct" );
+
+ return format("USB VID:PID=%s:%s %s", vid.c_str(), pid.c_str(), serial_number.c_str() );
+}
+
+vector<PortInfo>
+serial::list_ports()
+{
+ vector<PortInfo> results;
+
+ vector<string> search_globs;
+ search_globs.push_back("/dev/ttyACM*");
+ search_globs.push_back("/dev/ttyS*");
+ search_globs.push_back("/dev/ttyUSB*");
+ search_globs.push_back("/dev/tty.*");
+ search_globs.push_back("/dev/cu.*");
+
+ vector<string> devices_found = glob( search_globs );
+
+ vector<string>::iterator iter = devices_found.begin();
+
+ while( iter != devices_found.end() )
+ {
+ string device = *iter++;
+
+ vector<string> sysfs_info = get_sysfs_info( device );
+
+ string friendly_name = sysfs_info[0];
+
+ string hardware_id = sysfs_info[1];
+
+ PortInfo device_entry;
+ device_entry.port = device;
+ device_entry.description = friendly_name;
+ device_entry.hardware_id = hardware_id;
+
+ results.push_back( device_entry );
+
+ }
+
+ return results;
+}
+
+#endif // defined(__linux__)
diff --git a/source/serial/src/impl/list_ports/list_ports_osx.cc b/source/serial/src/impl/list_ports/list_ports_osx.cc
new file mode 100644
index 0000000..333c55c
--- /dev/null
+++ b/source/serial/src/impl/list_ports/list_ports_osx.cc
@@ -0,0 +1,286 @@
+#if defined(__APPLE__)
+
+#include <sys/param.h>
+#include <stdint.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+#include <IOKit/IOBSD.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "serial/serial.h"
+
+using serial::PortInfo;
+using std::string;
+using std::vector;
+
+#define HARDWARE_ID_STRING_LENGTH 128
+
+string cfstring_to_string( CFStringRef cfstring );
+string get_device_path( io_object_t& serial_port );
+string get_class_name( io_object_t& obj );
+io_registry_entry_t get_parent_iousb_device( io_object_t& serial_port );
+string get_string_property( io_object_t& device, const char* property );
+uint16_t get_int_property( io_object_t& device, const char* property );
+string rtrim(const string& str);
+
+string
+cfstring_to_string( CFStringRef cfstring )
+{
+ char cstring[MAXPATHLEN];
+ string result;
+
+ if( cfstring )
+ {
+ Boolean success = CFStringGetCString( cfstring,
+ cstring,
+ sizeof(cstring),
+ kCFStringEncodingASCII );
+
+ if( success )
+ result = cstring;
+ }
+
+ return result;
+}
+
+string
+get_device_path( io_object_t& serial_port )
+{
+ CFTypeRef callout_path;
+ string device_path;
+
+ callout_path = IORegistryEntryCreateCFProperty( serial_port,
+ CFSTR(kIOCalloutDeviceKey),
+ kCFAllocatorDefault,
+ 0 );
+
+ if (callout_path)
+ {
+ if( CFGetTypeID(callout_path) == CFStringGetTypeID() )
+ device_path = cfstring_to_string( static_cast<CFStringRef>(callout_path) );
+
+ CFRelease(callout_path);
+ }
+
+ return device_path;
+}
+
+string
+get_class_name( io_object_t& obj )
+{
+ string result;
+ io_name_t class_name;
+ kern_return_t kern_result;
+
+ kern_result = IOObjectGetClass( obj, class_name );
+
+ if( kern_result == KERN_SUCCESS )
+ result = class_name;
+
+ return result;
+}
+
+io_registry_entry_t
+get_parent_iousb_device( io_object_t& serial_port )
+{
+ io_object_t device = serial_port;
+ io_registry_entry_t parent = 0;
+ io_registry_entry_t result = 0;
+ kern_return_t kern_result = KERN_FAILURE;
+ string name = get_class_name(device);
+
+ // Walk the IO Registry tree looking for this devices parent IOUSBDevice.
+ while( name != "IOUSBDevice" )
+ {
+ kern_result = IORegistryEntryGetParentEntry( device,
+ kIOServicePlane,
+ &parent );
+
+ if(kern_result != KERN_SUCCESS)
+ {
+ result = 0;
+ break;
+ }
+
+ device = parent;
+
+ name = get_class_name(device);
+ }
+
+ if(kern_result == KERN_SUCCESS)
+ result = device;
+
+ return result;
+}
+
+string
+get_string_property( io_object_t& device, const char* property )
+{
+ string property_name;
+
+ if( device )
+ {
+ CFStringRef property_as_cfstring = CFStringCreateWithCString (
+ kCFAllocatorDefault,
+ property,
+ kCFStringEncodingASCII );
+
+ CFTypeRef name_as_cfstring = IORegistryEntryCreateCFProperty(
+ device,
+ property_as_cfstring,
+ kCFAllocatorDefault,
+ 0 );
+
+ if( name_as_cfstring )
+ {
+ if( CFGetTypeID(name_as_cfstring) == CFStringGetTypeID() )
+ property_name = cfstring_to_string( static_cast<CFStringRef>(name_as_cfstring) );
+
+ CFRelease(name_as_cfstring);
+ }
+
+ if(property_as_cfstring)
+ CFRelease(property_as_cfstring);
+ }
+
+ return property_name;
+}
+
+uint16_t
+get_int_property( io_object_t& device, const char* property )
+{
+ uint16_t result = 0;
+
+ if( device )
+ {
+ CFStringRef property_as_cfstring = CFStringCreateWithCString (
+ kCFAllocatorDefault,
+ property,
+ kCFStringEncodingASCII );
+
+ CFTypeRef number = IORegistryEntryCreateCFProperty( device,
+ property_as_cfstring,
+ kCFAllocatorDefault,
+ 0 );
+
+ if(property_as_cfstring)
+ CFRelease(property_as_cfstring);
+
+ if( number )
+ {
+ if( CFGetTypeID(number) == CFNumberGetTypeID() )
+ {
+ bool success = CFNumberGetValue( static_cast<CFNumberRef>(number),
+ kCFNumberSInt16Type,
+ &result );
+
+ if( !success )
+ result = 0;
+ }
+
+ CFRelease(number);
+ }
+
+ }
+
+ return result;
+}
+
+string rtrim(const string& str)
+{
+ string result = str;
+
+ string whitespace = " \t\f\v\n\r";
+
+ std::size_t found = result.find_last_not_of(whitespace);
+
+ if (found != std::string::npos)
+ result.erase(found+1);
+ else
+ result.clear();
+
+ return result;
+}
+
+vector<PortInfo>
+serial::list_ports(void)
+{
+ vector<PortInfo> devices_found;
+ CFMutableDictionaryRef classes_to_match;
+ io_iterator_t serial_port_iterator;
+ io_object_t serial_port;
+ mach_port_t master_port;
+ kern_return_t kern_result;
+
+ kern_result = IOMasterPort(MACH_PORT_NULL, &master_port);
+
+ if(kern_result != KERN_SUCCESS)
+ return devices_found;
+
+ classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue);
+
+ if (classes_to_match == NULL)
+ return devices_found;
+
+ CFDictionarySetValue( classes_to_match,
+ CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDAllTypes) );
+
+ kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &serial_port_iterator);
+
+ if (KERN_SUCCESS != kern_result)
+ return devices_found;
+
+ while ( (serial_port = IOIteratorNext(serial_port_iterator)) )
+ {
+ string device_path = get_device_path( serial_port );
+ io_registry_entry_t parent = get_parent_iousb_device( serial_port );
+ IOObjectRelease(serial_port);
+
+ if( device_path.empty() )
+ continue;
+
+ PortInfo port_info;
+ port_info.port = device_path;
+ port_info.description = "n/a";
+ port_info.hardware_id = "n/a";
+
+ string device_name = rtrim( get_string_property( parent, "USB Product Name" ) );
+ string vendor_name = rtrim( get_string_property( parent, "USB Vendor Name") );
+ string description = rtrim( vendor_name + " " + device_name );
+ if( !description.empty() )
+ port_info.description = description;
+
+ string serial_number = rtrim(get_string_property( parent, "USB Serial Number" ) );
+ uint16_t vendor_id = get_int_property( parent, "idVendor" );
+ uint16_t product_id = get_int_property( parent, "idProduct" );
+
+ if( vendor_id && product_id )
+ {
+ char cstring[HARDWARE_ID_STRING_LENGTH];
+
+ if(serial_number.empty())
+ serial_number = "None";
+
+ int ret = snprintf( cstring, HARDWARE_ID_STRING_LENGTH, "USB VID:PID=%04x:%04x SNR=%s",
+ vendor_id,
+ product_id,
+ serial_number.c_str() );
+
+ if( (ret >= 0) && (ret < HARDWARE_ID_STRING_LENGTH) )
+ port_info.hardware_id = cstring;
+ }
+
+ devices_found.push_back(port_info);
+ }
+
+ IOObjectRelease(serial_port_iterator);
+ return devices_found;
+}
+
+#endif // defined(__APPLE__)
diff --git a/source/serial/src/impl/list_ports/list_ports_win.cc b/source/serial/src/impl/list_ports/list_ports_win.cc
new file mode 100644
index 0000000..7da40c4
--- /dev/null
+++ b/source/serial/src/impl/list_ports/list_ports_win.cc
@@ -0,0 +1,152 @@
+#if defined(_WIN32)
+
+/*
+ * Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
+ * This software is made available under the terms of the MIT licence.
+ * A copy of the licence can be obtained from:
+ * http://opensource.org/licenses/MIT
+ */
+
+#include "serial/serial.h"
+#include <tchar.h>
+#include <windows.h>
+#include <setupapi.h>
+#include <initguid.h>
+#include <devguid.h>
+#include <cstring>
+
+using serial::PortInfo;
+using std::vector;
+using std::string;
+
+static const DWORD port_name_max_length = 256;
+static const DWORD friendly_name_max_length = 256;
+static const DWORD hardware_id_max_length = 256;
+
+// Convert a wide Unicode string to an UTF8 string
+std::string utf8_encode(const std::wstring &wstr)
+{
+ int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
+ std::string strTo( size_needed, 0 );
+ WideCharToMultiByte (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
+ return strTo;
+}
+
+vector<PortInfo>
+serial::list_ports()
+{
+ vector<PortInfo> devices_found;
+
+ HDEVINFO device_info_set = SetupDiGetClassDevs(
+ (const GUID *) &GUID_DEVCLASS_PORTS,
+ NULL,
+ NULL,
+ DIGCF_PRESENT);
+
+ unsigned int device_info_set_index = 0;
+ SP_DEVINFO_DATA device_info_data;
+
+ device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ while(SetupDiEnumDeviceInfo(device_info_set, device_info_set_index, &device_info_data))
+ {
+ device_info_set_index++;
+
+ // Get port name
+
+ HKEY hkey = SetupDiOpenDevRegKey(
+ device_info_set,
+ &device_info_data,
+ DICS_FLAG_GLOBAL,
+ 0,
+ DIREG_DEV,
+ KEY_READ);
+
+ TCHAR port_name[port_name_max_length];
+ DWORD port_name_length = port_name_max_length;
+
+ LONG return_code = RegQueryValueEx(
+ hkey,
+ _T("PortName"),
+ NULL,
+ NULL,
+ (LPBYTE)port_name,
+ &port_name_length);
+
+ RegCloseKey(hkey);
+
+ if(return_code != EXIT_SUCCESS)
+ continue;
+
+ if(port_name_length > 0 && port_name_length <= port_name_max_length)
+ port_name[port_name_length-1] = '\0';
+ else
+ port_name[0] = '\0';
+
+ // Ignore parallel ports
+
+ if(_tcsstr(port_name, _T("LPT")) != NULL)
+ continue;
+
+ // Get port friendly name
+
+ TCHAR friendly_name[friendly_name_max_length];
+ DWORD friendly_name_actual_length = 0;
+
+ BOOL got_friendly_name = SetupDiGetDeviceRegistryProperty(
+ device_info_set,
+ &device_info_data,
+ SPDRP_FRIENDLYNAME,
+ NULL,
+ (PBYTE)friendly_name,
+ friendly_name_max_length,
+ &friendly_name_actual_length);
+
+ if(got_friendly_name == TRUE && friendly_name_actual_length > 0)
+ friendly_name[friendly_name_actual_length-1] = '\0';
+ else
+ friendly_name[0] = '\0';
+
+ // Get hardware ID
+
+ TCHAR hardware_id[hardware_id_max_length];
+ DWORD hardware_id_actual_length = 0;
+
+ BOOL got_hardware_id = SetupDiGetDeviceRegistryProperty(
+ device_info_set,
+ &device_info_data,
+ SPDRP_HARDWAREID,
+ NULL,
+ (PBYTE)hardware_id,
+ hardware_id_max_length,
+ &hardware_id_actual_length);
+
+ if(got_hardware_id == TRUE && hardware_id_actual_length > 0)
+ hardware_id[hardware_id_actual_length-1] = '\0';
+ else
+ hardware_id[0] = '\0';
+
+ #ifdef UNICODE
+ std::string portName = utf8_encode(port_name);
+ std::string friendlyName = utf8_encode(friendly_name);
+ std::string hardwareId = utf8_encode(hardware_id);
+ #else
+ std::string portName = port_name;
+ std::string friendlyName = friendly_name;
+ std::string hardwareId = hardware_id;
+ #endif
+
+ PortInfo port_entry;
+ port_entry.port = portName;
+ port_entry.description = friendlyName;
+ port_entry.hardware_id = hardwareId;
+
+ devices_found.push_back(port_entry);
+ }
+
+ SetupDiDestroyDeviceInfoList(device_info_set);
+
+ return devices_found;
+}
+
+#endif // #if defined(_WIN32)