Move usb scanning code to ao-tools library
[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 #include "cc.h"
20
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 dir_filter_tty_colon(const struct dirent *d)
69 {
70         return strncmp(d->d_name, "tty:", 4) == 0;
71 }
72
73 static int
74 dir_filter_tty(const struct dirent *d)
75 {
76         return strncmp(d->d_name, "tty", 3) == 0;
77 }
78
79 static char *
80 usb_tty(char *sys)
81 {
82         char *base;
83         int num_configs;
84         int config;
85         struct dirent **namelist;
86         int interface;
87         int num_interfaces;
88         char endpoint_base[20];
89         char *endpoint_full;
90         char *tty_dir;
91         int ntty;
92         char *tty;
93
94         base = cc_basename(sys);
95         num_configs = load_hex(sys, "bNumConfigurations");
96         num_interfaces = load_hex(sys, "bNumInterfaces");
97         for (config = 1; config <= num_configs; config++) {
98                 for (interface = 0; interface < num_interfaces; interface++) {
99                         sprintf(endpoint_base, "%s:%d.%d",
100                                 base, config, interface);
101                         endpoint_full = cc_fullname(sys, endpoint_base);
102
103                         /* Check for tty:ttyACMx style names
104                          */
105                         ntty = scandir(endpoint_full, &namelist,
106                                        dir_filter_tty_colon,
107                                        alphasort);
108                         if (ntty > 0) {
109                                 free(endpoint_full);
110                                 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
111                                 free(namelist);
112                                 return tty;
113                         }
114
115                         /* Check for tty/ttyACMx style names
116                          */
117                         tty_dir = cc_fullname(endpoint_full, "tty");
118                         free(endpoint_full);
119                         ntty = scandir(tty_dir, &namelist,
120                                        dir_filter_tty,
121                                        alphasort);
122                         free (tty_dir);
123                         if (ntty > 0) {
124                                 tty = cc_fullname("/dev", namelist[0]->d_name);
125                                 free(namelist);
126                                 return tty;
127                         }
128                 }
129         }
130         return NULL;
131 }
132
133 static struct cc_usbdev *
134 usb_scan_device(char *sys)
135 {
136         struct cc_usbdev *usbdev;
137
138         usbdev = calloc(1, sizeof (struct cc_usbdev));
139         if (!usbdev)
140                 return NULL;
141         usbdev->sys = strdup(sys);
142         usbdev->manufacturer = load_string(sys, "manufacturer");
143         usbdev->product = load_string(sys, "product");
144         usbdev->serial = load_string(sys, "serial");
145         usbdev->idProduct = load_hex(sys, "idProduct");
146         usbdev->idVendor = load_hex(sys, "idVendor");
147         usbdev->tty = usb_tty(sys);
148         return usbdev;
149 }
150
151 static void
152 usbdev_free(struct cc_usbdev *usbdev)
153 {
154         free(usbdev->sys);
155         free(usbdev->manufacturer);
156         free(usbdev->product);
157         free(usbdev->serial);
158         free(usbdev->tty);
159         free(usbdev);
160 }
161
162 #define USB_DEVICES     "/sys/bus/usb/devices"
163
164 static int
165 dir_filter_dev(const struct dirent *d)
166 {
167         const char      *n = d->d_name;
168         char    c;
169
170         while ((c = *n++)) {
171                 if (isdigit(c))
172                         continue;
173                 if (c == '-')
174                         continue;
175                 if (c == '.' && n != d->d_name + 1)
176                         continue;
177                 return 0;
178         }
179         return 1;
180 }
181
182 struct cc_usbdevs *
183 cc_usbdevs_scan(void)
184 {
185         int                     e;
186         struct dirent           **ents;
187         char                    *dir;
188         struct cc_usbdev        *dev;
189         struct cc_usbdevs       *devs;
190         int                     n;
191
192         devs = calloc(1, sizeof (struct cc_usbdevs));
193         if (!devs)
194                 return NULL;
195
196         n = scandir (USB_DEVICES, &ents,
197                      dir_filter_dev,
198                      alphasort);
199         if (!n)
200                 return 0;
201         for (e = 0; e < n; e++) {
202                 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
203                 dev = usb_scan_device(dir);
204                 free(dir);
205                 if (dev->idVendor == 0xfffe && dev->tty) {
206                         if (devs->dev)
207                                 devs->dev = realloc(devs->dev,
208                                                     devs->ndev + 1 * sizeof (struct usbdev *));
209                         else
210                                 devs->dev = malloc (sizeof (struct usbdev *));
211                         devs->dev[devs->ndev++] = dev;
212                 }
213         }
214         free(ents);
215         return devs;
216 }
217
218 void
219 cc_usbdevs_free(struct cc_usbdevs *usbdevs)
220 {
221         int     i;
222
223         if (!usbdevs)
224                 return;
225         for (i = 0; i < usbdevs->ndev; i++)
226                 usbdev_free(usbdevs->dev[i]);
227         free(usbdevs);
228 }