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?
|
## 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.
|
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.
|
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]]
|
./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
|
-v vendor USB vendor ID of the barcode scanner to configure.
|
||||||
-p product USB product ID of the barcode scanner
|
-p product USB product ID of the barcode scanner to configure.
|
||||||
-1 Print response on one line, rather than one token per line
|
-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.
|
-c Print only received commands that contain an argument. Useful for config backups.
|
||||||
-r Reset device after configuration
|
-r Reset device after configuration.
|
||||||
-d Debug mode (more verbose output)
|
-d Debug mode (more verbose output). Can be provided repeatedly to increase verbosity.
|
||||||
-i infile Read input from infile instead of stdin
|
-i infile Read input from infile instead of stdin.
|
||||||
-o outfile Write output to outfile instead of stdout
|
-o outfile Write output to outfile instead of stdout.
|
||||||
-h Show this help
|
-h Show this help and exit.
|
||||||
-V Show the version number
|
-V Show the version number and exit.
|
||||||
```
|
```
|
||||||
|
|
||||||
If no vendor ID or product ID is provided, the following set of IDs is tried:
|
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.
|
||||||
- `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).
|
|
||||||
|
|
||||||
For the actual configuration commands, please refer to your scanner's manual. Here are some examples.
|
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
|
$ 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
|
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:
|
||||||
- `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:
|
|
||||||
|
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ honeywell-config -i scanner.conf
|
$ honeywell-config -i scanner.conf
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#define ISSTATUS(c) (*c == S_ENQ || *c == S_ACK || *c == S_NAK)
|
#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 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 DEBUG(...) { if (debug >= 1) { dprintf(2, __VA_ARGS__); } }
|
||||||
#define DEBUG2(...) { if (debug >= 2) { dprintf(2, __VA_ARGS__); } }
|
#define DEBUG2(...) { if (debug >= 2) { dprintf(2, __VA_ARGS__); } }
|
||||||
#define DEBUG3(...) { if (debug >= 3) { dprintf(2, __VA_ARGS__); } }
|
#define DEBUG3(...) { if (debug >= 3) { dprintf(2, __VA_ARGS__); } }
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
|
|
||||||
uint16_t vendor_id = 0;
|
uint16_t vendor_id = 0;
|
||||||
uint16_t product_id = 0;
|
uint16_t product_id = 0;
|
||||||
|
char *serial_number = NULL;
|
||||||
uint8_t reset = 0;
|
uint8_t reset = 0;
|
||||||
uint8_t oneline = 0;
|
uint8_t oneline = 0;
|
||||||
uint8_t onlyargs = 0;
|
uint8_t onlyargs = 0;
|
||||||
|
@ -35,14 +37,8 @@ uint8_t explicit_in = 0;
|
||||||
|
|
||||||
libusb_device_handle *devh = NULL;
|
libusb_device_handle *devh = NULL;
|
||||||
uint8_t endpoint = 0;
|
uint8_t endpoint = 0;
|
||||||
|
uint8_t ints[32] = { 0 };
|
||||||
|
uint8_t inti = 0;
|
||||||
uint32_t vidpid[] = {
|
|
||||||
0x0c2e0b01,
|
|
||||||
0x0c2e0b02,
|
|
||||||
0x0c2e0b07,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct tokenizer {
|
struct tokenizer {
|
||||||
char tokbuf[1024];
|
char tokbuf[1024];
|
||||||
|
@ -225,95 +221,61 @@ int config(int argc, char **args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int usb(int argc, char **args) {
|
int check_device(libusb_device *dev) {
|
||||||
libusb_context *ctx = NULL;
|
|
||||||
libusb_device *dev = NULL;
|
|
||||||
struct libusb_device_descriptor devd = { 0 };
|
struct libusb_device_descriptor devd = { 0 };
|
||||||
struct libusb_config_descriptor *devc = NULL;
|
struct libusb_config_descriptor *devc = NULL;
|
||||||
const struct libusb_interface *devi = NULL;
|
const struct libusb_interface *devi = NULL;
|
||||||
const struct libusb_interface_descriptor *devid = NULL;
|
const struct libusb_interface_descriptor *devid = NULL;
|
||||||
const struct libusb_endpoint_descriptor *deved = NULL;
|
const struct libusb_endpoint_descriptor *deved = NULL;
|
||||||
char devdesc[256] = { 0 };
|
char devdesc[256] = { 0 };
|
||||||
int rc = 0, fail = 0;
|
|
||||||
|
|
||||||
uint8_t ints[32] = { 0 };
|
// Top-level checks against VID, PID and Serial
|
||||||
uint8_t inti = 0;
|
int rc = libusb_get_device_descriptor(dev, &devd);
|
||||||
|
|
||||||
//
|
|
||||||
// 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);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
USBERR("failed to get device descriptor", rc);
|
USBDEBUG2("failed to get device descriptor", rc);
|
||||||
fail = 1;
|
return rc;
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
if (vendor_id && vendor_id != devd.idVendor) {
|
||||||
// Print device info
|
return 1;
|
||||||
rc = libusb_get_string_descriptor_ascii(devh, devd.iManufacturer, (uint8_t *) devdesc, 256);
|
}
|
||||||
|
if (product_id && product_id != devd.idProduct) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
rc = libusb_open(dev, &devh);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
USBERR("failed to get manufacturer string descriptor", rc);
|
USBDEBUG2("failed to open device", rc);
|
||||||
fail = 1;
|
return rc;
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
DEBUG("Manufacturer: %s\n", devdesc);
|
if (serial_number) {
|
||||||
rc = libusb_get_string_descriptor_ascii(devh, devd.iProduct, (uint8_t *) devdesc, 256);
|
rc = libusb_get_string_descriptor_ascii(devh, devd.iSerialNumber, (uint8_t *) devdesc, 256);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
USBERR("failed to get product string descriptor", rc);
|
libusb_close(devh);
|
||||||
fail = 1;
|
devh = NULL;
|
||||||
goto fail;
|
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
|
// Iterate all configurations, interfaces and endpoints to find the REM/EZConfig endpoint
|
||||||
for (uint8_t c = 0; c < devd.bNumConfigurations; ++c) {
|
for (uint8_t c = 0; c < devd.bNumConfigurations; ++c) {
|
||||||
libusb_free_config_descriptor(devc);
|
libusb_free_config_descriptor(devc);
|
||||||
rc = libusb_get_config_descriptor(dev, c, &devc);
|
rc = libusb_get_config_descriptor(dev, c, &devc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
USBERR("failed to get config descriptor", rc);
|
USBDEBUG2("failed to get config descriptor", rc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rc = libusb_get_string_descriptor_ascii(devh, devc->iConfiguration, (uint8_t *) devdesc, 256);
|
rc = libusb_get_string_descriptor_ascii(devh, devc->iConfiguration, (uint8_t *) devdesc, 256);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
USBERR("failed to get config string descriptor", rc);
|
USBDEBUG2("failed to get config string descriptor", rc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DEBUG2("- configuration %d: %s\n", c, devdesc);
|
DEBUG2("- configuration %d: %s\n", c, devdesc);
|
||||||
|
inti = 0;
|
||||||
for (uint8_t i = 0; i < devc->bNumInterfaces; ++i) {
|
for (uint8_t i = 0; i < devc->bNumInterfaces; ++i) {
|
||||||
DEBUG2(" - interface %d\n", i);
|
DEBUG2(" - interface %d\n", i);
|
||||||
devi = &(devc->interface[i]);
|
devi = &(devc->interface[i]);
|
||||||
|
@ -321,7 +283,7 @@ int usb(int argc, char **args) {
|
||||||
devid = &(devi->altsetting[a]);
|
devid = &(devi->altsetting[a]);
|
||||||
rc = libusb_get_string_descriptor_ascii(devh, devid->iInterface, (uint8_t *) devdesc, 256);
|
rc = libusb_get_string_descriptor_ascii(devh, devid->iInterface, (uint8_t *) devdesc, 256);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
USBERR("failed to get interface string descriptor", rc);
|
USBDEBUG2("failed to get interface string descriptor", rc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DEBUG2(" - altsetting %d: interface=%d, desc=%s class=%d subclass=%d protocol=%d\n",
|
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)
|
&& !strncmp("REM", devdesc, 3) // human readable description of the interface must be "REM" (Honeywell "Remote MasterMind" control interface)
|
||||||
) {
|
) {
|
||||||
endpoint = deved->bEndpointAddress;
|
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
|
// Disconnect usbhid driver from both device interfaces
|
||||||
|
@ -405,6 +433,9 @@ int usb(int argc, char **args) {
|
||||||
}
|
}
|
||||||
libusb_close(devh);
|
libusb_close(devh);
|
||||||
}
|
}
|
||||||
|
if (list) {
|
||||||
|
libusb_free_device_list(list, 1);
|
||||||
|
}
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
libusb_exit(ctx);
|
libusb_exit(ctx);
|
||||||
}
|
}
|
||||||
|
@ -416,7 +447,7 @@ int main(int argc, char **argv) {
|
||||||
int opt;
|
int opt;
|
||||||
char *end;
|
char *end;
|
||||||
long parsed;
|
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) {
|
switch (opt) {
|
||||||
case 'v':
|
case 'v':
|
||||||
end = optarg;
|
end = optarg;
|
||||||
|
@ -444,6 +475,9 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
product_id = (uint16_t) parsed;
|
product_id = (uint16_t) parsed;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
serial_number = strdup(optarg);
|
||||||
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
reset = 1;
|
reset = 1;
|
||||||
break;
|
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]);
|
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(" -v vendor\tUSB vendor ID of the barcode scanner to configure.");
|
||||||
puts(" -p product\tUSB product 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(" -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(" -c\t\tPrint only received commands that contain an argument. Useful for config backups.");
|
||||||
puts(" -r\t\tReset device after configuration.");
|
puts(" -r\t\tReset device after configuration.");
|
||||||
|
|
Loading…
Reference in a new issue