support Quanta and Blackberry modes
[project/usbmode.git] / main.c
diff --git a/main.c b/main.c
index c00b5b9..6cfe3b2 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <getopt.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <getopt.h>
 #include <stdbool.h>
+#include <ctype.h>
 
 #include <libubox/blobmsg_json.h>
 #include <libubox/avl.h>
 
 #include <libubox/blobmsg_json.h>
 #include <libubox/avl.h>
@@ -18,15 +19,77 @@ static int verbose = 0;
 static const char *config_file = DEFAULT_CONFIG;
 static struct blob_buf conf;
 
 static const char *config_file = DEFAULT_CONFIG;
 static struct blob_buf conf;
 
-static struct blob_attr **messages;
-static int n_messages;
+char **messages = NULL;
+int *message_len;
+int n_messages = 0;
 
 static struct avl_tree devices;
 
 
 static struct avl_tree devices;
 
-static struct libusb_context *usb;
+struct libusb_context *usb;
 static struct libusb_device **usbdevs;
 static int n_usbdevs;
 
 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 {
 static int parse_config(void)
 {
        enum {
@@ -53,9 +116,19 @@ static int parse_config(void)
                n_messages++;
 
        messages = calloc(n_messages, sizeof(*messages));
                n_messages++;
 
        messages = calloc(n_messages, sizeof(*messages));
+       message_len = calloc(n_messages, sizeof(*message_len));
        n_messages = 0;
        n_messages = 0;
-       blobmsg_for_each_attr(cur, tb[CONF_MESSAGES], rem)
-               messages[n_messages++] = cur;
+       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));
 
        blobmsg_for_each_attr(cur, tb[CONF_DEVICES], rem) {
            dev = calloc(1, sizeof(*dev));
@@ -120,6 +193,51 @@ find_dev_data(struct usbdev_data *data, struct device *dev)
        return NULL;
 }
 
        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;
 static void iterate_devs(cmd_cb_t cb)
 {
        struct usbdev_data data;
@@ -144,6 +262,8 @@ static void iterate_devs(cmd_cb_t cb)
                if (libusb_open(usbdevs[i], &data.devh))
                        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.iManufacturer,
                        (void *) data.mfg, sizeof(data.mfg));
@@ -154,10 +274,17 @@ static void iterate_devs(cmd_cb_t cb)
                        data.devh, data.desc.iSerialNumber,
                        (void *) data.serial, sizeof(data.serial));
 
                        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);
                data.info = find_dev_data(&data, dev);
                if (data.info)
                        cb(&data);
-               libusb_close(data.devh);
+
+               if (data.config)
+                       libusb_free_config_descriptor(data.config);
+
+               if (data.devh)
+                       libusb_close(data.devh);
        }
 }
 
        }
 }