From 9db2e455127fa181c68cf093d23fe0c1ee142925 Mon Sep 17 00:00:00 2001 From: Michael Gernoth Date: Wed, 29 May 2013 16:03:32 +0200 Subject: [PATCH] libusb-based driver for HM-CFG-USB and an initial HM-CFG-LAN emulation --- .gitignore | 5 + Makefile | 17 +++ hexdump.h | 58 +++++++ hmcfgusb.c | 402 +++++++++++++++++++++++++++++++++++++++++++++++++ hmcfgusb.h | 37 +++++ hmcfgusb.rules | 1 + hmland.c | 222 +++++++++++++++++++++++++++ 7 files changed, 742 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 hexdump.h create mode 100644 hmcfgusb.c create mode 100644 hmcfgusb.h create mode 100644 hmcfgusb.rules create mode 100644 hmland.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2056e43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +hmcfgusb.d +hmcfgusb.o +hmland +hmland.d +hmland.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b82aa23 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +CFLAGS=-MMD -O2 -Wall -I/opt/local/include -g +LDFLAGS=-L/opt/local/lib -lusb-1.0 -lm +CC=gcc + +OBJS=hmcfgusb.o hmland.o + +all: hmland + +DEPEND=$(OBJS:.o=.d) +-include $(DEPEND) + +hmland: $(OBJS) + +clean: + rm -f $(OBJS) $(DEPEND) hmland + +.PHONY: all clean diff --git a/hexdump.h b/hexdump.h new file mode 100644 index 0000000..286435c --- /dev/null +++ b/hexdump.h @@ -0,0 +1,58 @@ +/* simple hexdumper + * + * Copyright (c) 2004-2013 Michael Gernoth + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +static void asciishow(unsigned char *buf, int len) +{ + int i; + + fprintf(stderr, " "); + for (i = 0; i < len; i++) { + if ((buf[i] >=32) && (buf[i] <=126)) { + fprintf(stderr, "%c", buf[i]); + } else { + fprintf(stderr, "."); + } + } +} + +static void hexdump(unsigned char *buf, int len, char *prefix) +{ + int i, j; + + fprintf(stderr, "\n%s", prefix); + for (i = 0; i < len; i++) { + if((i%16) == 0) { + fprintf(stderr, "0x%04x: ", i); + } + fprintf(stderr, "%02x ", buf[i]); + if ((i%16) == 15) { + asciishow(buf+i-15, 16); + if (i != (len-1)) + fprintf(stderr, "\n%s", prefix); + } + } + for (j = (i%16); j < 16; j++) + fprintf(stderr, " "); + asciishow(buf+i-(i%16), (i%16)); + fprintf(stderr, "\n"); +} diff --git a/hmcfgusb.c b/hmcfgusb.c new file mode 100644 index 0000000..0e45cbc --- /dev/null +++ b/hmcfgusb.c @@ -0,0 +1,402 @@ +/* HM-CFG-USB libusb-driver + * + * Copyright (c) 2013 Michael Gernoth + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hexdump.h" +#include "hmcfgusb.h" + +#define USB_TIMEOUT 10000 + +#define ID_VENDOR 0x1b1f +#define ID_PRODUCT 0xc00f + +/* TODO: dynamic */ +#define ASYNC_SIZE 0x0040 +#define ASYNC_INTERVAL 32 + +#define EP_OUT 0x02 +#define EP_IN 0x83 + +static int quit = 0; + +/* Not in all libusb-1.0 versions, so we have to roll our own :-( */ +static char * usb_strerror(int e) +{ + static char unknerr[256]; + + switch (e) { + case LIBUSB_SUCCESS: + return "Success"; + case LIBUSB_ERROR_IO: + return "Input/output error"; + case LIBUSB_ERROR_INVALID_PARAM: + return "Invalid parameter"; + case LIBUSB_ERROR_ACCESS: + return "Access denied (insufficient permissions)"; + case LIBUSB_ERROR_NO_DEVICE: + return "No such device (it may have been disconnected)"; + case LIBUSB_ERROR_NOT_FOUND: + return "Entity not found"; + case LIBUSB_ERROR_BUSY: + return "Resource busy"; + case LIBUSB_ERROR_TIMEOUT: + return "Operation timed out"; + case LIBUSB_ERROR_OVERFLOW: + return "Overflow"; + case LIBUSB_ERROR_PIPE: + return "Pipe error"; + case LIBUSB_ERROR_INTERRUPTED: + return "System call interrupted (perhaps due to signal)"; + case LIBUSB_ERROR_NO_MEM: + return "Insufficient memory"; + case LIBUSB_ERROR_NOT_SUPPORTED: + return "Operation not supported or unimplemented on this platform"; + case LIBUSB_ERROR_OTHER: + return "Other error"; + }; + snprintf(unknerr, sizeof(unknerr), "Unknown error code %d / 0x%02x", e, e); + return unknerr; +} + +static libusb_device_handle *hmcfgusb_find() { + libusb_device_handle *devh = NULL; + libusb_device **list; + ssize_t cnt; + ssize_t i; + int err; + + cnt = libusb_get_device_list(NULL, &list); + if (cnt < 0) { + fprintf(stderr, "Can't get USB device list: %d\n", (int)cnt); + return NULL; + } + + for (i = 0; i < cnt; i++){ + struct libusb_device_descriptor desc; + + err = libusb_get_device_descriptor(list[i], &desc); + if (err) + continue; + + if ((desc.idVendor == ID_VENDOR) && (desc.idProduct == ID_PRODUCT)) { + libusb_device *dev = list[i]; + + err = libusb_open(dev, &devh); + if (err) { + fprintf(stderr, "Can't open device: %s\n", usb_strerror(err)); + return NULL; + } + + err = libusb_detach_kernel_driver(devh, 0); + if ((err != 0) && (err != LIBUSB_ERROR_NOT_FOUND)) { + fprintf(stderr, "Can't detach kernel driver: %s\n", usb_strerror(err)); + return NULL; + } + + err = libusb_claim_interface(devh, 0); + if ((err != 0)) { + fprintf(stderr, "Can't claim interface: %s\n", usb_strerror(err)); + return NULL; + } + + return devh; + } + + } + + return NULL; +} + +int hmcfgusb_send(struct hmcfgusb_dev *usbdev, unsigned char* send_data, int len, int done) +{ + int err; + int cnt; + int ret; + + err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, len, &cnt, USB_TIMEOUT); + if (err) { + fprintf(stderr, "Can't send data: %s\n", usb_strerror(err)); + if (err == LIBUSB_ERROR_NO_DEVICE) + exit(EXIT_FAILURE); + return 0; + } + + if (done) { + err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, 0, &cnt, USB_TIMEOUT); + if (err) { + fprintf(stderr, "Can't send data: %s\n", usb_strerror(err)); + if (err == LIBUSB_ERROR_NO_DEVICE) + exit(EXIT_FAILURE); + return 0; + } + } + + return ret; +} + +static struct libusb_transfer *hmcfgusb_prepare_int(libusb_device_handle *devh, libusb_transfer_cb_fn cb, void *data) +{ + unsigned char *data_buf; + struct libusb_transfer *transfer; + int err; + + data_buf = malloc(ASYNC_SIZE); + if (!data_buf) { + fprintf(stderr, "Can't allocate memory for data-buffer!\n"); + return NULL; + } + + transfer = libusb_alloc_transfer(0); + if (!transfer) { + fprintf(stderr, "Can't allocate memory for usb-transfer!\n"); + free(data_buf); + return NULL; + } + + libusb_fill_interrupt_transfer(transfer, devh, EP_IN, + data_buf, ASYNC_SIZE, cb, data, USB_TIMEOUT); + + transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK; + + err = libusb_submit_transfer(transfer); + if (err != 0) { + fprintf(stderr, "Can't submit transfer: %s\n", usb_strerror(err)); + libusb_free_transfer(transfer); + free(data_buf); + return NULL; + } + + return transfer; +} + +struct hmcfgusb_cb_data { + hmcfgusb_cb_fn cb; + void *data; +}; + +static void LIBUSB_CALL hmcfgusb_interrupt(struct libusb_transfer *transfer) +{ + int err; + struct hmcfgusb_cb_data *cb_data; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + if (transfer->status != LIBUSB_TRANSFER_TIMED_OUT) { + fprintf(stderr, "Interrupt transfer not completed: %d!\n", transfer->status); + quit = EIO; + return; + } + + err = libusb_submit_transfer(transfer); + if (err != 0) { + fprintf(stderr, "Can't re-submit transfer: %s\n", usb_strerror(err)); + free(transfer->buffer); + libusb_free_transfer(transfer); + } + return; + } + + cb_data = transfer->user_data; + if (cb_data && cb_data->cb) { + cb_data->cb(transfer->buffer, transfer->actual_length, cb_data->data); + } else { + hexdump(transfer->buffer, transfer->actual_length, "RECV> "); + } + + err = libusb_submit_transfer(transfer); + if (err != 0) { + fprintf(stderr, "Can't re-submit transfer: %s\n", usb_strerror(err)); + free(transfer->buffer); + libusb_free_transfer(transfer); + } +} + +struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data) +{ + libusb_device_handle *devh = NULL; + const struct libusb_pollfd **usb_pfd = NULL; + struct hmcfgusb_dev *dev = NULL; + struct hmcfgusb_cb_data *cb_data = NULL; + int err; + int i; + + err = libusb_init(NULL); + if (err != 0) { + fprintf(stderr, "Can't initialize libusb: %s\n", usb_strerror(err)); + return NULL; + } + + devh = hmcfgusb_find(); + if (!devh) { + fprintf(stderr, "Can't find/open hmcfgusb!\n"); + return NULL; + } + + dev = malloc(sizeof(struct hmcfgusb_dev)); + if (!dev) { + perror("Can't allocate memory for hmcfgusb_dev"); + return NULL; + } + + memset(dev, 0, sizeof(struct hmcfgusb_dev)); + dev->usb_devh = devh; + + cb_data = malloc(sizeof(struct hmcfgusb_cb_data)); + if (!cb_data) { + perror("Can't allocate memory for hmcfgusb_cb_data"); + return NULL; + } + + memset(cb_data, 0, sizeof(struct hmcfgusb_cb_data)); + + cb_data->cb = cb; + cb_data->data = data; + + dev->transfer = hmcfgusb_prepare_int(devh, hmcfgusb_interrupt, cb_data); + if (!dev->transfer) { + fprintf(stderr, "Can't prepare async device io!\n"); + return NULL; + } + + usb_pfd = libusb_get_pollfds(NULL); + if (!usb_pfd) { + fprintf(stderr, "Can't get FDset from libusb!\n"); + free(dev); + return NULL; + } + + dev->n_usb_pfd = 0; + for(i = 0; usb_pfd[i]; i++) + dev->n_usb_pfd++; + + dev->pfd = malloc(dev->n_usb_pfd * sizeof(struct pollfd)); + if (!dev->pfd) { + perror("Can't allocate memory for poll-fds"); + return NULL; + } + + memset(dev->pfd, 0, dev->n_usb_pfd * sizeof(struct pollfd)); + + for (i = 0; i < dev->n_usb_pfd; i++) { + dev->pfd[i].fd = usb_pfd[i]->fd; + dev->pfd[i].events = usb_pfd[i]->events; + dev->pfd[i].revents = 0; + } + + free(usb_pfd); + + dev->n_pfd = dev->n_usb_pfd; + + return dev; +} + +int hmcfgusb_add_pfd(struct hmcfgusb_dev *dev, int fd, short events) +{ + dev->n_pfd++; + dev->pfd = realloc(dev->pfd, dev->n_pfd * sizeof(struct pollfd)); + if (!dev->pfd) { + perror("Can't realloc poll-fds"); + return 0; + } + + dev->pfd[dev->n_pfd-1].fd = fd; + dev->pfd[dev->n_pfd-1].events = events; + dev->pfd[dev->n_pfd-1].revents = 0; + + return 1; +} + +int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout) +{ + struct timeval tv; + int usb_event = 0; + int i; + int n; + int fd_n; + int err; + + errno = 0; + + memset(&tv, 0, sizeof(tv)); + err = libusb_get_next_timeout(NULL, &tv); + if (err < 0) { + fprintf(stderr, "libusb_get_next_timeout: %s\n", usb_strerror(err)); + errno = EIO; + return -1; + } else if (err == 0) { + /* No pending timeout or a sane platform */ + tv.tv_sec = timeout; + } else { + if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) { + usb_event = 1; + } + } + + if (!usb_event) { + for (i = 0; i < dev->n_pfd; i++) { + dev->pfd[i].revents = 0; + } + + n = poll(dev->pfd, dev->n_pfd, tv.tv_sec * 1000); + if (n < 0) { + perror("poll"); + return -1; + } else if (n == 0) { + usb_event = 1; + } else { + for (fd_n = 0; fd_n < dev->n_pfd; fd_n++) { + if (dev->pfd[fd_n].revents) { + if (fd_n < dev->n_usb_pfd) { + usb_event = 1; + break; + } else { + return dev->pfd[fd_n].fd; + } + } + } + } + } + + if (usb_event) { + memset(&tv, 0, sizeof(tv)); + err = libusb_handle_events_timeout_completed(NULL, &tv, NULL); + if (err < 0) { + fprintf(stderr, "libusb_handle_events_completed: %s\n", usb_strerror(err)); + errno = EIO; + return -1; + } + } + + if (quit) + errno = quit; + + return -1; +} diff --git a/hmcfgusb.h b/hmcfgusb.h new file mode 100644 index 0000000..98b5085 --- /dev/null +++ b/hmcfgusb.h @@ -0,0 +1,37 @@ +/* HM-CFG-USB libusb-driver + * + * Copyright (c) 2013 Michael Gernoth + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +typedef void (*hmcfgusb_cb_fn)(uint8_t *buf, int buf_len, void *data); + +struct hmcfgusb_dev { + libusb_device_handle *usb_devh; + struct libusb_transfer *transfer; + int n_usb_pfd; + struct pollfd *pfd; + int n_pfd; +}; + +int hmcfgusb_send(struct hmcfgusb_dev *usbdev, unsigned char* send_data, int len, int done); +struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data); +int hmcfgusb_add_pfd(struct hmcfgusb_dev *dev, int fd, short events); +int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout); diff --git a/hmcfgusb.rules b/hmcfgusb.rules new file mode 100644 index 0000000..a2e7fba --- /dev/null +++ b/hmcfgusb.rules @@ -0,0 +1 @@ +SUBSYSTEMS=="usb" ATTRS{idVendor}=="1b1f" ATTRS{idProduct}=="c00f" MODE:="0666" diff --git a/hmland.c b/hmland.c new file mode 100644 index 0000000..75d4e14 --- /dev/null +++ b/hmland.c @@ -0,0 +1,222 @@ +/* HM-CFG-LAN emuldation for HM-CFG-USB + * + * Copyright (c) 2013 Michael Gernoth + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hexdump.h" +#include "hmcfgusb.h" + +void hmlan_format_out(uint8_t *buf, int buf_len, void *data) +{ + int len; + int i; + + if (buf_len < 1) + return; + + //FIXME: Buffer here and write to fd_out + printf("%c", buf[0]); + switch(buf[0]) { + case 'H': + buf[5]='L'; + buf[6]='A'; + buf[7]='N'; + len = buf[1]; + for (i = 2; i < len + 2; i++) { + printf("%c", buf[i]); + } + printf(",%02X%02X,", buf[i], + buf[i+1]); + i+=2; + len = buf[i]+i+1; + i++; + for (; i < len; i++) { + printf("%c", buf[i]); + } + printf(","); + len = i+12; + for (; i < len; i++) { + printf("%02X", buf[i]); + switch(len-i) { + case 10: + case 7: + case 3: + printf(","); + break; + default: + break; + } + } + + break; + case 'E': + len = 13 + buf[13]; + for (i = 0; i < len; i++) { + if (i != 12) + printf("%02X", buf[1+i]); + switch(i) { + case 2: + case 4: + case 8: + case 9: + case 11: + printf(","); + break; + default: + break; + } + } + break; + case 'R': + len = 14 + buf[14]; + for (i = 0; i < len; i++) { + if (i != 13) + printf("%02X", buf[1+i]); + switch(i) { + case 3: + case 5: + case 9: + case 10: + case 12: + printf(","); + break; + default: + break; + } + } + break; + case 'I': + //HM> 0x0000: 49 00 00 00 00 55 53 42 2d 49 46 03 bc 0a 4a 45 I....USB-IF...JE + //HM> 0x0010: 51 30 35 33 35 31 32 32 1d b1 55 68 ea 13 00 14 Q0535122..Uh.... + //HM> 0x0020: 9f a6 00 03 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //HM> 0x0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + default: + for (i = 1; i < buf_len; i++) + printf("%02X", buf[i]); + hexdump(buf, buf_len, "Unknown> "); + break; + } + printf("\n"); +} + +void hmlan_parse_in(int fd, void *data) +{ + struct hmcfgusb_dev *dev = data; + unsigned char buf[1024]; + unsigned char send_buf[0x40]; //FIXME!!! + char tmp[3]; + int i; + int r; + + r = read(fd, buf, sizeof(buf)); + if (r > 0) { + int cnt; + + memset(send_buf, 0, sizeof(send_buf)); + for (i = 0; i < r; i++) { + if ((buf[i] == 0x0a) || + (buf[i] == 0x0d)) { + r = i; + break; + } + } + + send_buf[0] = buf[0]; + + cnt = 0; + for (i = 1; i < r; i++) { + if (buf[i] == ',') { + switch (buf[0]) { + case 'S': + if (cnt == 4) { + /* Add msg length */ + memmove(buf+i+2, buf+i+1, r-(i+1)); + snprintf(tmp, 3, "%02X", (int)((r-(i+1))/2)); + memcpy(buf+i, tmp, 2); + r++; + break; + } + default: + memmove(buf+i, buf+i+1, r-(i+1)); + r--; + break; + } + cnt++; + } + } + + memset(tmp, 0, sizeof(tmp)); + for (i = 1; i < r; i+=2) { + memcpy(tmp, buf + i, 2); + send_buf[1+(i/2)] = strtoul(tmp, NULL, 16); + } + hmcfgusb_send(dev, send_buf, 1+(i/2), 1); + } else if (r < 0) { + perror("read"); + } +} + +int main(int argc, char **argv) +{ + struct hmcfgusb_dev *dev; + int quit = 0; + + dev = hmcfgusb_init(hmlan_format_out, NULL); + if (!dev) { + fprintf(stderr, "Can't initialize HM-CFG-USB!\n"); + exit(EXIT_FAILURE); + } + + if (!hmcfgusb_add_pfd(dev, STDIN_FILENO, POLLIN)) { + fprintf(stderr, "Can't add stdin to pollfd!\n"); + exit(EXIT_FAILURE); + } + + hmcfgusb_send(dev, (unsigned char*)"K", 1, 1); + + while(!quit) { + int fd; + + fd = hmcfgusb_poll(dev, 3600); + if (fd >= 0) { + hmlan_parse_in(fd, dev); + } else if (fd == -1) { + if (errno) { + perror("hmcfgusb_poll"); + quit = 1; + } + } + } + + libusb_close(NULL); + + return EXIT_SUCCESS; +} -- 2.39.2