chiark / gitweb /
Merge branch 'master' of git+ssh://master.kernel.org/pub/scm/linux/hotplug/udev
[elogind.git] / extras / usb-db / usb-db.c
1 /*-*- linux-c -*-*/
2
3 /*
4  * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details:
15  */
16
17 #include <stdio.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <inttypes.h>
21 #include <ctype.h>
22 #include <stdlib.h>
23
24 #include <libudev.h>
25
26 #if defined(BUILD_FOR_USB)
27 # define DATABASE USB_DATABASE
28 # define SUBSYSTEM "usb"
29 # define DEVTYPE "usb_device"
30 # define VENDOR_ATTR "idVendor"
31 # define PRODUCT_ATTR "idProduct"
32 #elif defined(BUILD_FOR_PCI)
33 # define DATABASE PCI_DATABASE
34 # define SUBSYSTEM "pci"
35 # define DEVTYPE NULL
36 # define VENDOR_ATTR "vendor"
37 # define PRODUCT_ATTR "device"
38 #else
39 # error "Are you havin' a laugh?"
40 #endif
41
42 static int get_id_attr(
43         struct udev_device *parent,
44         const char *name,
45         uint16_t *value) {
46
47         const char *t;
48         unsigned u;
49
50         if (!(t = udev_device_get_sysattr_value(parent, name))) {
51                 fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name);
52                 return -1;
53         }
54
55         if (!strncmp(t, "0x", 2))
56                 t += 2;
57
58         if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) {
59                 fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent));
60                 return -1;
61         }
62
63         *value = (uint16_t) u;
64         return 0;
65 }
66
67 static int get_vid_pid(
68         struct udev_device *parent,
69         uint16_t *vid,
70         uint16_t *pid) {
71
72         if (get_id_attr(parent, VENDOR_ATTR, vid) < 0)
73                 return -1;
74         else if (*vid <= 0) {
75                 fprintf(stderr, "Invalid vendor id.\n");
76                 return -1;
77         }
78
79         if (get_id_attr(parent, PRODUCT_ATTR, pid) < 0)
80                 return -1;
81
82         return 0;
83 }
84
85 static void rstrip(char *n) {
86         size_t i;
87
88         for (i = strlen(n); i > 0 && isspace(n[i-1]); i--)
89                 n[i-1] = 0;
90 }
91
92 #define HEXCHARS "0123456789abcdefABCDEF"
93 #define WHITESPACE " \t\n\r"
94
95 static int lookup_vid_pid(
96         uint16_t vid,
97         uint16_t pid,
98         char **vendor,
99         char **product) {
100
101         FILE *f;
102         int ret = -1;
103         int found_vendor = 0;
104         char *line = NULL;
105
106         *vendor = *product = NULL;
107
108         if (!(f = fopen(DATABASE, "r"))) {
109                 fprintf(stderr, "Failed to open database file "DATABASE": %s\n", strerror(errno));
110                 return -1;
111         }
112
113         for (;;) {
114                 size_t n;
115
116                 if (line) {
117                         free(line);
118                         line = NULL;
119                 }
120
121                 if (getline(&line, &n, f) < 0)
122                         break;
123
124                 rstrip(line);
125
126                 if (line[0] == '#' || line[0] == 0)
127                         continue;
128
129                 if (strspn(line, HEXCHARS) == 4) {
130                         unsigned u;
131
132                         if (found_vendor)
133                                 break;
134
135                         if (sscanf(line, "%04x", &u) == 1 && u == vid) {
136                                 char *t;
137
138                                 t = line+4;
139                                 t += strspn(t, WHITESPACE);
140
141                                 if (!(*vendor = strdup(t))) {
142                                         fprintf(stderr, "Out of memory.\n");
143                                         goto finish;
144                                 }
145
146                                 found_vendor = 1;
147                         }
148
149                         continue;
150                 }
151
152                 if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) {
153                         unsigned u;
154
155                         if (sscanf(line+1, "%04x", &u) == 1 && u == pid) {
156                                 char *t;
157
158                                 t = line+5;
159                                 t += strspn(t, WHITESPACE);
160
161                                 if (!(*product = strdup(t))) {
162                                         fprintf(stderr, "Out of memory.\n");
163                                         goto finish;
164                                 }
165
166                                 break;
167                         }
168                 }
169         }
170
171         ret = 0;
172
173 finish:
174         free(line);
175         fclose(f);
176
177         if (ret < 0) {
178                 free(*product);
179                 free(*vendor);
180
181                 *product = *vendor = NULL;
182         }
183
184         return ret;
185 }
186
187 static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype)
188 {
189         const char *str;
190
191         str = udev_device_get_subsystem(dev);
192         if (str == NULL)
193                 goto try_parent;
194         if (strcmp(str, subsys) != 0)
195                 goto try_parent;
196
197         if (devtype != NULL) {
198                 str = udev_device_get_devtype(dev);
199                 if (str == NULL)
200                         goto try_parent;
201                 if (strcmp(str, devtype) != 0)
202                         goto try_parent;
203         }
204         return dev;
205 try_parent:
206         return udev_device_get_parent_with_subsystem_devtype(dev, SUBSYSTEM, DEVTYPE);
207 }
208
209 int main(int argc, char*argv[]) {
210
211         struct udev *udev = NULL;
212         int ret = 1;
213         char *sp;
214         struct udev_device *dev = NULL, *parent = NULL;
215         uint16_t vid = 0, pid = 0;
216         char *vendor = NULL, *product = NULL;
217
218         if (argc < 2) {
219                 fprintf(stderr, "Need to pass sysfs path.\n");
220                 goto finish;
221         }
222
223         if (!(udev = udev_new()))
224                 goto finish;
225
226         if (asprintf(&sp, "%s/%s", udev_get_sys_path(udev), argv[1]) < 0) {
227                 fprintf(stderr, "Failed to allocate sysfs path.\n");
228                 goto finish;
229         }
230
231         dev = udev_device_new_from_syspath(udev, sp);
232         free(sp);
233
234         if (!dev) {
235                 fprintf(stderr, "Failed to access %s.\n", argv[1]);
236                 goto finish;
237         }
238
239         parent = find_device(dev, SUBSYSTEM, DEVTYPE);
240         if (!parent) {
241                 fprintf(stderr, "Failed to find device.\n");
242                 goto finish;
243         }
244
245         if (get_vid_pid(parent, &vid, &pid) < 0)
246                 goto finish;
247
248         if (lookup_vid_pid(vid, pid, &vendor, &product) < 0)
249                 goto finish;
250
251         if (vendor)
252                 printf("ID_VENDOR_FROM_DATABASE=%s\n", vendor);
253
254         if (product)
255                 printf("ID_MODEL_FROM_DATABASE=%s\n", product);
256
257         ret = 0;
258
259 finish:
260         udev_device_unref(dev);
261         udev_unref(udev);
262         free(vendor);
263         free(product);
264
265         return ret;
266 }