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
This commit is contained in:
parent
21603523de
commit
18f2d75d91
4 changed files with 152 additions and 42 deletions
2
99-honeywell.rules
Normal file
2
99-honeywell.rules
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="0c2e", ATTRS{idProduct}=="0b07", MODE="0666"
|
||||||
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="0c2e", ATTRS{idProduct}=="0b01", MODE="0666"
|
4
Makefile
4
Makefile
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: honeywell-config clean
|
||||||
|
|
||||||
honeywell-config:
|
honeywell-config:
|
||||||
gcc -lusb-1.0 -o honeywell-config honeywell-config.c
|
gcc -lusb-1.0 -o honeywell-config honeywell-config.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm honeywell-config
|
rm -f honeywell-config
|
||||||
|
|
64
README.md
64
README.md
|
@ -7,7 +7,9 @@ A Linux tool for configuring Honeywell barcode scanners via USB.
|
||||||
## Why?
|
## 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.
|
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.
|
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.
|
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
|
## Usage
|
||||||
|
@ -20,14 +22,29 @@ The configuration strings need to be provided as command line arguments. You ca
|
||||||
./honeywell-config <string1> [... <stringN>]
|
./honeywell-config <string1> [... <stringN>]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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.
|
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
|
### 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
|
### Switch Operation Mode
|
||||||
|
@ -35,13 +52,17 @@ Here are some examples. Please not that this tool most likely needs to run as r
|
||||||
USBHID:
|
USBHID:
|
||||||
|
|
||||||
```
|
```
|
||||||
./honeywell-config PAP131.
|
$ ./honeywell-config PAP131.
|
||||||
|
> PAP131.
|
||||||
|
< PAP131.
|
||||||
```
|
```
|
||||||
|
|
||||||
Keyboard:
|
Keyboard:
|
||||||
|
|
||||||
```
|
```
|
||||||
./honeywell-config PAP124.
|
$ ./honeywell-config PAP124.
|
||||||
|
> PAP124.
|
||||||
|
< PAP124.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Add Prefix to Output
|
### 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:
|
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.
|
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.
|
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
|
## Adapting for Other Scanners
|
||||||
|
|
||||||
The tool currently has hardcoded USB vendor/product IDs for a Honeywell Hyperion 1300G in Keyboard or USBHID modes.
|
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:
|
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. 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`:
|
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
|
```c
|
||||||
libusb_device_handle *devh = libusb_open_device_with_vid_pid(ctx, 0x0c2e, 0x0b07);
|
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
|
## License
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,11 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <linux/ioctl.h>
|
|
||||||
#include <linux/usbdevice_fs.h>
|
|
||||||
#include <libusb-1.0/libusb.h>
|
#include <libusb-1.0/libusb.h>
|
||||||
|
|
||||||
// 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) {
|
int main(int argc, char **argv) {
|
||||||
uint8_t cmd[64];
|
uint8_t cmd[64];
|
||||||
memcpy(cmd, CMD, 64);
|
char tokbuf[1024];
|
||||||
struct usbdevfs_ioctl command;
|
|
||||||
libusb_context *ctx = NULL;
|
libusb_context *ctx = NULL;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -35,48 +25,114 @@ int main(int argc, char **argv) {
|
||||||
return 1;
|
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
|
// Disconnect usbhid driver from both device interfaces
|
||||||
//
|
//
|
||||||
char usbdevfs[32];
|
if (libusb_set_auto_detach_kernel_driver(devh, 1)) {
|
||||||
snprintf(usbdevfs, 32, "/dev/bus/usb/%03d/%03d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
|
perror("auto-attaching kernel driver not possible; please replug USB when done");
|
||||||
// usbdevfs/ioctl inspired from
|
for (uint8_t i = 0; i < 2; ++i) {
|
||||||
// https://www.linuxquestions.org/questions/linux-hardware-18/how-to-unclaim-usb-device-558138/#post3406986
|
int ret = libusb_kernel_driver_active(devh, i);
|
||||||
int fd = open(usbdevfs,O_RDWR);
|
if (ret < 0) {
|
||||||
if (fd < 1) {
|
perror("failed to query kernel driver state");
|
||||||
perror("failed to open usbdevfs file");
|
return 1;
|
||||||
return 1;
|
}
|
||||||
}
|
if (ret > 0) {
|
||||||
for (uint8_t i = 0; i < 2; ++i) {
|
ret = libusb_detach_kernel_driver(devh, i);
|
||||||
command.ifno = i;
|
if (ret) {
|
||||||
command.ioctl_code = USBDEVFS_DISCONNECT;
|
perror("failed to detach kernel driver");
|
||||||
command.data = NULL;
|
return 1;
|
||||||
int ret = ioctl(fd, USBDEVFS_IOCTL, &command);
|
}
|
||||||
if (ret < 0) {
|
}
|
||||||
perror("failed to unclaim USB interface from usbhid");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
// Prepare and send a message on the control channel for each config argument
|
||||||
//
|
//
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
memset(cmd+5, 0, 59);
|
memset(cmd+5, 0, 59);
|
||||||
|
cmd[0] = 0xfd;
|
||||||
cmd[1] = strlen(argv[i])+3;
|
cmd[1] = strlen(argv[i])+3;
|
||||||
|
cmd[2] = 0x16;
|
||||||
|
cmd[3] = 0x4d;
|
||||||
|
cmd[4] = 0x0d;
|
||||||
memcpy(cmd+5, argv[i], strlen(argv[i]));
|
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
|
// 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_close(devh);
|
||||||
libusb_exit(ctx);
|
libusb_exit(ctx);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue