add infrastructure for switching modes
[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 enum {
180         DATA_MODE,
181         DATA_MSG,
182         DATA_MSG2,
183         DATA_MSG3,
184         __DATA_MAX
185 };
186
187 static void handle_generic(struct usbdev_data *data, struct blob_attr **tb)
188 {
189         fprintf(stderr, "Do generic switch!\n");
190 }
191
192 static void handle_huawei(struct usbdev_data *data, struct blob_attr **tb)
193 {
194         /* TODO */
195 }
196
197 static void handle_sierra(struct usbdev_data *data, struct blob_attr **tb)
198 {
199         /* TODO */
200 }
201
202 static void handle_sony(struct usbdev_data *data, struct blob_attr **tb)
203 {
204         /* TODO */
205 }
206
207 static void handle_qisda(struct usbdev_data *data, struct blob_attr **tb)
208 {
209         /* TODO */
210 }
211
212 static void handle_gct(struct usbdev_data *data, struct blob_attr **tb)
213 {
214         /* TODO */
215 }
216
217 static void handle_kobil(struct usbdev_data *data, struct blob_attr **tb)
218 {
219         /* TODO */
220 }
221
222 static void handle_sequans(struct usbdev_data *data, struct blob_attr **tb)
223 {
224         /* TODO */
225 }
226
227 static void handle_mobile_action(struct usbdev_data *data, struct blob_attr **tb)
228 {
229         /* TODO */
230 }
231
232 static void handle_cisco(struct usbdev_data *data, struct blob_attr **tb)
233 {
234         /* TODO */
235 }
236
237 enum {
238         MODE_GENERIC,
239         MODE_HUAWEI,
240         MODE_SIERRA,
241         MODE_SONY,
242         MODE_QISDA,
243         MODE_GCT,
244         MODE_KOBIL,
245         MODE_SEQUANS,
246         MODE_MOBILE_ACTION,
247         MODE_CISCO,
248         __MODE_MAX
249 };
250
251 static const struct {
252         const char *name;
253         void (*cb)(struct usbdev_data *data, struct blob_attr **tb);
254 } modeswitch_cb[__MODE_MAX] = {
255         [MODE_GENERIC] = { "Generic", handle_generic },
256         [MODE_HUAWEI] = { "Huawei", handle_huawei },
257         [MODE_SIERRA] = { "Sierra", handle_sierra },
258         [MODE_SONY] = { "Sony", handle_sony },
259         [MODE_QISDA] = { "Qisda", handle_qisda },
260         [MODE_GCT] = { "GCT", handle_gct },
261         [MODE_KOBIL] = { "Kobil", handle_kobil },
262         [MODE_SEQUANS] = { "Sequans", handle_sequans },
263         [MODE_MOBILE_ACTION] = { "MobileAction", handle_mobile_action },
264         [MODE_CISCO] = { "Cisco", handle_cisco },
265 };
266
267 static void handle_switch(struct usbdev_data *data)
268 {
269         static const struct blobmsg_policy data_policy[__DATA_MAX] = {
270                 [DATA_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_STRING },
271                 [DATA_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_INT32 },
272                 [DATA_MSG2] = { .name = "msg2", .type = BLOBMSG_TYPE_INT32 },
273                 [DATA_MSG3] = { .name = "msg3", .type = BLOBMSG_TYPE_INT32 },
274         };
275         struct blob_attr *tb[__DATA_MAX];
276         int mode = MODE_GENERIC;
277
278         blobmsg_parse(data_policy, __DATA_MAX, tb, blobmsg_data(data->info), blobmsg_data_len(data->info));
279
280         if (tb[DATA_MODE]) {
281                 const char *modestr;
282                 int i;
283
284                 modestr = blobmsg_data(tb[DATA_MODE]);
285                 for (i = 0; i < __MODE_MAX; i++) {
286                         if (strcmp(modeswitch_cb[i].name, modestr) != 0)
287                                 continue;
288
289                         mode = i;
290                         break;
291                 }
292         }
293
294         modeswitch_cb[mode].cb(data, tb);
295 }
296
297 int main(int argc, char **argv)
298 {
299         cmd_cb_t cb = NULL;
300         int ret;
301         int ch;
302
303         avl_init(&devices, avl_strcmp, false, NULL);
304
305         while ((ch = getopt(argc, argv, "lsc:v")) != -1) {
306                 switch (ch) {
307                 case 'l':
308                         cb = handle_list;
309                         break;
310                 case 's':
311                         cb = handle_switch;
312                         break;
313                 case 'c':
314                         config_file = optarg;
315                         break;
316                 case 'v':
317                         verbose++;
318                         break;
319                 default:
320                         return usage(argv[0]);
321                 }
322         }
323
324         blob_buf_init(&conf, 0);
325         if (!blobmsg_add_json_from_file(&conf, config_file) ||
326             parse_config()) {
327                 fprintf(stderr, "Failed to load config file\n");
328                 return 1;
329         }
330
331         ret = libusb_init(&usb);
332         if (ret) {
333                 fprintf(stderr, "Failed to initialize libusb: %s\n", libusb_error_name(ret));
334                 return 1;
335         }
336
337         n_usbdevs = libusb_get_device_list(usb, &usbdevs);
338         iterate_devs(cb);
339         libusb_free_device_list(usbdevs, 1);
340         libusb_exit(usb);
341
342         return 0;
343 }