libusb-based driver for HM-CFG-USB and an initial HM-CFG-LAN emulation
authorMichael Gernoth <michael@gernoth.net>
Wed, 29 May 2013 14:03:32 +0000 (16:03 +0200)
committerMichael Gernoth <michael@gernoth.net>
Wed, 29 May 2013 14:03:32 +0000 (16:03 +0200)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
hexdump.h [new file with mode: 0644]
hmcfgusb.c [new file with mode: 0644]
hmcfgusb.h [new file with mode: 0644]
hmcfgusb.rules [new file with mode: 0644]
hmland.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..2056e43
--- /dev/null
@@ -0,0 +1,5 @@
+hmcfgusb.d
+hmcfgusb.o
+hmland
+hmland.d
+hmland.o
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..286435c
--- /dev/null
+++ b/hexdump.h
@@ -0,0 +1,58 @@
+/* simple hexdumper
+ *
+ * Copyright (c) 2004-2013 Michael Gernoth <michael@gernoth.net>
+ *
+ * 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 (file)
index 0000000..0e45cbc
--- /dev/null
@@ -0,0 +1,402 @@
+/* HM-CFG-USB libusb-driver
+ *
+ * Copyright (c) 2013 Michael Gernoth <michael@gernoth.net>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <poll.h>
+#include <errno.h>
+#include <libusb-1.0/libusb.h>
+
+#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 (file)
index 0000000..98b5085
--- /dev/null
@@ -0,0 +1,37 @@
+/* HM-CFG-USB libusb-driver
+ *
+ * Copyright (c) 2013 Michael Gernoth <michael@gernoth.net>
+ *
+ * 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 (file)
index 0000000..a2e7fba
--- /dev/null
@@ -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 (file)
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 <michael@gernoth.net>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <poll.h>
+#include <errno.h>
+#include <libusb-1.0/libusb.h>
+
+#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;
+}
Impressum, Datenschutz