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
, char *hub_name
,
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 utf8 converted string */
40 if (!(size
= WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wc_str
, -1,
41 NULL
, 0, NULL
, NULL
)))
44 /* allocate utf8 output buffer */
45 if (!(utf8_str
= malloc(size
)))
48 /* actually converted to utf8 */
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 utf8 */
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 utf8 */
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
, 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
)) {
168 /* try to get CONNECTION_INFORMATION if CONNECTION_INFORMATION_EX
170 PUSB_NODE_CONNECTION_INFORMATION connection_info
;
172 size
= sizeof(*connection_info
) + 30*sizeof(USB_PIPE_INFO
);
173 if (!(connection_info
= malloc(size
))) {
174 free(connection_info_ex
);
177 connection_info
->ConnectionIndex
= index
;
178 if (!DeviceIoControl(hub_device
,
179 IOCTL_USB_GET_NODE_CONNECTION_INFORMATION
,
180 connection_info
, size
,
181 connection_info
, size
, &size
, NULL
)) {
182 free(connection_info
);
183 free(connection_info_ex
);
187 connection_info_ex
->ConnectionIndex
= connection_info
->ConnectionIndex
;
188 connection_info_ex
->DeviceDescriptor
= connection_info
->DeviceDescriptor
;
189 connection_info_ex
->DeviceIsHub
= connection_info
->DeviceIsHub
;
190 connection_info_ex
->DeviceAddress
= connection_info
->DeviceAddress
;
191 free(connection_info
);
194 if (connection_info_ex
->DeviceIsHub
) {
195 /* recursively enumerate external hub */
197 if ((ext_hub_name
= get_external_hub_name(hub_device
, index
))) {
198 snprintf(path
, sizeof(path
), "%s%ld.",
199 parent_path
, connection_info_ex
->ConnectionIndex
);
200 enumerate_hub(port
, ext_hub_name
, path
);
202 free(connection_info_ex
);
204 snprintf(path
, sizeof(path
), "%s%ld",
205 parent_path
, connection_info_ex
->ConnectionIndex
);
207 /* check if this device is the one we search for */
208 if (strcmp(path
, port
->usb_path
)) {
209 free(connection_info_ex
);
213 /* finally grab detailed informations regarding the device */
214 port
->usb_address
= connection_info_ex
->DeviceAddress
+ 1;
215 port
->usb_vid
= connection_info_ex
->DeviceDescriptor
.idVendor
;
216 port
->usb_pid
= connection_info_ex
->DeviceDescriptor
.idProduct
;
218 if (connection_info_ex
->DeviceDescriptor
.iManufacturer
)
219 port
->usb_manufacturer
= get_string_descriptor(hub_device
,index
,
220 connection_info_ex
->DeviceDescriptor
.iManufacturer
);
221 if (connection_info_ex
->DeviceDescriptor
.iProduct
)
222 port
->usb_product
= get_string_descriptor(hub_device
, index
,
223 connection_info_ex
->DeviceDescriptor
.iProduct
);
224 if (connection_info_ex
->DeviceDescriptor
.iSerialNumber
)
225 port
->usb_serial
= get_string_descriptor(hub_device
, index
,
226 connection_info_ex
->DeviceDescriptor
.iSerialNumber
);
228 free(connection_info_ex
);
234 static void enumerate_hub(struct sp_port
*port
, char *hub_name
,
237 USB_NODE_INFORMATION hub_info
;
239 ULONG size
= sizeof(hub_info
);
242 /* open the hub with its full name */
243 if (!(device_name
= malloc(strlen("\\\\.\\") + strlen(hub_name
) + 1)))
245 strcpy(device_name
, "\\\\.\\");
246 strcat(device_name
, hub_name
);
247 hub_device
= CreateFile(device_name
, GENERIC_WRITE
, FILE_SHARE_WRITE
,
248 NULL
, OPEN_EXISTING
, 0, NULL
);
250 if (hub_device
== INVALID_HANDLE_VALUE
)
253 /* get the number of ports of the hub */
254 if (DeviceIoControl(hub_device
, IOCTL_USB_GET_NODE_INFORMATION
,
255 &hub_info
, size
, &hub_info
, size
, &size
, NULL
))
256 /* enumerate the ports of the hub */
257 enumerate_hub_ports(port
, hub_device
,
258 hub_info
.u
.HubInformation
.HubDescriptor
.bNumberOfPorts
, parent_path
);
260 CloseHandle(hub_device
);
263 static void enumerate_host_controller(struct sp_port
*port
,
264 HANDLE host_controller_device
)
268 if ((root_hub_name
= get_root_hub_name(host_controller_device
))) {
269 enumerate_hub(port
, root_hub_name
, "");
274 static void get_usb_details(struct sp_port
*port
, DEVINST dev_inst_match
)
276 HDEVINFO device_info
;
277 SP_DEVINFO_DATA device_info_data
;
280 device_info
= SetupDiGetClassDevs(&GUID_CLASS_USB_HOST_CONTROLLER
,NULL
,NULL
,
281 DIGCF_PRESENT
| DIGCF_DEVICEINTERFACE
);
282 device_info_data
.cbSize
= sizeof(device_info_data
);
284 for (i
=0; SetupDiEnumDeviceInfo(device_info
, i
, &device_info_data
); i
++) {
285 SP_DEVICE_INTERFACE_DATA device_interface_data
;
286 PSP_DEVICE_INTERFACE_DETAIL_DATA device_detail_data
;
287 DEVINST dev_inst
= dev_inst_match
;
288 HANDLE host_controller_device
;
290 device_interface_data
.cbSize
= sizeof(device_interface_data
);
291 if (!SetupDiEnumDeviceInterfaces(device_info
, 0,
292 &GUID_CLASS_USB_HOST_CONTROLLER
,
293 i
, &device_interface_data
))
296 if (!SetupDiGetDeviceInterfaceDetail(device_info
,&device_interface_data
,
297 NULL
, 0, &size
, NULL
)
298 && GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
301 if (!(device_detail_data
= malloc(size
)))
303 device_detail_data
->cbSize
= sizeof(*device_detail_data
);
304 if (!SetupDiGetDeviceInterfaceDetail(device_info
,&device_interface_data
,
305 device_detail_data
, size
, &size
,
307 free(device_detail_data
);
311 while (CM_Get_Parent(&dev_inst
, dev_inst
, 0) == CR_SUCCESS
312 && dev_inst
!= device_info_data
.DevInst
) { }
313 if (dev_inst
!= device_info_data
.DevInst
) {
314 free(device_detail_data
);
318 port
->usb_bus
= i
+ 1;
320 host_controller_device
= CreateFile(device_detail_data
->DevicePath
,
321 GENERIC_WRITE
, FILE_SHARE_WRITE
,
322 NULL
, OPEN_EXISTING
, 0, NULL
);
323 if (host_controller_device
!= INVALID_HANDLE_VALUE
) {
324 enumerate_host_controller(port
, host_controller_device
);
325 CloseHandle(host_controller_device
);
327 free(device_detail_data
);
330 SetupDiDestroyDeviceInfoList(device_info
);
334 SP_PRIV
enum sp_return
get_port_details(struct sp_port
*port
)
336 /* Description limited to 127 char,
337 anything longer would not be user friendly anyway */
338 char description
[128];
339 SP_DEVINFO_DATA device_info_data
= { .cbSize
= sizeof(device_info_data
) };
340 HDEVINFO device_info
;
343 device_info
= SetupDiGetClassDevs(NULL
, 0, 0,
344 DIGCF_PRESENT
| DIGCF_ALLCLASSES
);
345 if (device_info
== INVALID_HANDLE_VALUE
)
346 RETURN_FAIL("SetupDiGetClassDevs() failed");
348 for (i
=0; SetupDiEnumDeviceInfo(device_info
, i
, &device_info_data
); i
++) {
351 char value
[8], class[16];
355 /* check if this is the device we are looking for */
356 device_key
= SetupDiOpenDevRegKey(device_info
, &device_info_data
,
358 DIREG_DEV
, KEY_QUERY_VALUE
);
359 if (device_key
== INVALID_HANDLE_VALUE
)
361 size
= sizeof(value
);
362 if (RegQueryValueExA(device_key
, "PortName", NULL
, &type
, (LPBYTE
)value
,
363 &size
) != ERROR_SUCCESS
|| type
!= REG_SZ
) {
364 RegCloseKey(device_key
);
367 RegCloseKey(device_key
);
368 value
[sizeof(value
)-1] = 0;
369 if (strcmp(value
, port
->name
))
372 /* check port transport type */
373 dev_inst
= device_info_data
.DevInst
;
374 size
= sizeof(class);
376 while (CM_Get_Parent(&dev_inst
, dev_inst
, 0) == CR_SUCCESS
&&
377 (cr
= CM_Get_DevNode_Registry_PropertyA(dev_inst
,
378 CM_DRP_CLASS
, 0, class, &size
, 0)) != CR_SUCCESS
) { }
379 if (cr
== CR_SUCCESS
) {
380 if (!strcmp(class, "USB"))
381 port
->transport
= SP_TRANSPORT_USB
;
384 /* get port description (friendly name) */
385 dev_inst
= device_info_data
.DevInst
;
386 size
= sizeof(description
);
387 while ((cr
= CM_Get_DevNode_Registry_PropertyA(dev_inst
,
388 CM_DRP_FRIENDLYNAME
, 0, description
, &size
, 0)) != CR_SUCCESS
389 && CM_Get_Parent(&dev_inst
, dev_inst
, 0) == CR_SUCCESS
) { }
390 if (cr
== CR_SUCCESS
)
391 port
->description
= strdup(description
);
393 /* get more informations for USB connected ports */
394 if (port
->transport
== SP_TRANSPORT_USB
) {
395 char usb_path
[MAX_USB_PATH
] = "", tmp
[MAX_USB_PATH
];
396 char device_id
[MAX_DEVICE_ID_LEN
];
398 /* recurse over parents to build the USB device path */
399 dev_inst
= device_info_data
.DevInst
;
401 /* verify that this layer of the tree is USB related */
402 if (CM_Get_Device_IDA(dev_inst
, device_id
,
403 sizeof(device_id
), 0) != CR_SUCCESS
404 || strncmp(device_id
, "USB\\", 4))
407 /* discard one layer for composite devices */
408 char compat_ids
[512], *p
= compat_ids
;
409 size
= sizeof(compat_ids
);
410 if (CM_Get_DevNode_Registry_PropertyA(dev_inst
,
411 CM_DRP_COMPATIBLEIDS
, 0,
413 &size
, 0) == CR_SUCCESS
) {
415 if (!strncmp(p
, "USB\\COMPOSITE", 13))
423 /* stop the recursion when reaching the USB root */
424 if (!strncmp(device_id
, "USB\\ROOT", 8))
427 /* prepend the address of current USB layer to the USB path */
429 size
= sizeof(address
);
430 if (CM_Get_DevNode_Registry_PropertyA(dev_inst
, CM_DRP_ADDRESS
,
431 0, &address
, &size
, 0) == CR_SUCCESS
) {
432 strcpy(tmp
, usb_path
);
433 snprintf(usb_path
, sizeof(usb_path
), "%d%s%s",
434 (int)address
, *tmp
? "." : "", tmp
);
436 } while (CM_Get_Parent(&dev_inst
, dev_inst
, 0) == CR_SUCCESS
);
438 port
->usb_path
= strdup(usb_path
);
440 /* wake up the USB device to be able to read string descriptor */
441 char *escaped_port_name
;
443 if (!(escaped_port_name
= malloc(strlen(port
->name
) + 5)))
444 RETURN_ERROR(SP_ERR_MEM
, "Escaped port name malloc failed");
445 sprintf(escaped_port_name
, "\\\\.\\%s", port
->name
);
446 handle
= CreateFile(escaped_port_name
, GENERIC_READ
, 0, 0,
448 FILE_ATTRIBUTE_NORMAL
|FILE_FLAG_OVERLAPPED
, 0);
449 free(escaped_port_name
);
452 /* retrieve USB device details from the device descriptor */
453 get_usb_details(port
, device_info_data
.DevInst
);
458 SetupDiDestroyDeviceInfoList(device_info
);
463 SP_PRIV
enum sp_return
list_ports(struct sp_port
***list
)
467 DWORD max_value_len
, max_data_size
, max_data_len
;
468 DWORD value_len
, data_size
, data_len
;
469 DWORD type
, index
= 0;
474 DEBUG("Opening registry key");
475 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"),
476 0, KEY_QUERY_VALUE
, &key
) != ERROR_SUCCESS
) {
477 SET_FAIL(ret
, "RegOpenKeyEx() failed");
480 DEBUG("Querying registry key value and data sizes");
481 if (RegQueryInfoKey(key
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
482 &max_value_len
, &max_data_size
, NULL
, NULL
) != ERROR_SUCCESS
) {
483 SET_FAIL(ret
, "RegQueryInfoKey() failed");
486 max_data_len
= max_data_size
/ sizeof(TCHAR
);
487 if (!(value
= malloc((max_value_len
+ 1) * sizeof(TCHAR
)))) {
488 SET_ERROR(ret
, SP_ERR_MEM
, "registry value malloc failed");
491 if (!(data
= malloc((max_data_len
+ 1) * sizeof(TCHAR
)))) {
492 SET_ERROR(ret
, SP_ERR_MEM
, "registry data malloc failed");
495 DEBUG("Iterating over values");
497 value_len
= max_value_len
+ 1,
498 data_size
= max_data_size
,
499 RegEnumValue(key
, index
, value
, &value_len
,
500 NULL
, &type
, (LPBYTE
)data
, &data_size
) == ERROR_SUCCESS
)
502 if (type
== REG_SZ
) {
503 data_len
= data_size
/ sizeof(TCHAR
);
504 data
[data_len
] = '\0';
506 name_len
= WideCharToMultiByte(CP_ACP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
508 name_len
= data_len
+ 1;
510 if (!(name
= malloc(name_len
))) {
511 SET_ERROR(ret
, SP_ERR_MEM
, "registry port name malloc failed");
515 WideCharToMultiByte(CP_ACP
, 0, data
, -1, name
, name_len
, NULL
, NULL
);
519 DEBUG_FMT("Found port %s", name
);
520 if (!(*list
= list_append(*list
, name
))) {
521 SET_ERROR(ret
, SP_ERR_MEM
, "list append failed");