#include #include #include #include #include uint16_t vendor_id = 0; uint16_t product_id = 0; uint8_t reset = 0; uint8_t oneline = 0; uint8_t debug = 0; #define USBERR(msg, rc) { dprintf(2, "%s: %s\n", msg, libusb_strerror(rc)); } #define DEBUG(...) { if (debug) { dprintf(2, __VA_ARGS__); } } uint32_t vidpid[] = { 0x0c2e0b01, 0x0c2e0b02, 0x0c2e0b07, }; int config(int argc, char **args) { uint8_t cmd[64] = { 0 }; char tokbuf[1024] = { 0 }; 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; uint8_t ints[32] = { 0 }; uint8_t inti = 0; uint8_t endpoint = 0; // // Find and open the device // rc = libusb_init(&ctx); if (!ctx) { USBERR("failed to initialize libusb", rc); return 1; } 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; } } else { devh = NULL; for (uint8_t i = 0; i < sizeof(vidpid)/sizeof(vidpid[0]) && !devh; ++i) { devh = libusb_open_device_with_vid_pid(ctx, vidpid[i] >> 16, vidpid[i] & 0xffff); } if (!devh) { dprintf(2, "default devices not found, please provide vendor and product id\n"); return 1; } } // Get endpoint infos dev = libusb_get_device(devh); rc = libusb_get_device_descriptor(dev, &devd); if (rc) { USBERR("failed to get device descriptor", rc); return 1; } for (uint8_t c = 0; c < devd.bNumConfigurations; ++c) { libusb_free_config_descriptor(devc); rc = libusb_get_config_descriptor(dev, c, &devc); if (rc) { USBERR("failed to get config descriptor", rc); continue; } rc = libusb_get_string_descriptor_ascii(devh, devc->iConfiguration, (uint8_t *) devdesc, 256); if (rc < 0) { USBERR("failed to get config string descriptor", rc); continue; } DEBUG("- configuration %d: %s\n", c, devdesc); for (uint8_t i = 0; i < devc->bNumInterfaces; ++i) { DEBUG(" - interface %d\n", i); devi = &(devc->interface[i]); for (uint8_t a = 0; a < devi->num_altsetting; ++a) { devid = &(devi->altsetting[a]); rc = libusb_get_string_descriptor_ascii(devh, devid->iInterface, (uint8_t *) devdesc, 256); if (rc < 0) { 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); 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); // build a list of interfaces so that we can claim all of them ints[inti++] = devid->bInterfaceNumber; // endpoint selection: if ((deved->bmAttributes & 0b11) == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT // must be an interrupt endpoint && ((deved->bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) // must be a device to host endpoint && !strncmp("REM", devdesc, 3) // human readable description of the interface must be "REM" (Honeywell "Remote MasterMind" control interface) ) { endpoint = deved->bEndpointAddress; } } } } } DEBUG("chose endpoint %x\n", endpoint); // // Disconnect usbhid driver from both device interfaces // rc = libusb_set_auto_detach_kernel_driver(devh, 1); if (rc) { USBERR("auto-attaching kernel driver not possible; please replug USB when done", rc); for (uint8_t i = 0; i < inti; ++i) { rc = libusb_kernel_driver_active(devh, ints[i]); if (rc < 0) { USBERR("failed to query kernel driver state", rc); return 1; } if (rc > 0) { rc = libusb_detach_kernel_driver(devh, ints[i]); if (rc) { USBERR("failed to detach kernel driver", rc); return 1; } } } } for (uint8_t i = 0; i < inti; ++i) { rc = libusb_claim_interface(devh, ints[i]); if (rc) { USBERR("failed to claim interface", rc); return 1; } } // // 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; } } } // // 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; } } if (reset) { libusb_reset_device(devh); } libusb_close(devh); libusb_exit(ctx); return 0; } int main(int argc, char **argv) { int opt; char *end; long parsed; while ((opt = getopt(argc, argv, "v:p:ord")) != -1) { switch (opt) { case 'v': end = optarg; parsed = strtol(optarg, &end, 16); if (*end != 0) { perror("failed to parse vendor id"); return 1; } if (parsed <= 0 || parsed > 0xffff) { puts("invalid vendor id"); return 1; } vendor_id = (uint16_t) parsed; break; case 'p': end = optarg; parsed = strtol(optarg, &end, 16); if (*end != 0) { perror("failed to parse product id"); return 1; } if (parsed <= 0 || parsed > 0xffff) { puts("invalid product id"); return 1; } product_id = (uint16_t) parsed; break; case 'r': reset = 1; break; case 'o': oneline = 1; break; case 'd': debug = 1; break; default: printf("usage: %s [-v vendor] [-p product] [-o] [-r] [-d] command1 [command2 [... 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(" -r\t\tReset device after configuration"); puts(" -d\t\tDebug mode (more verbose output)"); return 1; } } return config(argc-optind, argv+optind); }