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 }