6 #include <libubox/blobmsg_json.h>
7 #include <libubox/avl.h>
8 #include <libubox/avl-cmp.h>
11 #define DEFAULT_CONFIG "/etc/usb-mode.json"
15 struct blob_attr *data;
18 static int verbose = 0;
19 static const char *config_file = DEFAULT_CONFIG;
20 static struct blob_buf conf;
22 struct blob_attr **messages = NULL;
25 static struct avl_tree devices;
27 static struct libusb_context *usb;
28 static struct libusb_device **usbdevs;
31 static int hex2num(char c)
33 if (c >= '0' && c <= '9')
37 if (c >= 'A' && c <= 'F')
43 static int hex2byte(const char *hex)
58 static int hexstr2bin(const char *hex, char *buffer, int len)
60 const char *ipos = hex;
64 for (i = 0; i < len; i++) {
76 static bool convert_message(struct blob_attr *attr)
84 data = blobmsg_data(attr);
89 return !hexstr2bin(data, data, len / 2);
92 static int parse_config(void)
99 static const struct blobmsg_policy policy[__CONF_MAX] = {
100 [CONF_MESSAGES] = { .name = "messages", .type = BLOBMSG_TYPE_ARRAY },
101 [CONF_DEVICES] = { .name = "devices", .type = BLOBMSG_TYPE_TABLE },
103 struct blob_attr *tb[__CONF_MAX];
104 struct blob_attr *cur;
108 blobmsg_parse(policy, __CONF_MAX, tb, blob_data(conf.head), blob_len(conf.head));
109 if (!tb[CONF_MESSAGES] || !tb[CONF_DEVICES]) {
110 fprintf(stderr, "Configuration incomplete\n");
114 blobmsg_for_each_attr(cur, tb[CONF_MESSAGES], rem)
117 messages = calloc(n_messages, sizeof(*messages));
119 blobmsg_for_each_attr(cur, tb[CONF_MESSAGES], rem) {
120 if (!convert_message(cur)) {
121 fprintf(stderr, "Invalid data in message %d\n", n_messages);
124 messages[n_messages++] = cur;
127 blobmsg_for_each_attr(cur, tb[CONF_DEVICES], rem) {
128 dev = calloc(1, sizeof(*dev));
129 dev->avl.key = blobmsg_name(cur);
131 avl_insert(&devices, &dev->avl);
137 static int usage(const char *prog)
139 fprintf(stderr, "Usage: %s <command> <options>\n"
141 " -l List matching devices\n"
142 " -s Modeswitch matching devices\n"
145 " -v Verbose output\n"
146 " -c <file> Set configuration file to <file> (default: %s)\n"
147 "\n", prog, DEFAULT_CONFIG);
151 typedef void (*cmd_cb_t)(struct usbdev_data *data);
153 static struct blob_attr *
154 find_dev_data(struct usbdev_data *data, struct device *dev)
156 struct blob_attr *cur;
159 blobmsg_for_each_attr(cur, dev->data, rem) {
160 const char *name = blobmsg_name(cur);
164 if (!strcmp(blobmsg_name(cur), "*"))
167 next = strchr(name, '=');
172 if (!strncmp(name, "uMa", 3)) {
174 } else if (!strncmp(name, "uPr", 3)) {
176 } else if (!strncmp(name, "uSe", 3)) {
179 /* ignore unsupported scsi attributes */
183 if (!strcmp(val, next))
191 parse_interface_config(libusb_device *dev, struct usbdev_data *data)
193 struct libusb_config_descriptor *config;
194 const struct libusb_interface *iface;
195 const struct libusb_interface_descriptor *alt;
198 data->interface = -1;
199 if (libusb_get_config_descriptor(dev, 0, &config))
202 data->config = config;
203 if (!config->bNumInterfaces)
206 iface = &config->interface[0];
207 if (!iface->num_altsetting)
210 alt = &iface->altsetting[0];
211 data->interface = alt->bInterfaceNumber;
213 for (i = 0; i < alt->bNumEndpoints; i++) {
214 const struct libusb_endpoint_descriptor *ep = &alt->endpoint[i];
217 if (data->msg_endpoint && data->response_endpoint)
220 if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) !=
221 LIBUSB_TRANSFER_TYPE_BULK)
224 out = (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) ==
227 if (!data->msg_endpoint && out)
228 data->msg_endpoint = ep->bEndpointAddress;
229 if (!data->response_endpoint && !out)
230 data->response_endpoint = ep->bEndpointAddress;
234 static void iterate_devs(cmd_cb_t cb)
236 struct usbdev_data data;
243 for (i = 0; i < n_usbdevs; i++) {
244 memset(&data, 0, sizeof(data));
246 if (libusb_get_device_descriptor(usbdevs[i], &data.desc))
249 sprintf(data.idstr, "%04x:%04x", data.desc.idVendor, data.desc.idProduct);
251 dev = avl_find_element(&devices, data.idstr, dev, avl);
255 if (libusb_open(usbdevs[i], &data.devh))
258 libusb_get_string_descriptor_ascii(
259 data.devh, data.desc.iManufacturer,
260 (void *) data.mfg, sizeof(data.mfg));
261 libusb_get_string_descriptor_ascii(
262 data.devh, data.desc.iProduct,
263 (void *) data.prod, sizeof(data.prod));
264 libusb_get_string_descriptor_ascii(
265 data.devh, data.desc.iSerialNumber,
266 (void *) data.serial, sizeof(data.serial));
268 parse_interface_config(usbdevs[i], &data);
270 data.info = find_dev_data(&data, dev);
275 libusb_free_config_descriptor(data.config);
277 libusb_close(data.devh);
281 static void handle_list(struct usbdev_data *data)
283 fprintf(stderr, "Found device: %s (Manufacturer: \"%s\", Product: \"%s\", Serial: \"%s\")\n",
284 data->idstr, data->mfg, data->prod, data->serial);
287 int main(int argc, char **argv)
293 avl_init(&devices, avl_strcmp, false, NULL);
295 while ((ch = getopt(argc, argv, "lsc:v")) != -1) {
304 config_file = optarg;
310 return usage(argv[0]);
314 blob_buf_init(&conf, 0);
315 if (!blobmsg_add_json_from_file(&conf, config_file) ||
317 fprintf(stderr, "Failed to load config file\n");
321 ret = libusb_init(&usb);
323 fprintf(stderr, "Failed to initialize libusb: %s\n", libusb_error_name(ret));
327 n_usbdevs = libusb_get_device_list(usb, &usbdevs);
329 libusb_free_device_list(usbdevs, 1);