--- /dev/null
+/* 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");
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}