diff --git a/freebsd.c b/freebsd.c index b5cd91a..e2b6c59 100644 --- a/freebsd.c +++ b/freebsd.c @@ -234,7 +234,7 @@ static int populate_port_struct_from_libusb_desc(struct sp_port *const port, return 0; } -SP_PRIV enum sp_return get_port_details(struct sp_port *port) +SP_PRIV enum sp_return get_port_details(struct sp_port *port, bool fetchDescriptors) { int rc; struct libusb20_backend *be; diff --git a/libserialport_internal.h b/libserialport_internal.h index 669152b..cf256bd 100644 --- a/libserialport_internal.h +++ b/libserialport_internal.h @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef _WIN32 #include #include @@ -65,6 +66,9 @@ #endif #ifdef __linux__ #include +#ifdef __amd64__ +__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); +#endif #ifndef __ANDROID__ #include "linux/serial.h" #endif @@ -112,6 +116,7 @@ struct sp_port { BYTE pending_byte; BOOL writing; BOOL wait_running; + BOOL composite; #else int fd; #endif @@ -231,7 +236,7 @@ extern void (*sp_debug_handler)(const char *format, ...); SP_PRIV struct sp_port **list_append(struct sp_port **list, const char *portname); /* OS-specific Helper functions. */ -SP_PRIV enum sp_return get_port_details(struct sp_port *port); +SP_PRIV enum sp_return get_port_details(struct sp_port *port, bool fetchDescriptors); SP_PRIV enum sp_return list_ports(struct sp_port ***list); #endif diff --git a/linux.c b/linux.c index 9016489..c081072 100644 --- a/linux.c +++ b/linux.c @@ -22,7 +22,7 @@ #include "libserialport.h" #include "libserialport_internal.h" -SP_PRIV enum sp_return get_port_details(struct sp_port *port) +SP_PRIV enum sp_return get_port_details(struct sp_port *port, bool fetchDescriptors) { /* * Description limited to 127 char, anything longer @@ -31,7 +31,7 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) char description[128]; int bus, address; unsigned int vid, pid; - char manufacturer[128], product[128], serial[128]; + char manufacturer[128], product[128], serial[128], interface[128]; char baddr[32]; const char dir_name[] = "/sys/class/tty/%s/device/%s%s"; char sub_dir[32] = "", file_name[PATH_MAX]; @@ -40,6 +40,8 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) int i, count; struct stat statbuf; + interface[0] = '\0'; + if (strncmp(port->name, "/dev/", 5)) RETURN_ERROR(SP_ERR_ARG, "Device name not recognized"); @@ -59,6 +61,18 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) if (port->transport == SP_TRANSPORT_USB) { for (i = 0; i < 5; i++) { + + snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "interface"); + if ((file = fopen(file_name, "r"))) { + if ((ptr = fgets(interface, sizeof(interface), file))) { + ptr = interface + strlen(interface) - 1; + if (ptr >= interface && *ptr == '\n') + *ptr = 0; + port->description = strdup(interface); + } + fclose(file); + } + strcat(sub_dir, "../"); snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "busnum"); @@ -145,9 +159,9 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) } /* If present, add serial to description for better identification. */ - if (port->usb_serial && strlen(port->usb_serial)) { + if (strlen(interface) > 0) { snprintf(description, sizeof(description), - "%s - %s", port->description, port->usb_serial); + "%s - %s", port->description, interface); if (port->description) free(port->description); port->description = strdup(description); diff --git a/macosx.c b/macosx.c index f5f1944..2a063f7 100644 --- a/macosx.c +++ b/macosx.c @@ -22,7 +22,7 @@ #include "libserialport.h" #include "libserialport_internal.h" -SP_PRIV enum sp_return get_port_details(struct sp_port *port) +SP_PRIV enum sp_return get_port_details(struct sp_port *port, bool fetchDescriptors) { /* * Description limited to 127 char, anything longer @@ -56,13 +56,24 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) result = CFStringGetCString(cf_property, path, sizeof(path), kCFStringEncodingASCII); CFRelease(cf_property); - if (!result || strcmp(path, port->name)) { + if (!result || strcmp(path, port->name) || strstr(path, "wwan")) { IOObjectRelease(ioport); continue; } DEBUG_FMT("Found port %s", path); IORegistryEntryGetParentEntry(ioport, kIOServicePlane, &ioparent); + if ((cf_property=IORegistryEntrySearchCFProperty(ioparent,kIOServicePlane, + CFSTR("IOClass"), kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents))) { + if (CFStringGetCString(cf_property, class, sizeof(class), + kCFStringEncodingASCII) && + strstr(class, "USB")) { + DEBUG("Found USB class device"); + port->transport = SP_TRANSPORT_USB; + } + CFRelease(cf_property); + } if ((cf_property=IORegistryEntrySearchCFProperty(ioparent,kIOServicePlane, CFSTR("IOProviderClass"), kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents))) { @@ -96,7 +107,6 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) } else { DEBUG("No description for this device"); } - cf_bus = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane, CFSTR("USBBusNumber"), kCFAllocatorDefault, @@ -107,9 +117,9 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); - if (cf_bus && cf_address && - CFNumberGetValue(cf_bus , kCFNumberIntType, &bus) && - CFNumberGetValue(cf_address, kCFNumberIntType, &address)) { + if (cf_bus && cf_address) { + CFNumberGetValue(cf_bus , kCFNumberIntType, &bus); + CFNumberGetValue(cf_address, kCFNumberIntType, &address); DEBUG_FMT("Found matching USB bus:address %03d:%03d", bus, address); port->usb_bus = bus; port->usb_address = address; @@ -141,37 +151,40 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) if (cf_product) CFRelease(cf_product); - if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane, - CFSTR("USB Vendor Name"), kCFAllocatorDefault, - kIORegistryIterateRecursively | kIORegistryIterateParents))) { - if (CFStringGetCString(cf_property, manufacturer, sizeof(manufacturer), - kCFStringEncodingASCII)) { - DEBUG_FMT("Found manufacturer %s", manufacturer); - port->usb_manufacturer = strdup(manufacturer); + if (fetchDescriptors == true) { + + if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane, + CFSTR("USB Vendor Name"), kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents))) { + if (CFStringGetCString(cf_property, manufacturer, sizeof(manufacturer), + kCFStringEncodingASCII)) { + DEBUG_FMT("Found manufacturer %s", manufacturer); + port->usb_manufacturer = strdup(manufacturer); + } + CFRelease(cf_property); } - CFRelease(cf_property); - } - if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane, - CFSTR("USB Product Name"), kCFAllocatorDefault, - kIORegistryIterateRecursively | kIORegistryIterateParents))) { - if (CFStringGetCString(cf_property, product, sizeof(product), - kCFStringEncodingASCII)) { - DEBUG_FMT("Found product name %s", product); - port->usb_product = strdup(product); + if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane, + CFSTR("USB Product Name"), kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents))) { + if (CFStringGetCString(cf_property, product, sizeof(product), + kCFStringEncodingASCII)) { + DEBUG_FMT("Found product name %s", product); + port->usb_product = strdup(product); + } + CFRelease(cf_property); } - CFRelease(cf_property); - } - if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane, - CFSTR("USB Serial Number"), kCFAllocatorDefault, - kIORegistryIterateRecursively | kIORegistryIterateParents))) { - if (CFStringGetCString(cf_property, serial, sizeof(serial), - kCFStringEncodingASCII)) { - DEBUG_FMT("Found serial number %s", serial); - port->usb_serial = strdup(serial); + if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane, + CFSTR("USB Serial Number"), kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents))) { + if (CFStringGetCString(cf_property, serial, sizeof(serial), + kCFStringEncodingASCII)) { + DEBUG_FMT("Found serial number %s", serial); + port->usb_serial = strdup(serial); + } + CFRelease(cf_property); } - CFRelease(cf_property); } IOObjectRelease(ioport); diff --git a/serialport.c b/serialport.c index 520d27e..ad03249 100644 --- a/serialport.c +++ b/serialport.c @@ -55,7 +55,7 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data, static enum sp_return set_config(struct sp_port *port, struct port_data *data, const struct sp_port_config *config); -SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr) +static enum sp_return sp_get_port_by_name_desc(const char *portname, struct sp_port **port_ptr, bool fetchDescriptors) { struct sp_port *port; #ifndef NO_PORT_METADATA @@ -106,7 +106,7 @@ SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port * port->bluetooth_address = NULL; #ifndef NO_PORT_METADATA - if ((ret = get_port_details(port)) != SP_OK) { + if ((ret = get_port_details(port, fetchDescriptors)) != SP_OK) { sp_free_port(port); return ret; } @@ -117,6 +117,11 @@ SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port * RETURN_OK(); } +SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr) +{ + return sp_get_port_by_name_desc(portname, port_ptr, true); +} + SP_API char *sp_get_port_name(const struct sp_port *port) { TRACE("%p", port); @@ -313,7 +318,7 @@ SP_PRIV struct sp_port **list_append(struct sp_port **list, if (!(tmp = realloc(list, sizeof(struct sp_port *) * (count + 2)))) goto fail; list = tmp; - if (sp_get_port_by_name(portname, &list[count]) != SP_OK) + if (sp_get_port_by_name_desc(portname, &list[count], false) != SP_OK) goto fail; list[count + 1] = NULL; return list; @@ -389,12 +394,12 @@ SP_API void sp_free_port_list(struct sp_port **list) #ifdef _WIN32 #define CHECK_PORT_HANDLE() do { \ if (port->hdl == INVALID_HANDLE_VALUE) \ - RETURN_ERROR(SP_ERR_ARG, "Invalid port handle"); \ + RETURN_ERROR(SP_ERR_ARG, "Port not open"); \ } while (0) #else #define CHECK_PORT_HANDLE() do { \ if (port->fd < 0) \ - RETURN_ERROR(SP_ERR_ARG, "Invalid port fd"); \ + RETURN_ERROR(SP_ERR_ARG, "Port not open"); \ } while (0) #endif #define CHECK_OPEN_PORT() do { \ diff --git a/windows.c b/windows.c index b411918..006dee8 100644 --- a/windows.c +++ b/windows.c @@ -26,34 +26,14 @@ #define MAX_USB_PATH ((8 * 3) + (7 * 1) + 1) static void enumerate_hub(struct sp_port *port, const char *hub_name, - const char *parent_path); - -static char *wc_to_utf8(PWCHAR wc_buffer, ULONG size) -{ - WCHAR wc_str[(size / sizeof(WCHAR)) + 1]; - char *utf8_str; - - /* Zero-terminate the wide char string. */ - memcpy(wc_str, wc_buffer, size); - wc_str[sizeof(wc_str) - 1] = 0; - - /* Compute the size of the UTF-8 converted string. */ - if (!(size = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wc_str, -1, - NULL, 0, NULL, NULL))) - return NULL; - - /* Allocate UTF-8 output buffer. */ - if (!(utf8_str = malloc(size))) - return NULL; - - /* Actually converted to UTF-8. */ - if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wc_str, -1, - utf8_str, size, NULL, NULL)) { - free(utf8_str); - return NULL; - } - - return utf8_str; + const char *parent_path, DEVINST dev_inst, bool fetchDescriptors); + +static char* wc_to_utf8(const wchar_t* wc, ULONG size) { + int ulen = WideCharToMultiByte(CP_UTF8, 0, wc, -1, NULL, 0, NULL, NULL); + char * ubuf = malloc(ulen + 1); + WideCharToMultiByte(CP_UTF8, 0, wc, -1, ubuf, ulen, NULL, NULL); + ubuf[ulen] = 0; + return ubuf; } static char *get_root_hub_name(HANDLE host_controller) @@ -149,7 +129,7 @@ static char *get_string_descriptor(HANDLE hub_device, ULONG connection_index, } static void enumerate_hub_ports(struct sp_port *port, HANDLE hub_device, - ULONG nb_ports, const char *parent_path) + ULONG nb_ports, const char *parent_path, DEVINST dev_inst, bool fetchDescriptors) { char path[MAX_USB_PATH]; ULONG index = 0; @@ -200,7 +180,7 @@ static void enumerate_hub_ports(struct sp_port *port, HANDLE hub_device, if ((ext_hub_name = get_external_hub_name(hub_device, index))) { snprintf(path, sizeof(path), "%s%ld.", parent_path, connection_info_ex->ConnectionIndex); - enumerate_hub(port, ext_hub_name, path); + enumerate_hub(port, ext_hub_name, path, dev_inst, fetchDescriptors); } free(connection_info_ex); } else { @@ -218,16 +198,32 @@ static void enumerate_hub_ports(struct sp_port *port, HANDLE hub_device, port->usb_vid = connection_info_ex->DeviceDescriptor.idVendor; port->usb_pid = connection_info_ex->DeviceDescriptor.idProduct; - if (connection_info_ex->DeviceDescriptor.iManufacturer) - port->usb_manufacturer = get_string_descriptor(hub_device,index, - connection_info_ex->DeviceDescriptor.iManufacturer); - if (connection_info_ex->DeviceDescriptor.iProduct) - port->usb_product = get_string_descriptor(hub_device, index, - connection_info_ex->DeviceDescriptor.iProduct); - if (connection_info_ex->DeviceDescriptor.iSerialNumber) - port->usb_serial = get_string_descriptor(hub_device, index, - connection_info_ex->DeviceDescriptor.iSerialNumber); + if (fetchDescriptors) { + if (connection_info_ex->DeviceDescriptor.iManufacturer) { + port->usb_manufacturer = get_string_descriptor(hub_device,index, + connection_info_ex->DeviceDescriptor.iManufacturer); + } + if (connection_info_ex->DeviceDescriptor.iProduct) { + port->usb_product = get_string_descriptor(hub_device, index, + connection_info_ex->DeviceDescriptor.iProduct); + } + if (connection_info_ex->DeviceDescriptor.iSerialNumber) { + port->usb_serial = get_string_descriptor(hub_device, index, + connection_info_ex->DeviceDescriptor.iSerialNumber); + if (port->usb_serial == NULL && port->composite) { + //composite device, get the parent's serial number + char device_id[MAX_DEVICE_ID_LEN]; + if (CM_Get_Parent(&dev_inst, dev_inst, 0) == CR_SUCCESS) { + if (CM_Get_Device_IDA(dev_inst, device_id, sizeof(device_id), 0) == CR_SUCCESS) { + if (strlen(device_id) > 0) { + port->usb_serial = strdup(strrchr(device_id, '\\')+1); + } + } + } + } + } + } free(connection_info_ex); break; } @@ -235,7 +231,7 @@ static void enumerate_hub_ports(struct sp_port *port, HANDLE hub_device, } static void enumerate_hub(struct sp_port *port, const char *hub_name, - const char *parent_path) + const char *parent_path, DEVINST dev_inst, bool fetchDescriptors) { USB_NODE_INFORMATION hub_info; HANDLE hub_device; @@ -258,23 +254,33 @@ static void enumerate_hub(struct sp_port *port, const char *hub_name, &hub_info, size, &hub_info, size, &size, NULL)) /* Enumerate the ports of the hub. */ enumerate_hub_ports(port, hub_device, - hub_info.u.HubInformation.HubDescriptor.bNumberOfPorts, parent_path); + hub_info.u.HubInformation.HubDescriptor.bNumberOfPorts, parent_path, dev_inst, fetchDescriptors); CloseHandle(hub_device); } static void enumerate_host_controller(struct sp_port *port, - HANDLE host_controller_device) + HANDLE host_controller_device, + DEVINST dev_inst, bool fetchDescriptors) { char *root_hub_name; + if (port->composite) { + //remove last part of the path + char * pch; + pch=strrchr(port->usb_path,'.'); + if (pch != NULL) { + port->usb_path[pch-port->usb_path] = '\0'; + } + } + if ((root_hub_name = get_root_hub_name(host_controller_device))) { - enumerate_hub(port, root_hub_name, ""); + enumerate_hub(port, root_hub_name, "", dev_inst, fetchDescriptors); free(root_hub_name); } } -static void get_usb_details(struct sp_port *port, DEVINST dev_inst_match) +static void get_usb_details(struct sp_port *port, DEVINST dev_inst_match, bool fetchDescriptors) { HDEVINFO device_info; SP_DEVINFO_DATA device_info_data; @@ -324,7 +330,7 @@ static void get_usb_details(struct sp_port *port, DEVINST dev_inst_match) GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (host_controller_device != INVALID_HANDLE_VALUE) { - enumerate_host_controller(port, host_controller_device); + enumerate_host_controller(port, host_controller_device, dev_inst_match, fetchDescriptors); CloseHandle(host_controller_device); } free(device_detail_data); @@ -334,7 +340,7 @@ static void get_usb_details(struct sp_port *port, DEVINST dev_inst_match) return; } -SP_PRIV enum sp_return get_port_details(struct sp_port *port) +SP_PRIV enum sp_return get_port_details(struct sp_port *port, bool fetchDescriptors) { /* * Description limited to 127 char, anything longer @@ -356,6 +362,7 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) char value[8], class[16]; DWORD size, type; CONFIGRET cr; + port->composite = FALSE; /* Check if this is the device we are looking for. */ device_key = SetupDiOpenDevRegKey(device_info, &device_info_data, @@ -417,12 +424,12 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) &compat_ids, &size, 0) == CR_SUCCESS) { while (*p) { - if (!strncmp(p, "USB\\COMPOSITE", 13)) + if (!strncmp(p, "USB\\COMPOSITE", 13)) { + port->composite = TRUE; break; + } p += strlen(p) + 1; } - if (*p) - continue; } /* Stop the recursion when reaching the USB root. */ @@ -442,20 +449,8 @@ SP_PRIV enum sp_return get_port_details(struct sp_port *port) port->usb_path = strdup(usb_path); - /* Wake up the USB device to be able to read string descriptor. */ - char *escaped_port_name; - HANDLE handle; - if (!(escaped_port_name = malloc(strlen(port->name) + 5))) - RETURN_ERROR(SP_ERR_MEM, "Escaped port name malloc failed"); - sprintf(escaped_port_name, "\\\\.\\%s", port->name); - handle = CreateFile(escaped_port_name, GENERIC_READ, 0, 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 0); - free(escaped_port_name); - CloseHandle(handle); - /* Retrieve USB device details from the device descriptor. */ - get_usb_details(port, device_info_data.DevInst); + get_usb_details(port, device_info_data.DevInst, fetchDescriptors); } break; }