feat: read support file i/o and reading from stdin
This commit is contained in:
parent
29d2890c26
commit
491807dfdb
2 changed files with 281 additions and 108 deletions
5
Makefile
5
Makefile
|
@ -1,8 +1,11 @@
|
|||
|
||||
.PHONY: honeywell-config clean
|
||||
.PHONY: honeywell-config debug clean
|
||||
|
||||
honeywell-config:
|
||||
gcc -Wall -Werror -o honeywell-config honeywell-config.c -lusb-1.0
|
||||
|
||||
debug:
|
||||
gcc -Wall -Werror -O0 -ggdb3 -o honeywell-config honeywell-config.c -lusb-1.0
|
||||
|
||||
clean:
|
||||
rm -f honeywell-config
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
|
||||
|
@ -10,9 +13,19 @@ uint16_t product_id = 0;
|
|||
uint8_t reset = 0;
|
||||
uint8_t oneline = 0;
|
||||
uint8_t debug = 0;
|
||||
int infd = 0;
|
||||
int outfd = 1;
|
||||
uint8_t explicit_in = 0;
|
||||
|
||||
libusb_device_handle *devh = NULL;
|
||||
uint8_t endpoint = 0;
|
||||
|
||||
#define USBERR(msg, rc) { dprintf(2, "%s: %s\n", msg, libusb_strerror(rc)); }
|
||||
#define DEBUG(...) { if (debug) { dprintf(2, __VA_ARGS__); } }
|
||||
#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__); } }
|
||||
#define HEXBUF(msg, buf, n) { if (debug >= 3) { dprintf(2, "%s:", msg); for (uint8_t *__h = buf; __h < buf+n;++__h) { dprintf(2, " %02x", *__h); } dprintf(2, "\n"); } }
|
||||
|
||||
|
||||
uint32_t vidpid[] = {
|
||||
0x0c2e0b01,
|
||||
|
@ -21,23 +34,186 @@ uint32_t vidpid[] = {
|
|||
};
|
||||
|
||||
|
||||
int config(int argc, char **args) {
|
||||
struct tokenizer {
|
||||
char tokbuf[1024];
|
||||
char *tok;
|
||||
uint8_t eot;
|
||||
int (*cb)(const char *tok, uint8_t last);
|
||||
};
|
||||
|
||||
|
||||
void tok_init(struct tokenizer *t, int (*cb)(const char *tok, uint8_t last)) {
|
||||
memset(t->tokbuf, 0, 1024);
|
||||
t->tok = t->tokbuf;
|
||||
t->eot = 0;
|
||||
t->cb = cb;
|
||||
}
|
||||
|
||||
int tok_read(struct tokenizer *t, char *buf, size_t n) {
|
||||
char *start = buf;
|
||||
char *end = start;
|
||||
while (!t->eot) {
|
||||
// non-final tokens: terminated by ;
|
||||
end = strchr(start, ';');
|
||||
if (end) {
|
||||
if (end > start) {
|
||||
for (char *c = start; c < end; ++c) {
|
||||
if (isprint(*c)) {
|
||||
*(t->tok++) = *c;
|
||||
}
|
||||
}
|
||||
}
|
||||
*(t->tok) = 0;
|
||||
if (t->cb(t->tokbuf, 0)) {
|
||||
return 1;
|
||||
}
|
||||
t->tok = t->tokbuf;
|
||||
*(t->tok) = 0;
|
||||
start = end + 1;
|
||||
continue;
|
||||
}
|
||||
// last token: terminated by .
|
||||
end = strchr(start, '.');
|
||||
if (end) {
|
||||
t->eot = 1;
|
||||
if (end > start) {
|
||||
for (char *c = start; c < end; ++c) {
|
||||
if (isprint(*c)) {
|
||||
*(t->tok++) = *c;
|
||||
}
|
||||
}
|
||||
}
|
||||
*(t->tok) = 0;
|
||||
if (t->cb(t->tokbuf, 1)) {
|
||||
return 1;
|
||||
}
|
||||
t->tok = t->tokbuf;
|
||||
*(t->tok) = 0;
|
||||
break;
|
||||
}
|
||||
// incomplete tokens continued in the next message
|
||||
end = strchr(start, '?');
|
||||
if (end) {
|
||||
*end = 0;
|
||||
}
|
||||
for (char *c = start; *c != 0; ++c) {
|
||||
if (isprint(*c)) {
|
||||
*(t->tok++) = *c;
|
||||
}
|
||||
}
|
||||
*(t->tok) = 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct tokenizer in_tok;
|
||||
struct tokenizer out_tok;
|
||||
|
||||
|
||||
int print_token(const char *tok, uint8_t last) {
|
||||
int rc;
|
||||
if (oneline && !last) {
|
||||
rc = dprintf(outfd, "%s;", tok);
|
||||
} else {
|
||||
rc = dprintf(outfd, "%s.\n", tok);
|
||||
}
|
||||
return rc < 0;
|
||||
}
|
||||
|
||||
|
||||
int send_token(const char *tok, uint8_t last) {
|
||||
uint8_t cmd[64] = { 0 };
|
||||
char tokbuf[1024] = { 0 };
|
||||
uint8_t *cmdptr = cmd + 5;
|
||||
int rc;
|
||||
memset(cmdptr, 0, 59);
|
||||
for (uint8_t i = 0; i < strlen(tok); ++i) {
|
||||
if (isprint(tok[i])) {
|
||||
*(cmdptr++) = tok[i];
|
||||
}
|
||||
}
|
||||
*cmdptr = '.';
|
||||
cmd[0] = 0xfd;
|
||||
cmd[1] = cmdptr - cmd - 1;
|
||||
cmd[2] = 0x16;
|
||||
cmd[3] = 0x4d;
|
||||
cmd[4] = 0x0d;
|
||||
HEXBUF(">", cmd, 64);
|
||||
rc = libusb_control_transfer(devh, 0x21, 0x09, 0x02fd, 1, cmd, 64, 0);
|
||||
if (rc < 0) {
|
||||
USBERR("failed to send control message", rc);
|
||||
return 1;
|
||||
} else {
|
||||
DEBUG2("> %s\n", cmd+5);
|
||||
}
|
||||
int tx;
|
||||
tok_init(&out_tok, print_token);
|
||||
// Repeat until a "." (EOT) is encountered in the response.
|
||||
while (!out_tok.eot) {
|
||||
rc = libusb_interrupt_transfer(devh, endpoint, cmd, 64, &tx, 1000);
|
||||
if (rc) {
|
||||
USBERR("failed to receive response from device", rc);
|
||||
return 1;
|
||||
}
|
||||
HEXBUF("<", cmd, tx);
|
||||
// skip first 5 bytes (report id, length, 3*AIMID)
|
||||
DEBUG2("< %s\n", cmd+5);
|
||||
if (tok_read(&out_tok, (char *) cmd+5, tx-5)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int config(int argc, char **args) {
|
||||
// If commands are provided on the CLI, execute only those
|
||||
if (argc > 0) {
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (send_token(args[i], i >= argc-1)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// If input is a tty, dump config
|
||||
// Can be overwritten with "-i -" on the cli
|
||||
if (isatty(infd) && !explicit_in) {
|
||||
return send_token("?", 1);
|
||||
}
|
||||
// Otherwhise parse tokens from stdin
|
||||
char buf[65];
|
||||
ssize_t n = 1;
|
||||
tok_init(&in_tok, send_token);
|
||||
while (n) {
|
||||
n = read(infd, buf, 64);
|
||||
if (n < 0) {
|
||||
perror("failed to read from input file");
|
||||
return 1;
|
||||
}
|
||||
buf[n] = 0;
|
||||
if (tok_read(&in_tok, buf, n)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int usb(int argc, char **args) {
|
||||
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;
|
||||
int rc = 0, fail = 0;
|
||||
|
||||
uint8_t ints[32] = { 0 };
|
||||
uint8_t inti = 0;
|
||||
uint8_t endpoint = 0;
|
||||
|
||||
//
|
||||
// Find and open the device
|
||||
|
@ -45,14 +221,16 @@ int config(int argc, char **args) {
|
|||
rc = libusb_init(&ctx);
|
||||
if (!ctx) {
|
||||
USBERR("failed to initialize libusb", rc);
|
||||
return 1;
|
||||
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);
|
||||
return 1;
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
devh = NULL;
|
||||
|
@ -61,7 +239,8 @@ int config(int argc, char **args) {
|
|||
}
|
||||
if (!devh) {
|
||||
dprintf(2, "default devices not found, please provide vendor and product id\n");
|
||||
return 1;
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,8 +249,34 @@ int config(int argc, char **args) {
|
|||
rc = libusb_get_device_descriptor(dev, &devd);
|
||||
if (rc) {
|
||||
USBERR("failed to get device descriptor", rc);
|
||||
return 1;
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Print device info
|
||||
rc = libusb_get_string_descriptor_ascii(devh, devd.iManufacturer, (uint8_t *) devdesc, 256);
|
||||
if (rc < 0) {
|
||||
USBERR("failed to get manufacturer string descriptor", rc);
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
@ -84,9 +289,9 @@ int config(int argc, char **args) {
|
|||
USBERR("failed to get config string descriptor", rc);
|
||||
continue;
|
||||
}
|
||||
DEBUG("- configuration %d: %s\n", c, devdesc);
|
||||
DEBUG2("- configuration %d: %s\n", c, devdesc);
|
||||
for (uint8_t i = 0; i < devc->bNumInterfaces; ++i) {
|
||||
DEBUG(" - interface %d\n", i);
|
||||
DEBUG2(" - interface %d\n", i);
|
||||
devi = &(devc->interface[i]);
|
||||
for (uint8_t a = 0; a < devi->num_altsetting; ++a) {
|
||||
devid = &(devi->altsetting[a]);
|
||||
|
@ -95,13 +300,13 @@ int config(int argc, char **args) {
|
|||
USBERR("failed to get interface string descriptor", rc);
|
||||
continue;
|
||||
}
|
||||
DEBUG(" - 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",
|
||||
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);
|
||||
DEBUG2(" - 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:
|
||||
|
@ -116,7 +321,7 @@ int config(int argc, char **args) {
|
|||
}
|
||||
}
|
||||
|
||||
DEBUG("chose endpoint %x\n", endpoint);
|
||||
DEBUG2("chose endpoint %x\n", endpoint);
|
||||
|
||||
//
|
||||
// Disconnect usbhid driver from both device interfaces
|
||||
|
@ -128,13 +333,15 @@ int config(int argc, char **args) {
|
|||
rc = libusb_kernel_driver_active(devh, ints[i]);
|
||||
if (rc < 0) {
|
||||
USBERR("failed to query kernel driver state", rc);
|
||||
return 1;
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
if (rc > 0) {
|
||||
rc = libusb_detach_kernel_driver(devh, ints[i]);
|
||||
if (rc) {
|
||||
USBERR("failed to detach kernel driver", rc);
|
||||
return 1;
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,102 +351,40 @@ int config(int argc, char **args) {
|
|||
rc = libusb_claim_interface(devh, ints[i]);
|
||||
if (rc) {
|
||||
USBERR("failed to claim interface", rc);
|
||||
return 1;
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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);
|
||||
rc = config(argc, args);
|
||||
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;
|
||||
}
|
||||
}
|
||||
fail = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//
|
||||
// Cleanup
|
||||
//
|
||||
fail:
|
||||
if (devh) {
|
||||
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);
|
||||
}
|
||||
if (ctx) {
|
||||
libusb_exit(ctx);
|
||||
return 0;
|
||||
}
|
||||
return fail;
|
||||
}
|
||||
|
||||
|
||||
|
@ -247,7 +392,7 @@ int main(int argc, char **argv) {
|
|||
int opt;
|
||||
char *end;
|
||||
long parsed;
|
||||
while ((opt = getopt(argc, argv, "v:p:ord")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "v:p:1rdi:o:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
end = optarg;
|
||||
|
@ -278,21 +423,46 @@ int main(int argc, char **argv) {
|
|||
case 'r':
|
||||
reset = 1;
|
||||
break;
|
||||
case 'o':
|
||||
case '1':
|
||||
oneline = 1;
|
||||
break;
|
||||
case 'd':
|
||||
debug = 1;
|
||||
++debug;
|
||||
break;
|
||||
case 'i':
|
||||
explicit_in = 1;
|
||||
if (!strcmp("-", optarg)) {
|
||||
infd = 0;
|
||||
} else {
|
||||
infd = open(optarg, O_CLOEXEC|O_RDONLY);
|
||||
if (infd < 0) {
|
||||
perror("failed to open input file");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (!strcmp("-", optarg)) {
|
||||
outfd = 1;
|
||||
} else {
|
||||
outfd = open(optarg, O_CLOEXEC|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
||||
if (outfd < 0) {
|
||||
perror("failed to open output file");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("usage: %s [-v vendor] [-p product] [-o] [-r] [-d] command1 [command2 [... commandN]]\n", argv[0]);
|
||||
printf("usage: %s [-v vendor] [-p product] [-1] [-r] [-d] [-i infile] [-o outfile] [command1 [... 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(" -1\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)");
|
||||
puts(" -i infile\tRead input from infile instead of stdin");
|
||||
puts(" -o outfile\tWrite output to outfile instead of stdout");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return config(argc-optind, argv+optind);
|
||||
return usb(argc-optind, argv+optind);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue