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/>.
22 #include "libserialport.h"
23 #include "libserialport_internal.h"
25 SP_PRIV
enum sp_return
get_port_details(struct sp_port
*port
)
28 * Description limited to 127 char, anything longer
29 * would not be user friendly anyway.
31 char description
[128];
32 int bus
, address
, vid
, pid
= -1;
33 char manufacturer
[128], product
[128], serial
[128];
34 CFMutableDictionaryRef classes
;
36 io_object_t ioport
, ioparent
;
37 CFTypeRef cf_property
, cf_bus
, cf_address
, cf_vendor
, cf_product
;
39 char path
[PATH_MAX
], class[16];
41 DEBUG("Getting serial port list");
42 if (!(classes
= IOServiceMatching(kIOSerialBSDServiceValue
)))
43 RETURN_FAIL("IOServiceMatching() failed");
45 if (IOServiceGetMatchingServices(kIOMasterPortDefault
, classes
,
46 &iter
) != KERN_SUCCESS
)
47 RETURN_FAIL("IOServiceGetMatchingServices() failed");
49 DEBUG("Iterating over results");
50 while ((ioport
= IOIteratorNext(iter
))) {
51 if (!(cf_property
= IORegistryEntryCreateCFProperty(ioport
,
52 CFSTR(kIOCalloutDeviceKey
), kCFAllocatorDefault
, 0))) {
53 IOObjectRelease(ioport
);
56 result
= CFStringGetCString(cf_property
, path
, sizeof(path
),
57 kCFStringEncodingASCII
);
58 CFRelease(cf_property
);
59 if (!result
|| strcmp(path
, port
->name
)) {
60 IOObjectRelease(ioport
);
63 DEBUG_FMT("Found port %s", path
);
65 IORegistryEntryGetParentEntry(ioport
, kIOServicePlane
, &ioparent
);
66 if ((cf_property
=IORegistryEntrySearchCFProperty(ioparent
,kIOServicePlane
,
67 CFSTR("IOClass"), kCFAllocatorDefault
,
68 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
69 if (CFStringGetCString(cf_property
, class, sizeof(class),
70 kCFStringEncodingASCII
) &&
71 strstr(class, "USB")) {
72 DEBUG("Found USB class device");
73 port
->transport
= SP_TRANSPORT_USB
;
75 CFRelease(cf_property
);
77 if ((cf_property
=IORegistryEntrySearchCFProperty(ioparent
,kIOServicePlane
,
78 CFSTR("IOProviderClass"), kCFAllocatorDefault
,
79 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
80 if (CFStringGetCString(cf_property
, class, sizeof(class),
81 kCFStringEncodingASCII
) &&
82 strstr(class, "USB")) {
83 DEBUG("Found USB class device");
84 port
->transport
= SP_TRANSPORT_USB
;
86 CFRelease(cf_property
);
88 IOObjectRelease(ioparent
);
90 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
91 CFSTR("USB Interface Name"), kCFAllocatorDefault
,
92 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
93 (cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
94 CFSTR("USB Product Name"), kCFAllocatorDefault
,
95 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
96 (cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
97 CFSTR("Product Name"), kCFAllocatorDefault
,
98 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
99 (cf_property
= IORegistryEntryCreateCFProperty(ioport
,
100 CFSTR(kIOTTYDeviceKey
), kCFAllocatorDefault
, 0))) {
101 if (CFStringGetCString(cf_property
, description
, sizeof(description
),
102 kCFStringEncodingASCII
)) {
103 DEBUG_FMT("Found description %s", description
);
104 port
->description
= strdup(description
);
106 CFRelease(cf_property
);
108 DEBUG("No description for this device");
111 cf_bus
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
112 CFSTR("USBBusNumber"),
114 kIORegistryIterateRecursively
115 | kIORegistryIterateParents
);
116 cf_address
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
117 CFSTR("USB Address"),
119 kIORegistryIterateRecursively
120 | kIORegistryIterateParents
);
121 if (cf_bus
&& cf_address
&&
122 CFNumberGetValue(cf_bus
, kCFNumberIntType
, &bus
) &&
123 CFNumberGetValue(cf_address
, kCFNumberIntType
, &address
)) {
124 DEBUG_FMT("Found matching USB bus:address %03d:%03d", bus
, address
);
126 port
->usb_address
= address
;
131 CFRelease(cf_address
);
133 cf_vendor
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
136 kIORegistryIterateRecursively
137 | kIORegistryIterateParents
);
138 cf_product
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
141 kIORegistryIterateRecursively
142 | kIORegistryIterateParents
);
143 if (cf_vendor
&& cf_product
&&
144 CFNumberGetValue(cf_vendor
, kCFNumberIntType
, &vid
) &&
145 CFNumberGetValue(cf_product
, kCFNumberIntType
, &pid
)) {
146 DEBUG_FMT("Found matching USB VID:PID %04X:%04X", vid
, pid
);
151 CFRelease(cf_vendor
);
153 CFRelease(cf_product
);
155 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
156 CFSTR("USB Vendor Name"), kCFAllocatorDefault
,
157 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
158 if (CFStringGetCString(cf_property
, manufacturer
, sizeof(manufacturer
),
159 kCFStringEncodingASCII
)) {
160 DEBUG_FMT("Found manufacturer %s", manufacturer
);
161 port
->usb_manufacturer
= strdup(manufacturer
);
163 CFRelease(cf_property
);
166 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
167 CFSTR("USB Product Name"), kCFAllocatorDefault
,
168 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
169 if (CFStringGetCString(cf_property
, product
, sizeof(product
),
170 kCFStringEncodingASCII
)) {
171 DEBUG_FMT("Found product name %s", product
);
172 port
->usb_product
= strdup(product
);
174 CFRelease(cf_property
);
177 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
178 CFSTR("USB Serial Number"), kCFAllocatorDefault
,
179 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
180 if (CFStringGetCString(cf_property
, serial
, sizeof(serial
),
181 kCFStringEncodingASCII
)) {
182 DEBUG_FMT("Found serial number %s", serial
);
183 port
->usb_serial
= strdup(serial
);
185 CFRelease(cf_property
);
188 IOObjectRelease(ioport
);
191 IOObjectRelease(iter
);
196 SP_PRIV
enum sp_return
list_ports(struct sp_port
***list
)
198 CFMutableDictionaryRef classes
;
206 DEBUG("Creating matching dictionary");
207 if (!(classes
= IOServiceMatching(kIOSerialBSDServiceValue
))) {
208 SET_FAIL(ret
, "IOServiceMatching() failed");
212 DEBUG("Getting matching services");
213 if (IOServiceGetMatchingServices(kIOMasterPortDefault
, classes
,
214 &iter
) != KERN_SUCCESS
) {
215 SET_FAIL(ret
, "IOServiceGetMatchingServices() failed");
219 DEBUG("Iterating over results");
220 while ((port
= IOIteratorNext(iter
))) {
221 cf_path
= IORegistryEntryCreateCFProperty(port
,
222 CFSTR(kIOCalloutDeviceKey
), kCFAllocatorDefault
, 0);
224 result
= CFStringGetCString(cf_path
, path
, sizeof(path
),
225 kCFStringEncodingASCII
);
228 DEBUG_FMT("Found port %s", path
);
229 if (!(*list
= list_append(*list
, path
))) {
230 SET_ERROR(ret
, SP_ERR_MEM
, "List append failed");
231 IOObjectRelease(port
);
236 IOObjectRelease(port
);
239 IOObjectRelease(iter
);