feat: iterate usb devices and find compatible ones
All checks were successful
/ build_debian (push) Successful in 30s
All checks were successful
/ build_debian (push) Successful in 30s
This commit is contained in:
parent
4d9be10dd9
commit
d7223710ea
2 changed files with 125 additions and 101 deletions
41
README.md
41
README.md
|
@ -6,7 +6,7 @@ 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.
|
||||
I had obtained a used Honeywell barcode 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.
|
||||
|
||||
|
@ -19,26 +19,22 @@ First of all, compile the program using `make`. It depends only on `libusb-1.0`
|
|||
The configuration commands can be provided either as CLI arguments or via stdin; if CLI commands are present, stdin is ignrored.
|
||||
|
||||
```
|
||||
honeywell-config [-v vendor] [-p product] [-1] [-c] [-r] [-d] [-i infile] [-o outfile] [command1 [... commandN]]
|
||||
-v vendor USB vendor ID of the barcode scanner
|
||||
-p product USB product ID of the barcode scanner
|
||||
-1 Print response on one line, rather than one token per line
|
||||
./honeywell-config [-v vendor] [-p product] [-1] [-c] [-r] [-d] [-i infile] [-o outfile] [command1 [... commandN]]
|
||||
-v vendor USB vendor ID of the barcode scanner to configure.
|
||||
-p product USB product ID of the barcode scanner to configure.
|
||||
-s serial USB serial number of the barcode scanner to configure.
|
||||
-1 Print response on one line, rather than one token per line.
|
||||
-c Print only received commands that contain an argument. Useful for config backups.
|
||||
-r Reset device after configuration
|
||||
-d Debug mode (more verbose output)
|
||||
-i infile Read input from infile instead of stdin
|
||||
-o outfile Write output to outfile instead of stdout
|
||||
-h Show this help
|
||||
-V Show the version number
|
||||
-r Reset device after configuration.
|
||||
-d Debug mode (more verbose output). Can be provided repeatedly to increase verbosity.
|
||||
-i infile Read input from infile instead of stdin.
|
||||
-o outfile Write output to outfile instead of stdout.
|
||||
-h Show this help and exit.
|
||||
-V Show the version number and exit.
|
||||
```
|
||||
|
||||
If no vendor ID or product ID is provided, the following set of IDs is tried:
|
||||
|
||||
- `0c2e:0b01` (Honeywell 1300G, USB Kkeyboard mode PC)
|
||||
- `0c2e:0b02` (Honeywell 1300G, USB Keyboard Apple)
|
||||
- `0c2e:0b07` (Honeywell 1300G, USB HID POS mode)
|
||||
|
||||
USB serial mode (`0c2e:0b0a`) is skipped because USB configuration does not seem to work for it (and it isn't found by EZconfig either).
|
||||
All USB devices on the host are iterated, and the first one with an EZConfig configuration endpoint is chosen.
|
||||
If there are multiple compatible devices on the host, you can control which device is selected by providing a vendor ID, product ID or serial number.
|
||||
|
||||
For the actual configuration commands, please refer to your scanner's manual. Here are some examples.
|
||||
|
||||
|
@ -52,14 +48,7 @@ To only retrieve the config commands that have a value associated with them, use
|
|||
$ honeywell-config -c -o scanner.conf
|
||||
```
|
||||
|
||||
This configuration can be restored the the scanner by providing it as an input file using `-i`. Each sent config command should be acknowledged
|
||||
- `0c2e:0b01` (Honeywell 1300G, USB Kkeyboard mode PC)
|
||||
- `0c2e:0b02` (Honeywell 1300G, USB Keyboard Apple)
|
||||
- `0c2e:0b07` (Honeywell 1300G, USB HID POS mode)
|
||||
|
||||
USB serial mode (`0c2e:0b0a`) is skipped because USB configuration does not seem to work for it (and it isn't found by EZconfig either).
|
||||
by the scanner and printed to stdout:
|
||||
|
||||
This configuration can be restored the the scanner by providing it as an input file using `-i`. Each sent config command should be acknowledged by the scanner and printed to stdout:
|
||||
|
||||
```shell-session
|
||||
$ honeywell-config -i scanner.conf
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#define ISSTATUS(c) (*c == S_ENQ || *c == S_ACK || *c == S_NAK)
|
||||
#define USBERR(msg, rc) { dprintf(2, "%s: %s\n", msg, libusb_strerror(rc)); }
|
||||
#define USBDEBUG2(msg, rc) { if (debug >= 3) { dprintf(2, "%s: %s\n", msg, libusb_strerror(rc)); } }
|
||||
#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__); } }
|
||||
|
@ -25,6 +26,7 @@
|
|||
|
||||
uint16_t vendor_id = 0;
|
||||
uint16_t product_id = 0;
|
||||
char *serial_number = NULL;
|
||||
uint8_t reset = 0;
|
||||
uint8_t oneline = 0;
|
||||
uint8_t onlyargs = 0;
|
||||
|
@ -35,14 +37,8 @@ uint8_t explicit_in = 0;
|
|||
|
||||
libusb_device_handle *devh = NULL;
|
||||
uint8_t endpoint = 0;
|
||||
|
||||
|
||||
uint32_t vidpid[] = {
|
||||
0x0c2e0b01,
|
||||
0x0c2e0b02,
|
||||
0x0c2e0b07,
|
||||
};
|
||||
|
||||
uint8_t ints[32] = { 0 };
|
||||
uint8_t inti = 0;
|
||||
|
||||
struct tokenizer {
|
||||
char tokbuf[1024];
|
||||
|
@ -225,95 +221,61 @@ int config(int argc, char **args) {
|
|||
}
|
||||
|
||||
|
||||
int usb(int argc, char **args) {
|
||||
libusb_context *ctx = NULL;
|
||||
libusb_device *dev = NULL;
|
||||
int check_device(libusb_device *dev) {
|
||||
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, fail = 0;
|
||||
|
||||
uint8_t ints[32] = { 0 };
|
||||
uint8_t inti = 0;
|
||||
|
||||
//
|
||||
// Find and open the device
|
||||
//
|
||||
rc = libusb_init(&ctx);
|
||||
if (!ctx) {
|
||||
USBERR("failed to initialize libusb", rc);
|
||||
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);
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
} 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");
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
// Get endpoint infos
|
||||
dev = libusb_get_device(devh);
|
||||
rc = libusb_get_device_descriptor(dev, &devd);
|
||||
// Top-level checks against VID, PID and Serial
|
||||
int rc = libusb_get_device_descriptor(dev, &devd);
|
||||
if (rc) {
|
||||
USBERR("failed to get device descriptor", rc);
|
||||
fail = 1;
|
||||
goto fail;
|
||||
USBDEBUG2("failed to get device descriptor", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Print device info
|
||||
rc = libusb_get_string_descriptor_ascii(devh, devd.iManufacturer, (uint8_t *) devdesc, 256);
|
||||
if (vendor_id && vendor_id != devd.idVendor) {
|
||||
return 1;
|
||||
}
|
||||
if (product_id && product_id != devd.idProduct) {
|
||||
return 1;
|
||||
}
|
||||
rc = libusb_open(dev, &devh);
|
||||
if (rc < 0) {
|
||||
USBERR("failed to get manufacturer string descriptor", rc);
|
||||
fail = 1;
|
||||
goto fail;
|
||||
USBDEBUG2("failed to open device", rc);
|
||||
return rc;
|
||||
}
|
||||
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;
|
||||
if (serial_number) {
|
||||
rc = libusb_get_string_descriptor_ascii(devh, devd.iSerialNumber, (uint8_t *) devdesc, 256);
|
||||
if (rc < 0) {
|
||||
libusb_close(devh);
|
||||
devh = NULL;
|
||||
USBDEBUG2("failed to get serial id string descriptor", rc);
|
||||
return rc;
|
||||
}
|
||||
if (strncmp(serial_number, devdesc, strlen(serial_number))) {
|
||||
libusb_close(devh);
|
||||
devh = NULL;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (rc) {
|
||||
USBERR("failed to get config descriptor", rc);
|
||||
USBDEBUG2("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);
|
||||
USBDEBUG2("failed to get config string descriptor", rc);
|
||||
continue;
|
||||
}
|
||||
DEBUG2("- configuration %d: %s\n", c, devdesc);
|
||||
inti = 0;
|
||||
for (uint8_t i = 0; i < devc->bNumInterfaces; ++i) {
|
||||
DEBUG2(" - interface %d\n", i);
|
||||
devi = &(devc->interface[i]);
|
||||
|
@ -321,7 +283,7 @@ int usb(int argc, char **args) {
|
|||
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);
|
||||
USBDEBUG2("failed to get interface string descriptor", rc);
|
||||
continue;
|
||||
}
|
||||
DEBUG2(" - altsetting %d: interface=%d, desc=%s class=%d subclass=%d protocol=%d\n",
|
||||
|
@ -339,13 +301,79 @@ int usb(int argc, char **args) {
|
|||
&& !strncmp("REM", devdesc, 3) // human readable description of the interface must be "REM" (Honeywell "Remote MasterMind" control interface)
|
||||
) {
|
||||
endpoint = deved->bEndpointAddress;
|
||||
// Print device info
|
||||
rc = libusb_get_string_descriptor_ascii(devh, devd.iManufacturer, (uint8_t *) devdesc, 256);
|
||||
if (rc < 0) {
|
||||
USBDEBUG2("failed to get manufacturer string descriptor", rc);
|
||||
libusb_close(devh);
|
||||
devh = NULL;
|
||||
return 1;
|
||||
}
|
||||
DEBUG("Manufacturer: %s\n", devdesc);
|
||||
rc = libusb_get_string_descriptor_ascii(devh, devd.iProduct, (uint8_t *) devdesc, 256);
|
||||
if (rc < 0) {
|
||||
USBDEBUG2("failed to get product string descriptor", rc);
|
||||
libusb_close(devh);
|
||||
devh = NULL;
|
||||
return 1;
|
||||
}
|
||||
DEBUG("Product: %s\n", devdesc);
|
||||
rc = libusb_get_string_descriptor_ascii(devh, devd.iSerialNumber, (uint8_t *) devdesc, 256);
|
||||
if (rc < 0) {
|
||||
USBDEBUG2("failed to get serial number string descriptor", rc);
|
||||
libusb_close(devh);
|
||||
devh = NULL;
|
||||
return 1;
|
||||
}
|
||||
DEBUG("Serial Number: %s\n", devdesc);
|
||||
DEBUG2("chose endpoint %x\n", endpoint);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
libusb_close(devh);
|
||||
devh = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
DEBUG2("chose endpoint %x\n", endpoint);
|
||||
|
||||
int usb(int argc, char **args) {
|
||||
libusb_context *ctx = NULL;
|
||||
libusb_device **list = NULL;
|
||||
int rc = 0, fail = 0;
|
||||
|
||||
//
|
||||
// Find and open the device
|
||||
//
|
||||
rc = libusb_init(&ctx);
|
||||
if (!ctx) {
|
||||
USBERR("failed to initialize libusb", rc);
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//
|
||||
// Iterate list of USB devices
|
||||
//
|
||||
ssize_t ndev = libusb_get_device_list(ctx, &list);
|
||||
if (ndev < 0) {
|
||||
USBERR("failed to list usb devices", ndev);
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
for (size_t i = 0; i < ndev; ++i) {
|
||||
rc = check_device(list[i]);
|
||||
if (!rc) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!devh) {
|
||||
dprintf(2, "failed to find compatible device\n");
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//
|
||||
// Disconnect usbhid driver from both device interfaces
|
||||
|
@ -405,6 +433,9 @@ int usb(int argc, char **args) {
|
|||
}
|
||||
libusb_close(devh);
|
||||
}
|
||||
if (list) {
|
||||
libusb_free_device_list(list, 1);
|
||||
}
|
||||
if (ctx) {
|
||||
libusb_exit(ctx);
|
||||
}
|
||||
|
@ -416,7 +447,7 @@ int main(int argc, char **argv) {
|
|||
int opt;
|
||||
char *end;
|
||||
long parsed;
|
||||
while ((opt = getopt(argc, argv, "v:p:1crdi:o:hV")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "v:p:s:1crdi:o:hV")) != -1) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
end = optarg;
|
||||
|
@ -444,6 +475,9 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
product_id = (uint16_t) parsed;
|
||||
break;
|
||||
case 's':
|
||||
serial_number = strdup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
reset = 1;
|
||||
break;
|
||||
|
@ -487,6 +521,7 @@ int main(int argc, char **argv) {
|
|||
printf("%s [-v vendor] [-p product] [-1] [-c] [-r] [-d] [-i infile] [-o outfile] [command1 [... commandN]]\n", argv[0]);
|
||||
puts(" -v vendor\tUSB vendor ID of the barcode scanner to configure.");
|
||||
puts(" -p product\tUSB product ID of the barcode scanner to configure.");
|
||||
puts(" -s serial\tUSB serial number of the barcode scanner to configure.");
|
||||
puts(" -1\t\tPrint response on one line, rather than one token per line.");
|
||||
puts(" -c\t\tPrint only received commands that contain an argument. Useful for config backups.");
|
||||
puts(" -r\t\tReset device after configuration.");
|
||||
|
|
Loading…
Reference in a new issue