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
)
26 /* Description limited to 127 char,
27 anything longer would not be user friendly anyway */
28 char description
[128];
29 int bus
, address
, vid
, pid
= -1;
30 char manufacturer
[128], product
[128], serial
[128];
31 CFMutableDictionaryRef classes
;
33 io_object_t ioport
, ioparent
;
34 CFTypeRef cf_property
, cf_bus
, cf_address
, cf_vendor
, cf_product
;
36 char path
[PATH_MAX
], class[16];
38 DEBUG("Getting serial port list");
39 if (!(classes
= IOServiceMatching(kIOSerialBSDServiceValue
)))
40 RETURN_FAIL("IOServiceMatching() failed");
42 if (IOServiceGetMatchingServices(kIOMasterPortDefault
, classes
,
43 &iter
) != KERN_SUCCESS
)
44 RETURN_FAIL("IOServiceGetMatchingServices() failed");
46 DEBUG("Iterating over results");
47 while ((ioport
= IOIteratorNext(iter
))) {
48 if (!(cf_property
= IORegistryEntryCreateCFProperty(ioport
,
49 CFSTR(kIOCalloutDeviceKey
), kCFAllocatorDefault
, 0))) {
50 IOObjectRelease(ioport
);
53 result
= CFStringGetCString(cf_property
, path
, sizeof(path
),
54 kCFStringEncodingASCII
);
55 CFRelease(cf_property
);
56 if (!result
|| strcmp(path
, port
->name
)) {
57 IOObjectRelease(ioport
);
60 DEBUG_FMT("Found port %s", path
);
62 IORegistryEntryGetParentEntry(ioport
, kIOServicePlane
, &ioparent
);
63 if ((cf_property
=IORegistryEntrySearchCFProperty(ioparent
,kIOServicePlane
,
64 CFSTR("IOProviderClass"), kCFAllocatorDefault
,
65 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
66 if (CFStringGetCString(cf_property
, class, sizeof(class),
67 kCFStringEncodingASCII
) &&
68 strstr(class, "USB")) {
69 DEBUG("Found USB class device");
70 port
->transport
= SP_TRANSPORT_USB
;
72 CFRelease(cf_property
);
74 IOObjectRelease(ioparent
);
76 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
77 CFSTR("USB Interface Name"), kCFAllocatorDefault
,
78 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
79 (cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
80 CFSTR("USB Product Name"), kCFAllocatorDefault
,
81 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
82 (cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
83 CFSTR("Product Name"), kCFAllocatorDefault
,
84 kIORegistryIterateRecursively
| kIORegistryIterateParents
)) ||
85 (cf_property
= IORegistryEntryCreateCFProperty(ioport
,
86 CFSTR(kIOTTYDeviceKey
), kCFAllocatorDefault
, 0))) {
87 if (CFStringGetCString(cf_property
, description
, sizeof(description
),
88 kCFStringEncodingASCII
)) {
89 DEBUG_FMT("Found description %s", description
);
90 port
->description
= strdup(description
);
92 CFRelease(cf_property
);
94 DEBUG("No description for this device");
97 cf_bus
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
98 CFSTR("USBBusNumber"),
100 kIORegistryIterateRecursively
101 | kIORegistryIterateParents
);
102 cf_address
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
103 CFSTR("USB Address"),
105 kIORegistryIterateRecursively
106 | kIORegistryIterateParents
);
107 if (cf_bus
&& cf_address
&&
108 CFNumberGetValue(cf_bus
, kCFNumberIntType
, &bus
) &&
109 CFNumberGetValue(cf_address
, kCFNumberIntType
, &address
)) {
110 DEBUG_FMT("Found matching USB bus:address %03d:%03d", bus
, address
);
112 port
->usb_address
= address
;
114 if (cf_bus
) CFRelease(cf_bus
);
115 if (cf_address
) CFRelease(cf_address
);
117 cf_vendor
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
120 kIORegistryIterateRecursively
121 | kIORegistryIterateParents
);
122 cf_product
= IORegistryEntrySearchCFProperty(ioport
, kIOServicePlane
,
125 kIORegistryIterateRecursively
126 | kIORegistryIterateParents
);
127 if (cf_vendor
&& cf_product
&&
128 CFNumberGetValue(cf_vendor
, kCFNumberIntType
, &vid
) &&
129 CFNumberGetValue(cf_product
, kCFNumberIntType
, &pid
)) {
130 DEBUG_FMT("Found matching USB vid:pid %04X:%04X", vid
, pid
);
134 if (cf_vendor
) CFRelease(cf_vendor
);
135 if (cf_product
) CFRelease(cf_product
);
137 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
138 CFSTR("USB Vendor Name"), kCFAllocatorDefault
,
139 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
140 if (CFStringGetCString(cf_property
, manufacturer
, sizeof(manufacturer
),
141 kCFStringEncodingASCII
)) {
142 DEBUG_FMT("Found manufacturer %s", manufacturer
);
143 port
->usb_manufacturer
= strdup(manufacturer
);
145 CFRelease(cf_property
);
148 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
149 CFSTR("USB Product Name"), kCFAllocatorDefault
,
150 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
151 if (CFStringGetCString(cf_property
, product
, sizeof(product
),
152 kCFStringEncodingASCII
)) {
153 DEBUG_FMT("Found product name %s", product
);
154 port
->usb_product
= strdup(product
);
156 CFRelease(cf_property
);
159 if ((cf_property
= IORegistryEntrySearchCFProperty(ioport
,kIOServicePlane
,
160 CFSTR("USB Serial Number"), kCFAllocatorDefault
,
161 kIORegistryIterateRecursively
| kIORegistryIterateParents
))) {
162 if (CFStringGetCString(cf_property
, serial
, sizeof(serial
),
163 kCFStringEncodingASCII
)) {
164 DEBUG_FMT("Found serial number %s", serial
);
165 port
->usb_serial
= strdup(serial
);
167 CFRelease(cf_property
);
170 IOObjectRelease(ioport
);
173 IOObjectRelease(iter
);
178 SP_PRIV
enum sp_return
list_ports(struct sp_port
***list
)
180 CFMutableDictionaryRef classes
;
188 DEBUG("Creating matching dictionary");
189 if (!(classes
= IOServiceMatching(kIOSerialBSDServiceValue
))) {
190 SET_FAIL(ret
, "IOServiceMatching() failed");
194 DEBUG("Getting matching services");
195 if (IOServiceGetMatchingServices(kIOMasterPortDefault
, classes
,
196 &iter
) != KERN_SUCCESS
) {
197 SET_FAIL(ret
, "IOServiceGetMatchingServices() failed");
201 DEBUG("Iterating over results");
202 while ((port
= IOIteratorNext(iter
))) {
203 cf_path
= IORegistryEntryCreateCFProperty(port
,
204 CFSTR(kIOCalloutDeviceKey
), kCFAllocatorDefault
, 0);
206 result
= CFStringGetCString(cf_path
, path
, sizeof(path
),
207 kCFStringEncodingASCII
);
210 DEBUG_FMT("Found port %s", path
);
211 if (!(*list
= list_append(*list
, path
))) {
212 SET_ERROR(ret
, SP_ERR_MEM
, "list append failed");
213 IOObjectRelease(port
);
218 IOObjectRelease(port
);
221 IOObjectRelease(iter
);