diff --git a/Makefile b/Makefile index 44cad7e..fd4c2bc 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ -.PHONY: honeywell-config clean +.PHONY: honeywell-config debug clean honeywell-config: gcc -Wall -Werror -o honeywell-config honeywell-config.c -lusb-1.0 +debug: + gcc -Wall -Werror -O0 -ggdb3 -o honeywell-config honeywell-config.c -lusb-1.0 + clean: rm -f honeywell-config diff --git a/honeywell-config.c b/honeywell-config.c index e177b51..14ac575 100644 --- a/honeywell-config.c +++ b/honeywell-config.c @@ -1,7 +1,10 @@ +#include +#include #include #include #include #include + #include @@ -10,9 +13,19 @@ uint16_t product_id = 0; uint8_t reset = 0; uint8_t oneline = 0; uint8_t debug = 0; +int infd = 0; +int outfd = 1; +uint8_t explicit_in = 0; + +libusb_device_handle *devh = NULL; +uint8_t endpoint = 0; #define USBERR(msg, rc) { dprintf(2, "%s: %s\n", msg, libusb_strerror(rc)); } -#define DEBUG(...) { if (debug) { dprintf(2, __VA_ARGS__); } } +#define DEBUG(...) { if (debug >= 1) { dprintf(2, __VA_ARGS__); } } +#define DEBUG2(...) { if (debug >= 2) { dprintf(2, __VA_ARGS__); } } +#define DEBUG3(...) { if (debug >= 3) { dprintf(2, __VA_ARGS__); } } +#define HEXBUF(msg, buf, n) { if (debug >= 3) { dprintf(2, "%s:", msg); for (uint8_t *__h = buf; __h < buf+n;++__h) { dprintf(2, " %02x", *__h); } dprintf(2, "\n"); } } + uint32_t vidpid[] = { 0x0c2e0b01, @@ -21,23 +34,186 @@ uint32_t vidpid[] = { }; -int config(int argc, char **args) { +struct tokenizer { + char tokbuf[1024]; + char *tok; + uint8_t eot; + int (*cb)(const char *tok, uint8_t last); +}; + + +void tok_init(struct tokenizer *t, int (*cb)(const char *tok, uint8_t last)) { + memset(t->tokbuf, 0, 1024); + t->tok = t->tokbuf; + t->eot = 0; + t->cb = cb; +} + +int tok_read(struct tokenizer *t, char *buf, size_t n) { + char *start = buf; + char *end = start; + while (!t->eot) { + // non-final tokens: terminated by ; + end = strchr(start, ';'); + if (end) { + if (end > start) { + for (char *c = start; c < end; ++c) { + if (isprint(*c)) { + *(t->tok++) = *c; + } + } + } + *(t->tok) = 0; + if (t->cb(t->tokbuf, 0)) { + return 1; + } + t->tok = t->tokbuf; + *(t->tok) = 0; + start = end + 1; + continue; + } + // last token: terminated by . + end = strchr(start, '.'); + if (end) { + t->eot = 1; + if (end > start) { + for (char *c = start; c < end; ++c) { + if (isprint(*c)) { + *(t->tok++) = *c; + } + } + } + *(t->tok) = 0; + if (t->cb(t->tokbuf, 1)) { + return 1; + } + t->tok = t->tokbuf; + *(t->tok) = 0; + break; + } + // incomplete tokens continued in the next message + end = strchr(start, '?'); + if (end) { + *end = 0; + } + for (char *c = start; *c != 0; ++c) { + if (isprint(*c)) { + *(t->tok++) = *c; + } + } + *(t->tok) = 0; + break; + } + return 0; +} + + +struct tokenizer in_tok; +struct tokenizer out_tok; + + +int print_token(const char *tok, uint8_t last) { + int rc; + if (oneline && !last) { + rc = dprintf(outfd, "%s;", tok); + } else { + rc = dprintf(outfd, "%s.\n", tok); + } + return rc < 0; +} + + +int send_token(const char *tok, uint8_t last) { uint8_t cmd[64] = { 0 }; - char tokbuf[1024] = { 0 }; + uint8_t *cmdptr = cmd + 5; + int rc; + memset(cmdptr, 0, 59); + for (uint8_t i = 0; i < strlen(tok); ++i) { + if (isprint(tok[i])) { + *(cmdptr++) = tok[i]; + } + } + *cmdptr = '.'; + cmd[0] = 0xfd; + cmd[1] = cmdptr - cmd - 1; + cmd[2] = 0x16; + cmd[3] = 0x4d; + cmd[4] = 0x0d; + HEXBUF(">", cmd, 64); + rc = libusb_control_transfer(devh, 0x21, 0x09, 0x02fd, 1, cmd, 64, 0); + if (rc < 0) { + USBERR("failed to send control message", rc); + return 1; + } else { + DEBUG2("> %s\n", cmd+5); + } + int tx; + tok_init(&out_tok, print_token); + // Repeat until a "." (EOT) is encountered in the response. + while (!out_tok.eot) { + rc = libusb_interrupt_transfer(devh, endpoint, cmd, 64, &tx, 1000); + if (rc) { + USBERR("failed to receive response from device", rc); + return 1; + } + HEXBUF("<", cmd, tx); + // skip first 5 bytes (report id, length, 3*AIMID) + DEBUG2("< %s\n", cmd+5); + if (tok_read(&out_tok, (char *) cmd+5, tx-5)) { + return 1; + } + } + return 0; +} + + +int config(int argc, char **args) { + // If commands are provided on the CLI, execute only those + if (argc > 0) { + for (int i = 0; i < argc; ++i) { + if (send_token(args[i], i >= argc-1)) { + return 1; + } + } + return 0; + } + // If input is a tty, dump config + // Can be overwritten with "-i -" on the cli + if (isatty(infd) && !explicit_in) { + return send_token("?", 1); + } + // Otherwhise parse tokens from stdin + char buf[65]; + ssize_t n = 1; + tok_init(&in_tok, send_token); + while (n) { + n = read(infd, buf, 64); + if (n < 0) { + perror("failed to read from input file"); + return 1; + } + buf[n] = 0; + if (tok_read(&in_tok, buf, n)) { + return 1; + } + } + return 0; +} + + +int usb(int argc, char **args) { libusb_context *ctx = NULL; libusb_device *dev = NULL; - libusb_device_handle *devh = NULL; struct libusb_device_descriptor devd = { 0 }; struct libusb_config_descriptor *devc = NULL; const struct libusb_interface *devi = NULL; const struct libusb_interface_descriptor *devid = NULL; const struct libusb_endpoint_descriptor *deved = NULL; char devdesc[256] = { 0 }; - int rc = 0; + int rc = 0, fail = 0; uint8_t ints[32] = { 0 }; uint8_t inti = 0; - uint8_t endpoint = 0; // // Find and open the device @@ -45,14 +221,16 @@ int config(int argc, char **args) { rc = libusb_init(&ctx); if (!ctx) { USBERR("failed to initialize libusb", rc); - return 1; + fail = 1; + goto fail; } if (vendor_id || product_id) { devh = libusb_open_device_with_vid_pid(ctx, vendor_id, product_id); if (!devh) { dprintf(2, "device %04x:%04x not found\n", vendor_id, product_id); - return 1; + fail = 1; + goto fail; } } else { devh = NULL; @@ -61,7 +239,8 @@ int config(int argc, char **args) { } if (!devh) { dprintf(2, "default devices not found, please provide vendor and product id\n"); - return 1; + fail = 1; + goto fail; } } @@ -70,8 +249,34 @@ int config(int argc, char **args) { rc = libusb_get_device_descriptor(dev, &devd); if (rc) { USBERR("failed to get device descriptor", rc); - return 1; + fail = 1; + goto fail; } + + // Print device info + rc = libusb_get_string_descriptor_ascii(devh, devd.iManufacturer, (uint8_t *) devdesc, 256); + if (rc < 0) { + USBERR("failed to get manufacturer string descriptor", rc); + fail = 1; + goto fail; + } + DEBUG("Manufacturer: %s\n", devdesc); + rc = libusb_get_string_descriptor_ascii(devh, devd.iProduct, (uint8_t *) devdesc, 256); + if (rc < 0) { + USBERR("failed to get product string descriptor", rc); + fail = 1; + goto fail; + } + DEBUG("Product: %s\n", devdesc); + rc = libusb_get_string_descriptor_ascii(devh, devd.iSerialNumber, (uint8_t *) devdesc, 256); + if (rc < 0) { + USBERR("failed to get serial number string descriptor", rc); + fail = 1; + goto fail; + } + DEBUG("Serial Number: %s\n", devdesc); + + // Iterate all configurations, interfaces and endpoints to find the REM/EZConfig endpoint for (uint8_t c = 0; c < devd.bNumConfigurations; ++c) { libusb_free_config_descriptor(devc); rc = libusb_get_config_descriptor(dev, c, &devc); @@ -84,9 +289,9 @@ int config(int argc, char **args) { USBERR("failed to get config string descriptor", rc); continue; } - DEBUG("- configuration %d: %s\n", c, devdesc); + DEBUG2("- configuration %d: %s\n", c, devdesc); for (uint8_t i = 0; i < devc->bNumInterfaces; ++i) { - DEBUG(" - interface %d\n", i); + DEBUG2(" - interface %d\n", i); devi = &(devc->interface[i]); for (uint8_t a = 0; a < devi->num_altsetting; ++a) { devid = &(devi->altsetting[a]); @@ -95,13 +300,13 @@ int config(int argc, char **args) { USBERR("failed to get interface string descriptor", rc); continue; } - DEBUG(" - altsetting %d: interface=%d, desc=%s class=%d subclass=%d protocol=%d\n", - devid->bAlternateSetting, devid->bInterfaceNumber, devdesc, - devid->bInterfaceClass, devid->bInterfaceSubClass, devid->bInterfaceProtocol); + DEBUG2(" - altsetting %d: interface=%d, desc=%s class=%d subclass=%d protocol=%d\n", + devid->bAlternateSetting, devid->bInterfaceNumber, devdesc, + devid->bInterfaceClass, devid->bInterfaceSubClass, devid->bInterfaceProtocol); for (uint8_t e = 0; e < devid->bNumEndpoints; ++e) { deved = &(devid->endpoint[e]); char type = "csbi"[deved->bmAttributes & 0b00000011]; - DEBUG(" - endpoint %x: t=%c\n", deved->bEndpointAddress, type); + DEBUG2(" - endpoint %x: t=%c\n", deved->bEndpointAddress, type); // build a list of interfaces so that we can claim all of them ints[inti++] = devid->bInterfaceNumber; // endpoint selection: @@ -116,7 +321,7 @@ int config(int argc, char **args) { } } - DEBUG("chose endpoint %x\n", endpoint); + DEBUG2("chose endpoint %x\n", endpoint); // // Disconnect usbhid driver from both device interfaces @@ -128,13 +333,15 @@ int config(int argc, char **args) { rc = libusb_kernel_driver_active(devh, ints[i]); if (rc < 0) { USBERR("failed to query kernel driver state", rc); - return 1; + fail = 1; + goto fail; } if (rc > 0) { rc = libusb_detach_kernel_driver(devh, ints[i]); if (rc) { USBERR("failed to detach kernel driver", rc); - return 1; + fail = 1; + goto fail; } } } @@ -144,102 +351,40 @@ int config(int argc, char **args) { rc = libusb_claim_interface(devh, ints[i]); if (rc) { USBERR("failed to claim interface", rc); - return 1; + fail = 1; + goto fail; } } // // Prepare and send a message on the control channel for each config argument // - for (int i = 0; i < argc; ++i) { - memset(cmd+5, 0, 59); - cmd[0] = 0xfd; - cmd[1] = strlen(args[i])+3; - cmd[2] = 0x16; - cmd[3] = 0x4d; - cmd[4] = 0x0d; - memcpy(cmd+5, args[i], strlen(args[i])); - rc = libusb_control_transfer(devh, 0x21, 0x09, 0x02fd, 1, cmd, 64, 0); - if (rc < 0) { - USBERR("failed to send control message", rc); - } else { - DEBUG("> %s\n", args[i]); - } - int tx; - tokbuf[64] = 0; - char *start, *end, *tok = tokbuf; - uint8_t eot = 0; - // Repeat until a "." (EOT) is encountered in the response. - while (!eot) { - rc = libusb_interrupt_transfer(devh, endpoint, cmd, 64, &tx, 1000); - if (rc) { - USBERR("failed to receive response from device", rc); - break; - } - // strip 2-byte header and AIMID - start = (char *) cmd+5; - DEBUG("< %s\n", start); - while (1) { - // non-final tokens: terminated by ; - end = strchr(start, ';'); - if (end) { - if (end > start) { - memcpy(tok, start, end-start-1); - tok += end-start-1; - } - *tok = 0; - if (oneline) { - printf("%s;", tokbuf); - } else { - printf("%s.\n", tokbuf); - } - tok = tokbuf; - *tok = 0; - start = end + 1; - continue; - } - // last token: terminated by . - end = strchr(start, '.'); - if (end) { - eot = 1; - if (end > start) { - memcpy(tok, start, end-start-1); - tok += end-start-1; - } - *tok = 0; - printf("%s.\n", tokbuf); - tok = tokbuf; - *tok = 0; - break; - } - // incomplete tokens continued in the next message - end = strchr(start, '?'); - if (end) { - *end = 0; - } - strcpy(tok, start); - tok += strlen(start); - break; - } - } + rc = config(argc, args); + if (rc) { + fail = 1; + goto fail; } // // Cleanup // - for (uint8_t i = 0; i < inti; ++i) { - rc = libusb_release_interface(devh, ints[i]); - if (rc) { - USBERR("failed to release interface", rc); - return 1; + fail: + if (devh) { + for (uint8_t i = 0; i < inti; ++i) { + rc = libusb_release_interface(devh, ints[i]); + if (rc) { + USBERR("failed to release interface", rc); + } } + if (reset) { + libusb_reset_device(devh); + } + libusb_close(devh); } - if (reset) { - libusb_reset_device(devh); + if (ctx) { + libusb_exit(ctx); } - libusb_close(devh); - libusb_exit(ctx); - return 0; + return fail; } @@ -247,7 +392,7 @@ int main(int argc, char **argv) { int opt; char *end; long parsed; - while ((opt = getopt(argc, argv, "v:p:ord")) != -1) { + while ((opt = getopt(argc, argv, "v:p:1rdi:o:")) != -1) { switch (opt) { case 'v': end = optarg; @@ -278,21 +423,46 @@ int main(int argc, char **argv) { case 'r': reset = 1; break; - case 'o': + case '1': oneline = 1; break; case 'd': - debug = 1; + ++debug; + break; + case 'i': + explicit_in = 1; + if (!strcmp("-", optarg)) { + infd = 0; + } else { + infd = open(optarg, O_CLOEXEC|O_RDONLY); + if (infd < 0) { + perror("failed to open input file"); + exit(1); + } + } + break; + case 'o': + if (!strcmp("-", optarg)) { + outfd = 1; + } else { + outfd = open(optarg, O_CLOEXEC|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (outfd < 0) { + perror("failed to open output file"); + exit(1); + } + } break; default: - printf("usage: %s [-v vendor] [-p product] [-o] [-r] [-d] command1 [command2 [... commandN]]\n", argv[0]); + printf("usage: %s [-v vendor] [-p product] [-1] [-r] [-d] [-i infile] [-o outfile] [command1 [... commandN]]\n", argv[0]); puts(" -v vendor\tUSB vendor ID of the barcode scanner"); puts(" -p product\tUSB product ID of the barcode scanner"); - puts(" -o\t\tPrint response on one line, rather than one token per line"); + puts(" -1\t\tPrint response on one line, rather than one token per line"); puts(" -r\t\tReset device after configuration"); puts(" -d\t\tDebug mode (more verbose output)"); + puts(" -i infile\tRead input from infile instead of stdin"); + puts(" -o outfile\tWrite output to outfile instead of stdout"); return 1; } } - return config(argc-optind, argv+optind); + return usb(argc-optind, argv+optind); }