5d135f24f55aa4e9a07b3423af5e80e3c40b586f
[project/usbmode.git] / main.c
1 #include <stdio.h>
2 #include <getopt.h>
3 #include <stdbool.h>
4
5 #include <libubox/blobmsg_json.h>
6 #include <libubox/avl.h>
7 #include <libubox/avl-cmp.h>
8 #include <libusb.h>
9
10 #define DEFAULT_CONFIG "/etc/usb-mode.json"
11
12 struct device {
13         struct avl_node avl;
14         struct blob_attr *data;
15 };
16
17 struct usbdev_data {
18         struct libusb_device_descriptor desc;
19         libusb_device_handle *devh;
20         struct blob_attr *info;
21
22         char idstr[10];
23         char mfg[128], prod[128], serial[128];
24 };
25
26 static int verbose = 0;
27 static const char *config_file = DEFAULT_CONFIG;
28 static struct blob_buf conf;
29
30 static struct blob_attr **messages;
31 static int n_messages;
32
33 static struct avl_tree devices;
34
35 static struct libusb_context *usb;
36 static struct libusb_device **usbdevs;
37 static int n_usbdevs;
38
39 static int parse_config(void)
40 {
41         enum {
42                 CONF_MESSAGES,
43                 CONF_DEVICES,
44                 __CONF_MAX
45         };
46         static const struct blobmsg_policy policy[__CONF_MAX] = {
47                 [CONF_MESSAGES] = { .name = "messages", .type = BLOBMSG_TYPE_ARRAY },
48                 [CONF_DEVICES] = { .name = "devices", .type = BLOBMSG_TYPE_TABLE },
49         };
50         struct blob_attr *tb[__CONF_MAX];
51         struct blob_attr *cur;
52         struct device *dev;
53         int rem;
54
55         blobmsg_parse(policy, __CONF_MAX, tb, blob_data(conf.head), blob_len(conf.head));
56         if (!tb[CONF_MESSAGES] || !tb[CONF_DEVICES]) {
57                 fprintf(stderr, "Configuration incomplete\n");
58                 return -1;
59         }
60
61         blobmsg_for_each_attr(cur, tb[CONF_MESSAGES], rem)
62                 n_messages++;
63
64         messages = calloc(n_messages, sizeof(*messages));
65         n_messages = 0;
66         blobmsg_for_each_attr(cur, tb[CONF_MESSAGES], rem)
67                 messages[n_messages++] = cur;
68
69         blobmsg_for_each_attr(cur, tb[CONF_DEVICES], rem) {
70             dev = calloc(1, sizeof(*dev));
71             dev->avl.key = blobmsg_name(cur);
72             dev->data = cur;
73             avl_insert(&devices, &dev->avl);
74         }
75
76         return 0;
77 }
78
79 static int usage(const char *prog)
80 {
81         fprintf(stderr, "Usage: %s <command> <options>\n"
82                 "Commands:\n"
83                 "       -l              List matching devices\n"
84                 "       -s              Modeswitch matching devices\n"
85                 "\n"
86                 "Options:\n"
87                 "       -v              Verbose output\n"
88                 "       -c <file>       Set configuration file to <file> (default: %s)\n"
89                 "\n", prog, DEFAULT_CONFIG);
90         return 1;
91 }
92
93 typedef void (*cmd_cb_t)(struct usbdev_data *data);
94
95 static struct blob_attr *
96 find_dev_data(struct usbdev_data *data, struct device *dev)
97 {
98         struct blob_attr *cur;
99         int rem;
100
101         blobmsg_for_each_attr(cur, dev->data, rem) {
102                 const char *name = blobmsg_name(cur);
103                 const char *next;
104                 char *val;
105
106                 if (!strcmp(blobmsg_name(cur), "*"))
107                         return cur;
108
109                 next = strchr(name, '=');
110                 if (!next)
111                         continue;
112
113                 next++;
114                 if (!strncmp(name, "uMa", 3)) {
115                         val = data->mfg;
116                 } else if (!strncmp(name, "uPr", 3)) {
117                         val = data->prod;
118                 } else if (!strncmp(name, "uSe", 3)) {
119                         val = data->serial;
120                 } else {
121                         /* ignore unsupported scsi attributes */
122                         return cur;
123                 }
124
125                 if (!strcmp(val, next))
126                         return cur;
127         }
128
129         return NULL;
130 }
131
132 static void iterate_devs(cmd_cb_t cb)
133 {
134         struct usbdev_data data;
135         struct device *dev;
136         int i;
137
138         if (!cb)
139                 return;
140
141         for (i = 0; i < n_usbdevs; i++) {
142                 memset(&data, 0, sizeof(data));
143
144                 if (libusb_get_device_descriptor(usbdevs[i], &data.desc))
145                         continue;
146
147                 sprintf(data.idstr, "%04x:%04x", data.desc.idVendor, data.desc.idProduct);
148
149                 dev = avl_find_element(&devices, data.idstr, dev, avl);
150                 if (!dev)
151                         continue;
152
153                 if (libusb_open(usbdevs[i], &data.devh))
154                         continue;
155
156                 libusb_get_string_descriptor_ascii(
157                         data.devh, data.desc.iManufacturer,
158                         (void *) data.mfg, sizeof(data.mfg));
159                 libusb_get_string_descriptor_ascii(
160                         data.devh, data.desc.iProduct,
161                         (void *) data.prod, sizeof(data.prod));
162                 libusb_get_string_descriptor_ascii(
163                         data.devh, data.desc.iSerialNumber,
164                         (void *) data.serial, sizeof(data.serial));
165
166                 data.info = find_dev_data(&data, dev);
167                 if (data.info)
168                         cb(&data);
169                 libusb_close(data.devh);
170         }
171 }
172
173 static void handle_list(struct usbdev_data *data)
174 {
175         fprintf(stderr, "Found device: %s (Manufacturer: \"%s\", Product: \"%s\", Serial: \"%s\")\n",
176                 data->idstr, data->mfg, data->prod, data->serial);
177 }
178
179 static void handle_switch(struct usbdev_data *data)
180 {
181 }
182
183 int main(int argc, char **argv)
184 {
185         cmd_cb_t cb = NULL;
186         int ret;
187         int ch;
188
189         avl_init(&devices, avl_strcmp, false, NULL);
190
191         while ((ch = getopt(argc, argv, "lsc:v")) != -1) {
192                 switch (ch) {
193                 case 'l':
194                         cb = handle_list;
195                         break;
196                 case 's':
197                         cb = handle_switch;
198                         break;
199                 case 'c':
200                         config_file = optarg;
201                         break;
202                 case 'v':
203                         verbose++;
204                         break;
205                 default:
206                         return usage(argv[0]);
207                 }
208         }
209
210         blob_buf_init(&conf, 0);
211         if (!blobmsg_add_json_from_file(&conf, config_file) ||
212             parse_config()) {
213                 fprintf(stderr, "Failed to load config file\n");
214                 return 1;
215         }
216
217         ret = libusb_init(&usb);
218         if (ret) {
219                 fprintf(stderr, "Failed to initialize libusb: %s\n", libusb_error_name(ret));
220                 return 1;
221         }
222
223         n_usbdevs = libusb_get_device_list(usb, &usbdevs);
224         iterate_devs(cb);
225         libusb_free_device_list(usbdevs, 1);
226         libusb_exit(usb);
227
228         return 0;
229 }