2 * This file is part of the libserialport project.
4 * Copyright (C) 2013-2014 Martin Ling <martin-libserialport@earth.li>
5 * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "libserialport.h"
22 #include "libserialport_internal.h"
24 /* USB path is a string of at most 8 decimal numbers < 128 separated by dots. */
25 #define MAX_USB_PATH ((8 * 3) + (7 * 1) + 1)
27 static void enumerate_hub(struct sp_port
*port
, const char *hub_name
,
28 const char *parent_path
);
30 static char *wc_to_utf8(PWCHAR wc_buffer
, ULONG size
)
32 WCHAR wc_str
[(size
/ sizeof(WCHAR
)) + 1];
35 /* Zero-terminate the wide char string. */
36 memcpy(wc_str
, wc_buffer
, size
);
37 wc_str
[sizeof(wc_str
) - 1] = 0;
39 /* Compute the size of the UTF-8 converted string. */
40 if (!(size
= WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wc_str
, -1,
41 NULL
, 0, NULL
, NULL
)))
44 /* Allocate UTF-8 output buffer. */
45 if (!(utf8_str
= malloc(size
)))
48 /* Actually converted to UTF-8. */
49 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wc_str
, -1,
50 utf8_str
, size
, NULL
, NULL
)) {
58 static char *get_root_hub_name(HANDLE host_controller
)
60 USB_ROOT_HUB_NAME root_hub_name
;
61 PUSB_ROOT_HUB_NAME root_hub_name_wc
;
62 char *root_hub_name_utf8
;
65 /* Compute the size of the root hub name string. */
66 if (!DeviceIoControl(host_controller
, IOCTL_USB_GET_ROOT_HUB_NAME
, 0, 0,
67 &root_hub_name
, sizeof(root_hub_name
), &size
, NULL
))
70 /* Allocate wide char root hub name string. */
71 size
= root_hub_name
.ActualLength
;
72 if (!(root_hub_name_wc
= malloc(size
)))
75 /* Actually get the root hub name string. */
76 if (!DeviceIoControl(host_controller
, IOCTL_USB_GET_ROOT_HUB_NAME
,
77 NULL
, 0, root_hub_name_wc
, size
, &size
, NULL
)) {
78 free(root_hub_name_wc
);
82 /* Convert the root hub name string to UTF-8. */
83 root_hub_name_utf8
= wc_to_utf8(root_hub_name_wc
->RootHubName
, size
);
84 free(root_hub_name_wc
);
85 return root_hub_name_utf8
;
88 static char *get_external_hub_name(HANDLE hub
, ULONG connection_index
)
90 USB_NODE_CONNECTION_NAME ext_hub_name
;
91 PUSB_NODE_CONNECTION_NAME ext_hub_name_wc
;
92 char *ext_hub_name_utf8
;
95 /* Compute the size of the external hub name string. */
96 ext_hub_name
.ConnectionIndex
= connection_index
;
97 if (!DeviceIoControl(hub
, IOCTL_USB_GET_NODE_CONNECTION_NAME
,
98 &ext_hub_name
, sizeof(ext_hub_name
),
99 &ext_hub_name
, sizeof(ext_hub_name
), &size
, NULL
))
102 /* Allocate wide char external hub name string. */
103 size
= ext_hub_name
.ActualLength
;
104 if (size
<= sizeof(ext_hub_name
)
105 || !(ext_hub_name_wc
= malloc(size
)))
108 /* Get the name of the external hub attached to the specified port. */
109 ext_hub_name_wc
->ConnectionIndex
= connection_index
;
110 if (!DeviceIoControl(hub
, IOCTL_USB_GET_NODE_CONNECTION_NAME
,
111 ext_hub_name_wc
, size
,
112 ext_hub_name_wc
, size
, &size
, NULL
)) {
113 free(ext_hub_name_wc
);
117 /* Convert the external hub name string to UTF-8. */
118 ext_hub_name_utf8
= wc_to_utf8(ext_hub_name_wc
->NodeName
, size
);
119 free(ext_hub_name_wc
);
120 return ext_hub_name_utf8
;
123 static char *get_string_descriptor(HANDLE hub_device
, ULONG connection_index
,
124 UCHAR descriptor_index
)
126 char desc_req_buf
[sizeof(USB_DESCRIPTOR_REQUEST
) +
127 MAXIMUM_USB_STRING_LENGTH
] = { 0 };
128 PUSB_DESCRIPTOR_REQUEST desc_req
= (void *)desc_req_buf
;
129 PUSB_STRING_DESCRIPTOR desc
= (void *)(desc_req
+ 1);
130 ULONG size
= sizeof(desc_req_buf
);
132 desc_req
->ConnectionIndex
= connection_index
;
133 desc_req
->SetupPacket
.wValue
= (USB_STRING_DESCRIPTOR_TYPE
<< 8)
135 desc_req
->SetupPacket
.wIndex
= 0;
136 desc_req
->SetupPacket
.wLength
= size
- sizeof(*desc_req
);
138 if (!DeviceIoControl(hub_device
,
139 IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
,
140 desc_req
, size
, desc_req
, size
, &size
, NULL
)
142 || desc
->bDescriptorType
!= USB_STRING_DESCRIPTOR_TYPE
143 || desc
->bLength
!= size
- sizeof(*desc_req
)
144 || desc
->bLength
% 2)
147 return wc_to_utf8(desc
->bString
, desc
->bLength
);
150 static void enumerate_hub_ports(struct sp_port
*port
, HANDLE hub_device
,
151 ULONG nb_ports
, const char *parent_path
)
153 char path
[MAX_USB_PATH
];
156 for (index
= 1; index
<= nb_ports
; index
++) {
157 PUSB_NODE_CONNECTION_INFORMATION_EX connection_info_ex
;
158 ULONG size
= sizeof(*connection_info_ex
) + (30 * sizeof(USB_PIPE_INFO
));
160 if (!(connection_info_ex
= malloc(size
)))
163 connection_info_ex
->ConnectionIndex
= index
;
164 if (!DeviceIoControl(hub_device
,
165 IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX
,
166 connection_info_ex
, size
,
167 connection_info_ex
, size
, &size
, NULL
)) {
169 * Try to get CONNECTION_INFORMATION if
170 * CONNECTION_INFORMATION_EX did not work.
172 PUSB_NODE_CONNECTION_INFORMATION connection_info
;
174 size
= sizeof(*connection_info
) + (30 * sizeof(USB_PIPE_INFO
));
175 if (!(connection_info
= malloc(size
))) {
176 free(connection_info_ex
);
179 connection_info
->ConnectionIndex
= index
;
180 if (!DeviceIoControl(hub_device
,
181 IOCTL_USB_GET_NODE_CONNECTION_INFORMATION
,
182 connection_info
, size
,
183 connection_info
, size
, &size
, NULL
)) {
184 free(connection_info
);
185 free(connection_info_ex
);
189 connection_info_ex
->ConnectionIndex
= connection_info
->ConnectionIndex
;
190 connection_info_ex
->DeviceDescriptor
= connection_info
->DeviceDescriptor
;
191 connection_info_ex
->DeviceIsHub
= connection_info
->DeviceIsHub
;
192 connection_info_ex
->DeviceAddress
= connection_info
->DeviceAddress
;
193 free(connection_info
);
196 if (connection_info_ex
->DeviceIsHub
) {
197 /* Recursively enumerate external hub. */
199 if ((ext_hub_name
= get_external_hub_name(hub_device
, index
))) {
200 snprintf(path
, sizeof(path
), "%s%ld.",
201 parent_path
, connection_info_ex
->ConnectionIndex
);
202 enumerate_hub(port
, ext_hub_name
, path
);
204 free(connection_info_ex
);
206 snprintf(path
, sizeof(path
), "%s%ld",
207 parent_path
, connection_info_ex
->ConnectionIndex
);
209 /* Check if this device is the one we search for. */
210 if (strcmp(path
, port
->usb_path
)) {
211 free(connection_info_ex
);
215 /* Finally grab detailed information regarding the device. */
216 port
->usb_address
= connection_info_ex
->DeviceAddress
+ 1;
217 port
->usb_vid
= connection_info_ex
->DeviceDescriptor
.idVendor
;
218 port
->usb_pid
= connection_info_ex
->DeviceDescriptor
.idProduct
;
220 if (connection_info_ex
->DeviceDescriptor
.iManufacturer
)
221 port
->usb_manufacturer
= get_string_descriptor(hub_device
,index
,
222 connection_info_ex
->DeviceDescriptor
.iManufacturer
);
223 if (connection_info_ex
->DeviceDescriptor
.iProduct
)
224 port
->usb_product
= get_string_descriptor(hub_device
, index
,
225 connection_info_ex
->DeviceDescriptor
.iProduct
);
226 if (connection_info_ex
->DeviceDescriptor
.iSerialNumber
)
227 port
->usb_serial
= get_string_descriptor(hub_device
, index
,
228 connection_info_ex
->DeviceDescriptor
.iSerialNumber
);
230 free(connection_info_ex
);
236 static void enumerate_hub(struct sp_port
*port
, const char *hub_name
,
237 const char *parent_path
)
239 USB_NODE_INFORMATION hub_info
;
241 ULONG size
= sizeof(hub_info
);
244 /* Open the hub with its full name. */
245 if (!(device_name
= malloc(strlen("\\\\.\\") + strlen(hub_name
) + 1)))
247 strcpy(device_name
, "\\\\.\\");
248 strcat(device_name
, hub_name
);
249 hub_device
= CreateFile(device_name
, GENERIC_WRITE
, FILE_SHARE_WRITE
,
250 NULL
, OPEN_EXISTING
, 0, NULL
);
252 if (hub_device
== INVALID_HANDLE_VALUE
)
255 /* Get the number of ports of the hub. */
256 if (DeviceIoControl(hub_device
, IOCTL_USB_GET_NODE_INFORMATION
,
257 &hub_info
, size
, &hub_info
, size
, &size
, NULL
))
258 /* Enumerate the ports of the hub. */
259 enumerate_hub_ports(port
, hub_device
,
260 hub_info
.u
.HubInformation
.HubDescriptor
.bNumberOfPorts
, parent_path
);
262 CloseHandle(hub_device
);
265 static void enumerate_host_controller(struct sp_port
*port
,
266 HANDLE host_controller_device
)
270 if ((root_hub_name
= get_root_hub_name(host_controller_device
))) {
271 enumerate_hub(port
, root_hub_name
, "");
276 static void get_usb_details(struct sp_port
*port
, DEVINST dev_inst_match
)
278 HDEVINFO device_info
;
279 SP_DEVINFO_DATA device_info_data
;
282 device_info
= SetupDiGetClassDevs(&GUID_CLASS_USB_HOST_CONTROLLER
, NULL
, NULL
,
283 DIGCF_PRESENT
| DIGCF_DEVICEINTERFACE
);
284 device_info_data
.cbSize
= sizeof(device_info_data
);
286 for (i
= 0; SetupDiEnumDeviceInfo(device_info
, i
, &device_info_data
); i
++) {
287 SP_DEVICE_INTERFACE_DATA device_interface_data
;
288 PSP_DEVICE_INTERFACE_DETAIL_DATA device_detail_data
;
289 DEVINST dev_inst
= dev_inst_match
;
290 HANDLE host_controller_device
;
292 device_interface_data
.cbSize
= sizeof(device_interface_data
);
293 if (!SetupDiEnumDeviceInterfaces(device_info
, 0,
294 &GUID_CLASS_USB_HOST_CONTROLLER
,
295 i
, &device_interface_data
))
298 if (!SetupDiGetDeviceInterfaceDetail(device_info
,&device_interface_data
,
299 NULL
, 0, &size
, NULL
)
300 && GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
303 if (!(device_detail_data
= malloc(size
)))
305 device_detail_data
->cbSize
= sizeof(*device_detail_data
);
306 if (!SetupDiGetDeviceInterfaceDetail(device_info
,&device_interface_data
,
307 device_detail_data
, size
, &size
,
309 free(device_detail_data
);
313 while (CM_Get_Parent(&dev_inst
, dev_inst
, 0) == CR_SUCCESS
314 && dev_inst
!= device_info_data
.DevInst
) { }
315 if (dev_inst
!= device_info_data
.DevInst
) {
316 free(device_detail_data
);
320 port
->usb_bus
= i
+ 1;
322 host_controller_device
= CreateFile(device_detail_data
->DevicePath
,
323 GENERIC_WRITE
, FILE_SHARE_WRITE
,
324 NULL
, OPEN_EXISTING
, 0, NULL
);
325 if (host_controller_device
!= INVALID_HANDLE_VALUE
) {
326 enumerate_host_controller(port
, host_controller_device
);
327 CloseHandle(host_controller_device
);
329 free(device_detail_data
);
332 SetupDiDestroyDeviceInfoList(device_info
);
336 SP_PRIV
enum sp_return
get_port_details(struct sp_port
*port
)
339 * Description limited to 127 char, anything longer
340 * would not be user friendly anyway.
342 char description
[128];
343 SP_DEVINFO_DATA device_info_data
= { .cbSize
= sizeof(device_info_data
) };
344 HDEVINFO device_info
;
347 device_info
= SetupDiGetClassDevs(NULL
, 0, 0,
348 DIGCF_PRESENT
| DIGCF_ALLCLASSES
);
349 if (device_info
== INVALID_HANDLE_VALUE
)
350 RETURN_FAIL("SetupDiGetClassDevs() failed");
352 for (i
= 0; SetupDiEnumDeviceInfo(device_info
, i
, &device_info_data
); i
++) {
355 char value
[8], class[16];
359 /* Check if this is the device we are looking for. */
360 device_key
= SetupDiOpenDevRegKey(device_info
, &device_info_data
,
362 DIREG_DEV
, KEY_QUERY_VALUE
);
363 if (device_key
== INVALID_HANDLE_VALUE
)
365 size
= sizeof(value
);
366 if (RegQueryValueExA(device_key
, "PortName", NULL
, &type
, (LPBYTE
)value
,
367 &size
) != ERROR_SUCCESS
|| type
!= REG_SZ
) {
368 RegCloseKey(device_key
);
371 RegCloseKey(device_key
);
372 value
[sizeof(value
)-1] = 0;
373 if (strcmp(value
, port
->name
))
376 /* Check port transport type. */
377 dev_inst
= device_info_data
.DevInst
;
378 size
= sizeof(class);
380 while (CM_Get_Parent(&dev_inst
, dev_inst
, 0) == CR_SUCCESS
&&
381 (cr
= CM_Get_DevNode_Registry_PropertyA(dev_inst
,
382 CM_DRP_CLASS
, 0, class, &size
, 0)) != CR_SUCCESS
) { }
383 if (cr
== CR_SUCCESS
) {
384 if (!strcmp(class, "USB"))
385 port
->transport
= SP_TRANSPORT_USB
;
388 /* Get port description (friendly name). */
389 dev_inst
= device_info_data
.DevInst
;
390 size
= sizeof(description
);
391 while ((cr
= CM_Get_DevNode_Registry_PropertyA(dev_inst
,
392 CM_DRP_FRIENDLYNAME
, 0, description
, &size
, 0)) != CR_SUCCESS
393 && CM_Get_Parent(&dev_inst
, dev_inst
, 0) == CR_SUCCESS
) { }
394 if (cr
== CR_SUCCESS
)
395 port
->description
= strdup(description
);
397 /* Get more informations for USB connected ports. */
398 if (port
->transport
== SP_TRANSPORT_USB
) {
399 char usb_path
[MAX_USB_PATH
] = "", tmp
[MAX_USB_PATH
];
400 char device_id
[MAX_DEVICE_ID_LEN
];
402 /* Recurse over parents to build the USB device path. */
403 dev_inst
= device_info_data
.DevInst
;
405 /* Verify that this layer of the tree is USB related. */
406 if (CM_Get_Device_IDA(dev_inst
, device_id
,
407 sizeof(device_id
), 0) != CR_SUCCESS
408 || strncmp(device_id
, "USB\\", 4))
411 /* Discard one layer for composite devices. */
412 char compat_ids
[512], *p
= compat_ids
;
413 size
= sizeof(compat_ids
);
414 if (CM_Get_DevNode_Registry_PropertyA(dev_inst
,
415 CM_DRP_COMPATIBLEIDS
, 0,
417 &size
, 0) == CR_SUCCESS
) {
419 if (!strncmp(p
, "USB\\COMPOSITE", 13))
427 /* Stop the recursion when reaching the USB root. */
428 if (!strncmp(device_id
, "USB\\ROOT", 8))
431 /* Prepend the address of current USB layer to the USB path. */
433 size
= sizeof(address
);
434 if (CM_Get_DevNode_Registry_PropertyA(dev_inst
, CM_DRP_ADDRESS
,
435 0, &address
, &size
, 0) == CR_SUCCESS
) {
436 strcpy(tmp
, usb_path
);
437 snprintf(usb_path
, sizeof(usb_path
), "%d%s%s",
438 (int)address
, *tmp
? "." : "", tmp
);
440 } while (CM_Get_Parent(&dev_inst
, dev_inst
, 0) == CR_SUCCESS
);
442 port
->usb_path
= strdup(usb_path
);
444 /* Wake up the USB device to be able to read string descriptor. */
445 char *escaped_port_name
;
447 if (!(escaped_port_name
= malloc(strlen(port
->name
) + 5)))
448 RETURN_ERROR(SP_ERR_MEM
, "Escaped port name malloc failed");
449 sprintf(escaped_port_name
, "\\\\.\\%s", port
->name
);
450 handle
= CreateFile(escaped_port_name
, GENERIC_READ
, 0, 0,
452 FILE_ATTRIBUTE_NORMAL
|FILE_FLAG_OVERLAPPED
, 0);
453 free(escaped_port_name
);
456 /* Retrieve USB device details from the device descriptor. */
457 get_usb_details(port
, device_info_data
.DevInst
);
462 SetupDiDestroyDeviceInfoList(device_info
);
467 SP_PRIV
enum sp_return
list_ports(struct sp_port
***list
)
471 DWORD max_value_len
, max_data_size
, max_data_len
;
472 DWORD value_len
, data_size
, data_len
;
473 DWORD type
, index
= 0;
478 DEBUG("Opening registry key");
479 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"),
480 0, KEY_QUERY_VALUE
, &key
) != ERROR_SUCCESS
) {
481 SET_FAIL(ret
, "RegOpenKeyEx() failed");
484 DEBUG("Querying registry key value and data sizes");
485 if (RegQueryInfoKey(key
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
486 &max_value_len
, &max_data_size
, NULL
, NULL
) != ERROR_SUCCESS
) {
487 SET_FAIL(ret
, "RegQueryInfoKey() failed");
490 max_data_len
= max_data_size
/ sizeof(TCHAR
);
491 if (!(value
= malloc((max_value_len
+ 1) * sizeof(TCHAR
)))) {
492 SET_ERROR(ret
, SP_ERR_MEM
, "Registry value malloc failed");
495 if (!(data
= malloc((max_data_len
+ 1) * sizeof(TCHAR
)))) {
496 SET_ERROR(ret
, SP_ERR_MEM
, "Registry data malloc failed");
499 DEBUG("Iterating over values");
501 value_len
= max_value_len
+ 1,
502 data_size
= max_data_size
,
503 RegEnumValue(key
, index
, value
, &value_len
,
504 NULL
, &type
, (LPBYTE
)data
, &data_size
) == ERROR_SUCCESS
)
506 if (type
== REG_SZ
) {
507 data_len
= data_size
/ sizeof(TCHAR
);
508 data
[data_len
] = '\0';
510 name_len
= WideCharToMultiByte(CP_ACP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
512 name_len
= data_len
+ 1;
514 if (!(name
= malloc(name_len
))) {
515 SET_ERROR(ret
, SP_ERR_MEM
, "Registry port name malloc failed");
519 WideCharToMultiByte(CP_ACP
, 0, data
, -1, name
, name_len
, NULL
, NULL
);
523 DEBUG_FMT("Found port %s", name
);
524 if (!(*list
= list_append(*list
, name
))) {
525 SET_ERROR(ret
, SP_ERR_MEM
, "List append failed");