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 SP_PRIV
enum sp_return
get_port_details(struct sp_port
*port
)
27 * Description limited to 127 char, anything longer
28 * would not be user friendly anyway.
30 char description
[128];
31 int bus
, address
, vid
, pid
= -1;
32 char manufacturer
[128], product
[128], serial
[128];
33 CFMutableDictionaryRef classes
;
35 io_object_t ioport
, ioparent
;
36 CFTypeRef cf_property
, cf_bus
, cf_address
, cf_vendor
, cf_product
;
38 char path
[PATH_MAX
], class[16];
40 DEBUG("Getting serial port list");
41 if (!(classes
= IOServiceMatching(kIOSerialBSDServiceValue
)))
42 RETURN_FAIL("IOServiceMatching() failed");
44 if (IOServiceGetMatchingServices(kIOMasterPortDefault
, classes
,
45 &iter
) != KERN_SUCCESS
)
46 RETURN_FAIL("IOServiceGetMatchingServices() failed");
48 DEBUG("Iterating over results");
49 while ((ioport
= IOIteratorNext(iter
))) {
50 if (!(cf_property
= IORegistryEntryCreateCFProperty(ioport
,
51 CFSTR(kIOCalloutDeviceKey
), kCFAllocatorDefault
, 0))) {
52 IOObjectRelease(ioport
);
55 result
= CFStringGetCString(cf_property
, path
, sizeof(path
),
56 kCFStringEncodingASCII
);
57 CFRelease(cf_property
);
58 if (!result
|| strcmp(path
, port
->name
)) {
59 IOObjectRelease(ioport
);
62 DEBUG_FMT("Found port %s", path
);
64 IORegistryEntryGetParentEntry(ioport
, kIOServicePlane
, &ioparent
);
65 if ((cf_property
=IORegistryEntrySearchCFProperty(ioparent
,kIOServicePlane
,
66 CFSTR("IOProviderClass"), kCFAllocatorDefault
,
67 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
68 if (CFStringGetCString(cf_property
, class, sizeof(class),
69 kCFStringEncodingASCII
) &&
70 strstr(class, "USB")) {
71 DEBUG("Found USB class device");
72 port
->transport
= SP_TRANSPORT_USB
;
74 CFRelease(cf_property
);
76 IOObjectRelease(ioparent
);
78 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
79 CFSTR("USB Interface Name"), kCFAllocatorDefault
,
80 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
81 (cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
82 CFSTR("USB Product Name"), kCFAllocatorDefault
,
83 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
84 (cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
85 CFSTR("Product Name"), kCFAllocatorDefault
,
86 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
87 (cf_property
= IORegistryEntryCreateCFProperty(ioport
,
88 CFSTR(kIOTTYDeviceKey
), kCFAllocatorDefault
, 0))) {
89 if (CFStringGetCString(cf_property
, description
, sizeof(description
),
90 kCFStringEncodingASCII
)) {
91 DEBUG_FMT("Found description %s", description
);
92 port
->description
= strdup(description
);
94 CFRelease(cf_property
);
96 DEBUG("No description for this device");
99 cf_bus
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
100 CFSTR("USBBusNumber"),
102 kIORegistryIterateRecursively
103 | kIORegistryIterateParents
);
104 cf_address
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
105 CFSTR("USB Address"),
107 kIORegistryIterateRecursively
108 | kIORegistryIterateParents
);
109 if (cf_bus
&& cf_address
&&
110 CFNumberGetValue(cf_bus
, kCFNumberIntType
, &bus
) &&
111 CFNumberGetValue(cf_address
, kCFNumberIntType
, &address
)) {
112 DEBUG_FMT("Found matching USB bus:address %03d:%03d", bus
, address
);
114 port
->usb_address
= address
;
119 CFRelease(cf_address
);
121 cf_vendor
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
124 kIORegistryIterateRecursively
125 | kIORegistryIterateParents
);
126 cf_product
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
129 kIORegistryIterateRecursively
130 | kIORegistryIterateParents
);
131 if (cf_vendor
&& cf_product
&&
132 CFNumberGetValue(cf_vendor
, kCFNumberIntType
, &vid
) &&
133 CFNumberGetValue(cf_product
, kCFNumberIntType
, &pid
)) {
134 DEBUG_FMT("Found matching USB VID:PID %04X:%04X", vid
, pid
);
139 CFRelease(cf_vendor
);
141 CFRelease(cf_product
);
143 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
144 CFSTR("USB Vendor Name"), kCFAllocatorDefault
,
145 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
146 if (CFStringGetCString(cf_property
, manufacturer
, sizeof(manufacturer
),
147 kCFStringEncodingASCII
)) {
148 DEBUG_FMT("Found manufacturer %s", manufacturer
);
149 port
->usb_manufacturer
= strdup(manufacturer
);
151 CFRelease(cf_property
);
154 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
155 CFSTR("USB Product Name"), kCFAllocatorDefault
,
156 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
157 if (CFStringGetCString(cf_property
, product
, sizeof(product
),
158 kCFStringEncodingASCII
)) {
159 DEBUG_FMT("Found product name %s", product
);
160 port
->usb_product
= strdup(product
);
162 CFRelease(cf_property
);
165 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
166 CFSTR("USB Serial Number"), kCFAllocatorDefault
,
167 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
168 if (CFStringGetCString(cf_property
, serial
, sizeof(serial
),
169 kCFStringEncodingASCII
)) {
170 DEBUG_FMT("Found serial number %s", serial
);
171 port
->usb_serial
= strdup(serial
);
173 CFRelease(cf_property
);
176 IOObjectRelease(ioport
);
179 IOObjectRelease(iter
);
184 SP_PRIV
enum sp_return
list_ports(struct sp_port
***list
)
186 CFMutableDictionaryRef classes
;
194 DEBUG("Creating matching dictionary");
195 if (!(classes
= IOServiceMatching(kIOSerialBSDServiceValue
))) {
196 SET_FAIL(ret
, "IOServiceMatching() failed");
200 DEBUG("Getting matching services");
201 if (IOServiceGetMatchingServices(kIOMasterPortDefault
, classes
,
202 &iter
) != KERN_SUCCESS
) {
203 SET_FAIL(ret
, "IOServiceGetMatchingServices() failed");
207 DEBUG("Iterating over results");
208 while ((port
= IOIteratorNext(iter
))) {
209 cf_path
= IORegistryEntryCreateCFProperty(port
,
210 CFSTR(kIOCalloutDeviceKey
), kCFAllocatorDefault
, 0);
212 result
= CFStringGetCString(cf_path
, path
, sizeof(path
),
213 kCFStringEncodingASCII
);
216 DEBUG_FMT("Found port %s", path
);
217 if (!(*list
= list_append(*list
, path
))) {
218 SET_ERROR(ret
, SP_ERR_MEM
, "List append failed");
219 IOObjectRelease(port
);
224 IOObjectRelease(port
);
227 IOObjectRelease(iter
);