X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fusbmode.git;a=blobdiff_plain;f=main.c;h=6cfe3b22bea35ff4aaa96ba0797a1810b91babaa;hp=3fed7e60ca234b92048f733711dcc95f33b4f413;hb=HEAD;hpb=a1545f995346f16f3df657b5f494c699890bd1f4 diff --git a/main.c b/main.c index 3fed7e6..6cfe3b2 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,343 @@ #include +#include +#include +#include + +#include +#include +#include +#include "switch.h" + +#define DEFAULT_CONFIG "/etc/usb-mode.json" + +struct device { + struct avl_node avl; + struct blob_attr *data; +}; + +static int verbose = 0; +static const char *config_file = DEFAULT_CONFIG; +static struct blob_buf conf; + +char **messages = NULL; +int *message_len; +int n_messages = 0; + +static struct avl_tree devices; + +struct libusb_context *usb; +static struct libusb_device **usbdevs; +static int n_usbdevs; + +static int hex2num(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + c = toupper(c); + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; +} + +static int hex2byte(const char *hex) +{ + int a, b; + + a = hex2num(*hex++); + if (a < 0) + return -1; + + b = hex2num(*hex++); + if (b < 0) + return -1; + + return (a << 4) | b; +} + +static int hexstr2bin(const char *hex, char *buffer, int len) +{ + const char *ipos = hex; + char *opos = buffer; + int i, a; + + for (i = 0; i < len; i++) { + a = hex2byte(ipos); + if (a < 0) + return -1; + + *opos++ = a; + ipos += 2; + } + + return 0; +} + +static int convert_message(struct blob_attr *attr) +{ + char *data; + int len; + + data = blobmsg_data(attr); + len = strlen(data); + if (len % 2) + return -1; + + if (hexstr2bin(data, data, len / 2)) + return -1; + + return len / 2; +} + +static int parse_config(void) +{ + enum { + CONF_MESSAGES, + CONF_DEVICES, + __CONF_MAX + }; + static const struct blobmsg_policy policy[__CONF_MAX] = { + [CONF_MESSAGES] = { .name = "messages", .type = BLOBMSG_TYPE_ARRAY }, + [CONF_DEVICES] = { .name = "devices", .type = BLOBMSG_TYPE_TABLE }, + }; + struct blob_attr *tb[__CONF_MAX]; + struct blob_attr *cur; + struct device *dev; + int rem; + + blobmsg_parse(policy, __CONF_MAX, tb, blob_data(conf.head), blob_len(conf.head)); + if (!tb[CONF_MESSAGES] || !tb[CONF_DEVICES]) { + fprintf(stderr, "Configuration incomplete\n"); + return -1; + } + + blobmsg_for_each_attr(cur, tb[CONF_MESSAGES], rem) + n_messages++; + + messages = calloc(n_messages, sizeof(*messages)); + message_len = calloc(n_messages, sizeof(*message_len)); + n_messages = 0; + blobmsg_for_each_attr(cur, tb[CONF_MESSAGES], rem) { + int len = convert_message(cur); + + if (len < 0) { + fprintf(stderr, "Invalid data in message %d\n", n_messages); + return -1; + } + + message_len[n_messages] = len; + messages[n_messages++] = blobmsg_data(cur); + } + + blobmsg_for_each_attr(cur, tb[CONF_DEVICES], rem) { + dev = calloc(1, sizeof(*dev)); + dev->avl.key = blobmsg_name(cur); + dev->data = cur; + avl_insert(&devices, &dev->avl); + } + + return 0; +} + +static int usage(const char *prog) +{ + fprintf(stderr, "Usage: %s \n" + "Commands:\n" + " -l List matching devices\n" + " -s Modeswitch matching devices\n" + "\n" + "Options:\n" + " -v Verbose output\n" + " -c Set configuration file to (default: %s)\n" + "\n", prog, DEFAULT_CONFIG); + return 1; +} + +typedef void (*cmd_cb_t)(struct usbdev_data *data); + +static struct blob_attr * +find_dev_data(struct usbdev_data *data, struct device *dev) +{ + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, dev->data, rem) { + const char *name = blobmsg_name(cur); + const char *next; + char *val; + + if (!strcmp(blobmsg_name(cur), "*")) + return cur; + + next = strchr(name, '='); + if (!next) + continue; + + next++; + if (!strncmp(name, "uMa", 3)) { + val = data->mfg; + } else if (!strncmp(name, "uPr", 3)) { + val = data->prod; + } else if (!strncmp(name, "uSe", 3)) { + val = data->serial; + } else { + /* ignore unsupported scsi attributes */ + return cur; + } + + if (!strcmp(val, next)) + return cur; + } + + return NULL; +} + +static void +parse_interface_config(libusb_device *dev, struct usbdev_data *data) +{ + struct libusb_config_descriptor *config; + const struct libusb_interface *iface; + const struct libusb_interface_descriptor *alt; + int i; + + data->interface = -1; + if (libusb_get_config_descriptor(dev, 0, &config)) + return; + + data->config = config; + if (!config->bNumInterfaces) + return; + + iface = &config->interface[0]; + if (!iface->num_altsetting) + return; + + alt = &iface->altsetting[0]; + data->interface = alt->bInterfaceNumber; + data->dev_class = alt->bInterfaceClass; + + for (i = 0; i < alt->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *ep = &alt->endpoint[i]; + bool out = false; + + if (data->msg_endpoint && data->response_endpoint) + break; + + if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) != + LIBUSB_TRANSFER_TYPE_BULK) + continue; + + out = (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == + LIBUSB_ENDPOINT_OUT; + + if (!data->msg_endpoint && out) + data->msg_endpoint = ep->bEndpointAddress; + if (!data->response_endpoint && !out) + data->response_endpoint = ep->bEndpointAddress; + } +} + +static void iterate_devs(cmd_cb_t cb) +{ + struct usbdev_data data; + struct device *dev; + int i; + + if (!cb) + return; + + for (i = 0; i < n_usbdevs; i++) { + memset(&data, 0, sizeof(data)); + + if (libusb_get_device_descriptor(usbdevs[i], &data.desc)) + continue; + + sprintf(data.idstr, "%04x:%04x", data.desc.idVendor, data.desc.idProduct); + + dev = avl_find_element(&devices, data.idstr, dev, avl); + if (!dev) + continue; + + if (libusb_open(usbdevs[i], &data.devh)) + continue; + + data.dev = usbdevs[i]; + + libusb_get_string_descriptor_ascii( + data.devh, data.desc.iManufacturer, + (void *) data.mfg, sizeof(data.mfg)); + libusb_get_string_descriptor_ascii( + data.devh, data.desc.iProduct, + (void *) data.prod, sizeof(data.prod)); + libusb_get_string_descriptor_ascii( + data.devh, data.desc.iSerialNumber, + (void *) data.serial, sizeof(data.serial)); + + parse_interface_config(usbdevs[i], &data); + + data.info = find_dev_data(&data, dev); + if (data.info) + cb(&data); + + if (data.config) + libusb_free_config_descriptor(data.config); + + if (data.devh) + libusb_close(data.devh); + } +} + +static void handle_list(struct usbdev_data *data) +{ + fprintf(stderr, "Found device: %s (Manufacturer: \"%s\", Product: \"%s\", Serial: \"%s\")\n", + data->idstr, data->mfg, data->prod, data->serial); +} int main(int argc, char **argv) { + cmd_cb_t cb = NULL; + int ret; + int ch; + + avl_init(&devices, avl_strcmp, false, NULL); + + while ((ch = getopt(argc, argv, "lsc:v")) != -1) { + switch (ch) { + case 'l': + cb = handle_list; + break; + case 's': + cb = handle_switch; + break; + case 'c': + config_file = optarg; + break; + case 'v': + verbose++; + break; + default: + return usage(argv[0]); + } + } + + blob_buf_init(&conf, 0); + if (!blobmsg_add_json_from_file(&conf, config_file) || + parse_config()) { + fprintf(stderr, "Failed to load config file\n"); + return 1; + } + + ret = libusb_init(&usb); + if (ret) { + fprintf(stderr, "Failed to initialize libusb: %s\n", libusb_error_name(ret)); + return 1; + } + + n_usbdevs = libusb_get_device_list(usb, &usbdevs); + iterate_devs(cb); + libusb_free_device_list(usbdevs, 1); + libusb_exit(usb); + return 0; }