6c3ba5910b3602465e918dc38f7bcec04d700e9f
[fw/altos] / ao-tools / lib / cc-usbdev.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #define _GNU_SOURCE
20 #include "cc.h"
21 #include <ctype.h>
22 #include <dirent.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 static char *
28 load_string(char *dir, char *file)
29 {
30         char    *full = cc_fullname(dir, file);
31         char    line[4096];
32         char    *r;
33         FILE    *f;
34         int     rlen;
35
36         f = fopen(full, "r");
37         free(full);
38         if (!f)
39                 return NULL;
40         r = fgets(line, sizeof (line), f);
41         fclose(f);
42         if (!r)
43                 return NULL;
44         rlen = strlen(r);
45         if (r[rlen-1] == '\n')
46                 r[rlen-1] = '\0';
47         return strdup(r);
48 }
49
50 static int
51 load_hex(char *dir, char *file)
52 {
53         char    *line;
54         char    *end;
55         long    i;
56
57         line = load_string(dir, file);
58         if (!line)
59                 return -1;
60         i = strtol(line, &end, 16);
61         free(line);
62         if (end == line)
63                 return -1;
64         return i;
65 }
66
67 static int
68 load_dec(char *dir, char *file)
69 {
70         char    *line;
71         char    *end;
72         long    i;
73
74         line = load_string(dir, file);
75         if (!line)
76                 return -1;
77         i = strtol(line, &end, 10);
78         free(line);
79         if (end == line)
80                 return -1;
81         return i;
82 }
83
84 static int
85 dir_filter_tty_colon(const struct dirent *d)
86 {
87         return strncmp(d->d_name, "tty:", 4) == 0;
88 }
89
90 static int
91 dir_filter_tty(const struct dirent *d)
92 {
93         return strncmp(d->d_name, "tty", 3) == 0;
94 }
95
96 static char *
97 usb_tty(char *sys)
98 {
99         char *base;
100         int num_configs;
101         int config;
102         struct dirent **namelist;
103         int interface;
104         int num_interfaces;
105         char endpoint_base[20];
106         char *endpoint_full;
107         char *tty_dir;
108         int ntty;
109         char *tty;
110
111         base = cc_basename(sys);
112         num_configs = load_hex(sys, "bNumConfigurations");
113         num_interfaces = load_hex(sys, "bNumInterfaces");
114         for (config = 1; config <= num_configs; config++) {
115                 for (interface = 0; interface < num_interfaces; interface++) {
116                         sprintf(endpoint_base, "%s:%d.%d",
117                                 base, config, interface);
118                         endpoint_full = cc_fullname(sys, endpoint_base);
119
120                         /* Check for tty:ttyACMx style names
121                          */
122                         ntty = scandir(endpoint_full, &namelist,
123                                        dir_filter_tty_colon,
124                                        alphasort);
125                         if (ntty > 0) {
126                                 free(endpoint_full);
127                                 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
128                                 free(namelist);
129                                 return tty;
130                         }
131
132                         /* Check for tty/ttyACMx style names
133                          */
134                         tty_dir = cc_fullname(endpoint_full, "tty");
135                         ntty = scandir(tty_dir, &namelist,
136                                        dir_filter_tty,
137                                        alphasort);
138                         free (tty_dir);
139                         if (ntty > 0) {
140                                 tty = cc_fullname("/dev", namelist[0]->d_name);
141                                 free(endpoint_full);
142                                 free(namelist);
143                                 return tty;
144                         }
145
146                         /* Check for ttyACMx style names
147                          */
148                         ntty = scandir(endpoint_full, &namelist,
149                                        dir_filter_tty,
150                                        alphasort);
151                         free(endpoint_full);
152                         if (ntty > 0) {
153                                 tty = cc_fullname("/dev", namelist[0]->d_name);
154                                 free(namelist);
155                                 return tty;
156                         }
157                 }
158         }
159         return NULL;
160 }
161
162 static struct cc_usbdev *
163 usb_scan_device(char *sys)
164 {
165         struct cc_usbdev *usbdev;
166
167         usbdev = calloc(1, sizeof (struct cc_usbdev));
168         if (!usbdev)
169                 return NULL;
170         usbdev->sys = strdup(sys);
171         usbdev->manufacturer = load_string(sys, "manufacturer");
172         usbdev->product = load_string(sys, "product");
173         usbdev->serial = load_dec(sys, "serial");
174         usbdev->idProduct = load_hex(sys, "idProduct");
175         usbdev->idVendor = load_hex(sys, "idVendor");
176         usbdev->tty = usb_tty(sys);
177         return usbdev;
178 }
179
180 static void
181 usbdev_free(struct cc_usbdev *usbdev)
182 {
183         free(usbdev->sys);
184         free(usbdev->manufacturer);
185         free(usbdev->product);
186         /* this can get used as a return value */
187         if (usbdev->tty)
188                 free(usbdev->tty);
189         free(usbdev);
190 }
191
192 #define USB_DEVICES     "/sys/bus/usb/devices"
193
194 static int
195 dir_filter_dev(const struct dirent *d)
196 {
197         const char      *n = d->d_name;
198         char    c;
199
200         while ((c = *n++)) {
201                 if (isdigit(c))
202                         continue;
203                 if (c == '-')
204                         continue;
205                 if (c == '.' && n != d->d_name + 1)
206                         continue;
207                 return 0;
208         }
209         return 1;
210 }
211
212 static int
213 is_am(int idVendor, int idProduct) {
214         if (idVendor == 0xfffe)
215                 return 1;
216         if (idVendor == 0x0403 && idProduct == 0x6015)
217                 return 1;
218         return 0;
219 }
220
221 struct cc_usbdevs *
222 cc_usbdevs_scan(int non_tty)
223 {
224         int                     e;
225         struct dirent           **ents;
226         char                    *dir;
227         struct cc_usbdev        *dev;
228         struct cc_usbdevs       *devs;
229         int                     n;
230
231         devs = calloc(1, sizeof (struct cc_usbdevs));
232         if (!devs)
233                 return NULL;
234
235         n = scandir (USB_DEVICES, &ents,
236                      dir_filter_dev,
237                      alphasort);
238         if (!n)
239                 return 0;
240         for (e = 0; e < n; e++) {
241                 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
242                 dev = usb_scan_device(dir);
243                 free(dir);
244                 if (is_am(dev->idVendor, dev->idProduct) && (non_tty || dev->tty)) {
245                         if (devs->dev)
246                                 devs->dev = realloc(devs->dev,
247                                                     (devs->ndev + 1) * sizeof (struct usbdev *));
248                         else
249                                 devs->dev = malloc (sizeof (struct usbdev *));
250                         devs->dev[devs->ndev++] = dev;
251                 }
252         }
253         free(ents);
254         return devs;
255 }
256
257 void
258 cc_usbdevs_free(struct cc_usbdevs *usbdevs)
259 {
260         int     i;
261
262         if (!usbdevs)
263                 return;
264         for (i = 0; i < usbdevs->ndev; i++)
265                 usbdev_free(usbdevs->dev[i]);
266         free(usbdevs);
267 }
268
269 static char *
270 match_dev(char *product, int serial)
271 {
272         struct cc_usbdevs       *devs;
273         struct cc_usbdev        *dev;
274         int                     i;
275         char                    *tty = NULL;
276
277         devs = cc_usbdevs_scan(FALSE);
278         if (!devs)
279                 return NULL;
280         for (i = 0; i < devs->ndev; i++) {
281                 dev = devs->dev[i];
282                 if (product && strncmp (product, dev->product, strlen(product)) != 0)
283                         continue;
284                 if (serial && serial != dev->serial)
285                         continue;
286                 break;
287         }
288         if (i < devs->ndev) {
289                 tty = devs->dev[i]->tty;
290                 devs->dev[i]->tty = NULL;
291         }
292         cc_usbdevs_free(devs);
293         return tty;
294 }
295
296 char *
297 cc_usbdevs_find_by_arg(char *arg, char *default_product)
298 {
299         char    *product;
300         int     serial;
301         char    *end;
302         char    *colon;
303         char    *tty;
304
305         if (arg)
306         {
307                 /* check for <serial> */
308                 serial = strtol(arg, &end, 0);
309                 if (end != arg) {
310                         if (*end != '\0')
311                                 return NULL;
312                         product = NULL;
313                 } else {
314                         /* check for <product>:<serial> */
315                         colon = strchr(arg, ':');
316                         if (colon) {
317                                 product = strndup(arg, colon - arg);
318                                 serial = strtol(colon + 1, &end, 0);
319                                 if (*end != '\0')
320                                         return NULL;
321                         } else {
322                                 product = arg;
323                                 serial = 0;
324                         }
325                 }
326         } else {
327                 product = NULL;
328                 serial = 0;
329         }
330         tty = NULL;
331         if (!product && default_product)
332                 tty = match_dev(default_product, serial);
333         if (!tty)
334                 tty = match_dev(product, serial);
335         if (product && product != arg)
336                 free(product);
337         return tty;
338 }