]> sigrok.org Git - libserialport.git/blob - macosx.c
windows: -no-undefined is required to make a DLL.
[libserialport.git] / macosx.c
1 /*
2  * This file is part of the libserialport project.
3  *
4  * Copyright (C) 2013-2014 Martin Ling <martin-libserialport@earth.li>
5  * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
6  *
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.
11  *
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.
16  *
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/>.
19  */
20
21 #include "libserialport.h"
22 #include "libserialport_internal.h"
23
24 SP_PRIV enum sp_return get_port_details(struct sp_port *port)
25 {
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;
32         io_iterator_t iter;
33         io_object_t ioport, ioparent;
34         CFTypeRef cf_property, cf_bus, cf_address, cf_vendor, cf_product;
35         Boolean result;
36         char path[PATH_MAX], class[16];
37
38         DEBUG("Getting serial port list");
39         if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue)))
40                 RETURN_FAIL("IOServiceMatching() failed");
41
42         if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes,
43                                          &iter) != KERN_SUCCESS)
44                 RETURN_FAIL("IOServiceGetMatchingServices() failed");
45
46         DEBUG("Iterating over results");
47         while ((ioport = IOIteratorNext(iter))) {
48                 if (!(cf_property = IORegistryEntryCreateCFProperty(ioport,
49                             CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0))) {
50                         IOObjectRelease(ioport);
51                         continue;
52                 }
53                 result = CFStringGetCString(cf_property, path, sizeof(path),
54                                             kCFStringEncodingASCII);
55                 CFRelease(cf_property);
56                 if (!result || strcmp(path, port->name)) {
57                         IOObjectRelease(ioport);
58                         continue;
59                 }
60                 DEBUG_FMT("Found port %s", path);
61
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;
71                         }
72                         CFRelease(cf_property);
73                 }
74                 IOObjectRelease(ioparent);
75
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);
91                         }
92                         CFRelease(cf_property);
93                 } else {
94                         DEBUG("No description for this device");
95                 }
96
97                 cf_bus = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
98                                                          CFSTR("USBBusNumber"),
99                                                          kCFAllocatorDefault,
100                                                          kIORegistryIterateRecursively
101                                                          | kIORegistryIterateParents);
102                 cf_address = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
103                                                          CFSTR("USB Address"),
104                                                          kCFAllocatorDefault,
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);
111                         port->usb_bus = bus;
112                         port->usb_address = address;
113                 }
114                 if (cf_bus    )  CFRelease(cf_bus);
115                 if (cf_address)  CFRelease(cf_address);
116
117                 cf_vendor = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
118                                                          CFSTR("idVendor"),
119                                                          kCFAllocatorDefault,
120                                                          kIORegistryIterateRecursively
121                                                          | kIORegistryIterateParents);
122                 cf_product = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
123                                                          CFSTR("idProduct"),
124                                                          kCFAllocatorDefault,
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);
131                         port->usb_vid = vid;
132                         port->usb_pid = pid;
133                 }
134                 if (cf_vendor )  CFRelease(cf_vendor);
135                 if (cf_product)  CFRelease(cf_product);
136
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);
144                         }
145                         CFRelease(cf_property);
146                 }
147
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);
155                         }
156                         CFRelease(cf_property);
157                 }
158
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);
166                         }
167                         CFRelease(cf_property);
168                 }
169
170                 IOObjectRelease(ioport);
171                 break;
172         }
173         IOObjectRelease(iter);
174
175         RETURN_OK();
176 }
177
178 SP_PRIV enum sp_return list_ports(struct sp_port ***list)
179 {
180         CFMutableDictionaryRef classes;
181         io_iterator_t iter;
182         char path[PATH_MAX];
183         io_object_t port;
184         CFTypeRef cf_path;
185         Boolean result;
186         int ret = SP_OK;
187
188         DEBUG("Creating matching dictionary");
189         if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue))) {
190                 SET_FAIL(ret, "IOServiceMatching() failed");
191                 goto out_done;
192         }
193
194         DEBUG("Getting matching services");
195         if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes,
196                                          &iter) != KERN_SUCCESS) {
197                 SET_FAIL(ret, "IOServiceGetMatchingServices() failed");
198                 goto out_done;
199         }
200
201         DEBUG("Iterating over results");
202         while ((port = IOIteratorNext(iter))) {
203                 cf_path = IORegistryEntryCreateCFProperty(port,
204                                 CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
205                 if (cf_path) {
206                         result = CFStringGetCString(cf_path, path, sizeof(path),
207                                                     kCFStringEncodingASCII);
208                         CFRelease(cf_path);
209                         if (result) {
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);
214                                         goto out;
215                                 }
216                         }
217                 }
218                 IOObjectRelease(port);
219         }
220 out:
221         IOObjectRelease(iter);
222 out_done:
223
224         return ret;
225 }