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