From 18f2d75d916f1d489faa0ddcaadfe47c3348ec46 Mon Sep 17 00:00:00 2001 From: s3lph Date: Tue, 26 Nov 2024 19:56:30 +0100 Subject: [PATCH] feat: use libusb rather than usbdevfs to unclaim kernel driver feat: read and print responses from barcode scanner feat: udev rule docs: add more usage examples --- 99-honeywell.rules | 2 + Makefile | 4 +- README.md | 64 ++++++++++++++++++++--- honeywell-config.c | 124 ++++++++++++++++++++++++++++++++------------- 4 files changed, 152 insertions(+), 42 deletions(-) create mode 100644 99-honeywell.rules diff --git a/99-honeywell.rules b/99-honeywell.rules new file mode 100644 index 0000000..796d737 --- /dev/null +++ b/99-honeywell.rules @@ -0,0 +1,2 @@ +SUBSYSTEM=="usb", ATTRS{idVendor}=="0c2e", ATTRS{idProduct}=="0b07", MODE="0666" +SUBSYSTEM=="usb", ATTRS{idVendor}=="0c2e", ATTRS{idProduct}=="0b01", MODE="0666" diff --git a/Makefile b/Makefile index 1345548..5e895af 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -.PHONY: clean +.PHONY: honeywell-config clean honeywell-config: gcc -lusb-1.0 -o honeywell-config honeywell-config.c clean: - rm honeywell-config + rm -f honeywell-config diff --git a/README.md b/README.md index 47fe0fc..fa2ce5f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,9 @@ A Linux tool for configuring Honeywell barcode scanners via USB. ## Why? I had obtained a used Honeywell Hyperion 1300G scanner, however it was locked down and secured with a password, and the password was unknown. + There is a Windows-only configuration tool from Honeywell called "EZconfig" with which the barcode scanners can be configured via USB, even when locked down. + To not rely on a Windows VM for configuring the scanner, I sniffed the USB traffic using Wireshark and extracted the relevant messages, and wrapped them in this tool. ## Usage @@ -20,14 +22,29 @@ The configuration strings need to be provided as command line arguments. You ca ./honeywell-config [... ] ``` +The commands sent to the scanner are prefixed with `>`, the (tokenized) responses from the scanner with `<`. + In general, please refer to your scanner's manual for configuration strings. -Here are some examples. Please not that this tool most likely needs to run as root. +Here are some examples. ### Factory Reset ``` -./honeywell-config DEFOVR. DEFALT. +$ ./honeywell-config DEFOVR. DEFALT. +> DEFOVR. +< DEFOVR. +> DEFALT. +< DEFALT. +``` + +### Factory Reset - In one command + +``` +$ ./honeywell-config "DEFOVR;DEFALT." +> DEFOVR;DEFALT. +< DEFOVR. +< DEFALT. ``` ### Switch Operation Mode @@ -35,13 +52,17 @@ Here are some examples. Please not that this tool most likely needs to run as r USBHID: ``` -./honeywell-config PAP131. +$ ./honeywell-config PAP131. +> PAP131. +< PAP131. ``` Keyboard: ``` -./honeywell-config PAP124. +$ ./honeywell-config PAP124. +> PAP124. +< PAP124. ``` ### Add Prefix to Output @@ -49,7 +70,9 @@ Keyboard: The following adds the string `FCKAFD ` (ASCII hex `46 43 4B 41 46 44 20`) in front of every scanned barcode: ``` -./honeywell-config PREBK246434B41464420. +$ ./honeywell-config PREBK246434B41464420. +> PREBK246434B41464420. +< PREBK246434B41464420. ``` Note how this behaves differently from if you were to configure the scanner in-band via scanning config barcodes. @@ -65,9 +88,38 @@ There you would scan a lot of individual codes from the manual: On the USB config interface, all of that goes into a single string instead. +### Query Current Beeper Settings + +``` +$ ./honeywell-config 'BEP?.' +> BEP?. +< BEPFQ12550,FQ2100,RPT1,ERR1,BEP1,BIP0,LVL0,EXZ,GRX,EXE,DFT,LED1. +``` + +### List all Settings and Their Possible Values + +``` +$ ./honeywell-config '*.' +> *. +< BEPFQ1100-5000. +< BEPFQ2100-5000. +... +< AXXMOD0|1|2|3|4|5|6|7|8|9|10. +``` + +## USB Device Access + +Please note that this tool normally needs to run as root. Alternatively, you can set up a udev rule to grant access to the USB device to non-root users. + +An example for this can be found in the file `99-honeywell.rules`. +Put this file into `/etc/udev/rules.d`, and run `sudo udevadm control --reload` to load the new ruleset. + +Once you replug the scanner, you should have access to it as a regular user. + ## Adapting for Other Scanners The tool currently has hardcoded USB vendor/product IDs for a Honeywell Hyperion 1300G in Keyboard or USBHID modes. + If you have a different Honeywell scanner, and the tool doesn't find it, you can try making this tool work for it: 1. Obtain the USB vendor and product IDs for your scanner, e.g. through `lsusb`. 1. Replace the VID/PID in the following line of code in `honeywell-config.c`: @@ -75,7 +127,7 @@ If you have a different Honeywell scanner, and the tool doesn't find it, you can ```c libusb_device_handle *devh = libusb_open_device_with_vid_pid(ctx, 0x0c2e, 0x0b07); ``` -1. Recompile the tool with `make clean; make` +1. Recompile the tool with `make` ## License diff --git a/honeywell-config.c b/honeywell-config.c index 5c90f2d..25f675c 100644 --- a/honeywell-config.c +++ b/honeywell-config.c @@ -1,21 +1,11 @@ #include #include -#include -#include -#include -#include -#include -#include #include -// Template for control messages, obtained through packet capture -unsigned char *CMD = "\xfd\x00\x16M\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - int main(int argc, char **argv) { uint8_t cmd[64]; - memcpy(cmd, CMD, 64); - struct usbdevfs_ioctl command; + char tokbuf[1024]; libusb_context *ctx = NULL; // @@ -35,48 +25,114 @@ int main(int argc, char **argv) { return 1; } } - libusb_device *dev = libusb_get_device(devh); - if (!dev) { - perror("failed to get device info"); - return 1; - } // // Disconnect usbhid driver from both device interfaces // - char usbdevfs[32]; - snprintf(usbdevfs, 32, "/dev/bus/usb/%03d/%03d", libusb_get_bus_number(dev), libusb_get_device_address(dev)); - // usbdevfs/ioctl inspired from - // https://www.linuxquestions.org/questions/linux-hardware-18/how-to-unclaim-usb-device-558138/#post3406986 - int fd = open(usbdevfs,O_RDWR); - if (fd < 1) { - perror("failed to open usbdevfs file"); - return 1; - } - for (uint8_t i = 0; i < 2; ++i) { - command.ifno = i; - command.ioctl_code = USBDEVFS_DISCONNECT; - command.data = NULL; - int ret = ioctl(fd, USBDEVFS_IOCTL, &command); - if (ret < 0) { - perror("failed to unclaim USB interface from usbhid"); + if (libusb_set_auto_detach_kernel_driver(devh, 1)) { + perror("auto-attaching kernel driver not possible; please replug USB when done"); + for (uint8_t i = 0; i < 2; ++i) { + int ret = libusb_kernel_driver_active(devh, i); + if (ret < 0) { + perror("failed to query kernel driver state"); + return 1; + } + if (ret > 0) { + ret = libusb_detach_kernel_driver(devh, i); + if (ret) { + perror("failed to detach kernel driver"); + return 1; + } + } } } + for (uint8_t i = 0; i < 2; ++i) { + if (libusb_claim_interface(devh, i)) { + perror("failed to claim interface"); + return 1; + } + } + // // Prepare and send a message on the control channel for each config argument // for (int i = 1; i < argc; ++i) { memset(cmd+5, 0, 59); + cmd[0] = 0xfd; cmd[1] = strlen(argv[i])+3; + cmd[2] = 0x16; + cmd[3] = 0x4d; + cmd[4] = 0x0d; memcpy(cmd+5, argv[i], strlen(argv[i])); - libusb_control_transfer(devh, 0x21, 0x09, 0x02fd, 1, cmd, 64, 0); + int ret = libusb_control_transfer(devh, 0x21, 0x09, 0x02fd, 1, cmd, 64, 0); + if (ret < 0) { + perror("failed to send control message"); + } else { + printf("> %s\n", argv[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) { + ret = libusb_interrupt_transfer(devh, 0x83, cmd, 64, &tx, 0); + if (ret) { + perror("failed to receive response"); + break; + } + // strip 2-byte header and AIMID + start = cmd+5; + 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; + 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 // - libusb_release_interface(devh, 1); + for (uint8_t i = 0; i < 2; ++i) { + if (libusb_release_interface(devh, i)) { + perror("failed to release interface"); + } + } libusb_close(devh); libusb_exit(ctx); }