chiark / gitweb /
e38c6b9d268c3afd5767873430d708cff4bb00b4
[elogind.git] / udev / udev-builtin-hwdb.c
1 /*
2  * usb-db, pci-db - lookup vendor/product database
3  *
4  * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net>
5  * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (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 General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <stdio.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <inttypes.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27
28 #include "udev.h"
29
30 static int get_id_attr(
31         struct udev_device *parent,
32         const char *name,
33         uint16_t *value) {
34
35         const char *t;
36         unsigned u;
37
38         if (!(t = udev_device_get_sysattr_value(parent, name))) {
39                 fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name);
40                 return -1;
41         }
42
43         if (!strncmp(t, "0x", 2))
44                 t += 2;
45
46         if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) {
47                 fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent));
48                 return -1;
49         }
50
51         *value = (uint16_t) u;
52         return 0;
53 }
54
55 static int get_vid_pid(
56         struct udev_device *parent,
57         const char *vendor_attr,
58         const char *product_attr,
59         uint16_t *vid,
60         uint16_t *pid) {
61
62         if (get_id_attr(parent, vendor_attr, vid) < 0)
63                 return -1;
64         else if (*vid <= 0) {
65                 fprintf(stderr, "Invalid vendor id.\n");
66                 return -1;
67         }
68
69         if (get_id_attr(parent, product_attr, pid) < 0)
70                 return -1;
71
72         return 0;
73 }
74
75 static void rstrip(char *n) {
76         size_t i;
77
78         for (i = strlen(n); i > 0 && isspace(n[i-1]); i--)
79                 n[i-1] = 0;
80 }
81
82 #define HEXCHARS "0123456789abcdefABCDEF"
83 #define WHITESPACE " \t\n\r"
84 static int lookup_vid_pid(const char *database,
85                           uint16_t vid, uint16_t pid,
86                           char **vendor, char **product)
87 {
88
89         FILE *f;
90         int ret = -1;
91         int found_vendor = 0;
92         char *line = NULL;
93
94         *vendor = *product = NULL;
95
96         if (!(f = fopen(database, "rme"))) {
97                 fprintf(stderr, "Failed to open database file '%s': %s\n", database, strerror(errno));
98                 return -1;
99         }
100
101         for (;;) {
102                 size_t n;
103
104                 if (line) {
105                         free(line);
106                         line = NULL;
107                 }
108
109                 if (getline(&line, &n, f) < 0)
110                         break;
111
112                 rstrip(line);
113
114                 if (line[0] == '#' || line[0] == 0)
115                         continue;
116
117                 if (strspn(line, HEXCHARS) == 4) {
118                         unsigned u;
119
120                         if (found_vendor)
121                                 break;
122
123                         if (sscanf(line, "%04x", &u) == 1 && u == vid) {
124                                 char *t;
125
126                                 t = line+4;
127                                 t += strspn(t, WHITESPACE);
128
129                                 if (!(*vendor = strdup(t))) {
130                                         fprintf(stderr, "Out of memory.\n");
131                                         goto finish;
132                                 }
133
134                                 found_vendor = 1;
135                         }
136
137                         continue;
138                 }
139
140                 if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) {
141                         unsigned u;
142
143                         if (sscanf(line+1, "%04x", &u) == 1 && u == pid) {
144                                 char *t;
145
146                                 t = line+5;
147                                 t += strspn(t, WHITESPACE);
148
149                                 if (!(*product = strdup(t))) {
150                                         fprintf(stderr, "Out of memory.\n");
151                                         goto finish;
152                                 }
153
154                                 break;
155                         }
156                 }
157         }
158
159         ret = 0;
160
161 finish:
162         free(line);
163         fclose(f);
164
165         if (ret < 0) {
166                 free(*product);
167                 free(*vendor);
168
169                 *product = *vendor = NULL;
170         }
171
172         return ret;
173 }
174
175 static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype)
176 {
177         const char *str;
178
179         str = udev_device_get_subsystem(dev);
180         if (str == NULL)
181                 goto try_parent;
182         if (strcmp(str, subsys) != 0)
183                 goto try_parent;
184
185         if (devtype != NULL) {
186                 str = udev_device_get_devtype(dev);
187                 if (str == NULL)
188                         goto try_parent;
189                 if (strcmp(str, devtype) != 0)
190                         goto try_parent;
191         }
192         return dev;
193 try_parent:
194         return udev_device_get_parent_with_subsystem_devtype(dev, subsys, devtype);
195 }
196
197
198 static int builtin_db(struct udev_device *dev, bool test,
199                       const char *database,
200                       const char *vendor_attr, const char *product_attr,
201                       const char *subsys, const char *devtype)
202 {
203         struct udev_device *parent;
204         uint16_t vid = 0, pid = 0;
205         char *vendor = NULL, *product = NULL;
206
207         parent = find_device(dev, subsys, devtype);
208         if (!parent) {
209                 fprintf(stderr, "Failed to find device.\n");
210                 goto finish;
211         }
212
213         if (get_vid_pid(parent, vendor_attr, product_attr, &vid, &pid) < 0)
214                 goto finish;
215
216         if (lookup_vid_pid(database, vid, pid, &vendor, &product) < 0)
217                 goto finish;
218
219         if (vendor)
220                 udev_builtin_add_property(dev, test, "ID_VENDOR_FROM_DATABASE", vendor);
221         if (product)
222                 udev_builtin_add_property(dev, test, "ID_MODEL_FROM_DATABASE", product);
223
224 finish:
225         free(vendor);
226         free(product);
227         return 0;
228 }
229
230 static int builtin_usb_db(struct udev_device *dev, int argc, char *argv[], bool test)
231 {
232         return builtin_db(dev, test, USB_DATABASE, "idVendor", "idProduct", "usb", "usb_device");
233 }
234
235 static int builtin_pci_db(struct udev_device *dev, int argc, char *argv[], bool test)
236 {
237         return builtin_db(dev, test, PCI_DATABASE, "vendor", "device", "pci", NULL);
238 }
239
240 const struct udev_builtin udev_builtin_usb_db = {
241         .name = "usb-db",
242         .cmd = builtin_usb_db,
243         .help = "USB vendor/product database",
244         .run_once = true,
245 };
246
247 const struct udev_builtin udev_builtin_pci_db = {
248         .name = "pci-db",
249         .cmd = builtin_pci_db,
250         .help = "PCI vendor/product database",
251         .run_once = true,
252 };