#include #include #include #include #include #include #include #include #include #include #include #include "cp210x_int.h" #include "cp210x.h" #define cpu_to_le32(a) htole32(a) #define le32_to_cpu(a) le32toh(a) #define cpu_to_be16(a) htobe16(a) #define be16_to_cpu(a) be16toh(a) #define TIMEOUT 300 static int cp210x_ctlmsg (CP210X * c, uint8_t request, uint8_t requestype, uint16_t value, uint16_t index, void *data, uint16_t size) { uint8_t *tbuf; int ret; if (!(tbuf = malloc (size))) return -ENOMEM; if (requestype & 0x80) { ret = libusb_control_transfer (c->handle, requestype, request, value, index, tbuf, size, TIMEOUT); if (ret > 0 && size) memcpy (data, tbuf, size); } else { if (size) memcpy (tbuf, data, size); ret = libusb_control_transfer (c->handle, requestype, request, value, index, tbuf, size, TIMEOUT); } free (tbuf); if (ret < 0 && ret != -EPIPE) { fprintf (stderr, "cp210x: control failed cmd rqt %u " "rq %u len %u ret %d\n", requestype, request, size, ret); } return ret; } static int cp210x_get_config (CP210X * c, uint8_t request, unsigned int *data, int size) { uint32_t *buf; int result, i, length; /* Number of integers required to contain the array */ length = (((size - 1) | 3) + 1) / 4; buf = calloc (length, sizeof (uint32_t)); if (!buf) { fprintf (stderr, "%s - out of memory.\n", __FUNCTION__); return -ENOMEM; } /* Issue the request, attempting to read 'size' bytes */ result = libusb_control_transfer (c->handle, REQTYPE_INTERFACE_TO_HOST, request, 0x0000, c->interface, (uint8_t *) buf, size, TIMEOUT); /* Convert data into an array of integers */ for (i = 0; i < length; i++) data[i] = le32_to_cpu (buf[i]); free (buf); if (result != size) { fprintf (stderr, "%s - Unable to send config request, request=0x%x size=%d result=%d\n", __FUNCTION__, request, size, result); if (result > 0) result = -EPROTO; return result; } return 0; } int cp210x_set_config (CP210X * c, uint8_t request, unsigned int *data, int size) { uint32_t *buf; int result, i, length; /* Number of integers required to contain the array */ length = (((size - 1) | 3) + 1) / 4; buf = malloc (length * sizeof (uint32_t)); if (!buf) { fprintf (stderr, "%s - out of memory.\n", __FUNCTION__); return -ENOMEM; } /* Array of integers into bytes */ for (i = 0; i < length; i++) buf[i] = cpu_to_le32 (data[i]); if (size > 2) { result = libusb_control_transfer (c->handle, REQTYPE_HOST_TO_INTERFACE, request, 0x0000, c->interface, (uint8_t *) buf, size, TIMEOUT); } else { result = libusb_control_transfer (c->handle, REQTYPE_HOST_TO_INTERFACE, request, data[0], c->interface, NULL, 0, TIMEOUT); } free (buf); if ((size > 2 && result != size) || result < 0) { fprintf (stderr, "%s - Unable to send request, request=0x%x size=%d result=%d\n", __FUNCTION__, request, size, result); if (result > 0) result = -EPROTO; return result; } return 0; } static int cp210x_set_config_single (CP210X * c, uint8_t request, unsigned int data) { return cp210x_set_config (c, request, &data, 2); } static int cp210x_set_u16 (CP210X * c, int cmd, unsigned int value) { return cp210x_ctlmsg (c, 0xff, 0x40, 0x3700 | (cmd & 0xff), value, 0, 0); } static int cp210x_set_string (CP210X * c, int cmd, uint8_t * usbstr, size_t len, size_t maxlen) { int ret; if (len > maxlen) return -EMSGSIZE; ret = cp210x_ctlmsg (c, 0xff, 0x40, 0x3700 | (cmd & 0xff), 0, usbstr, len); fprintf (stderr, "%s - cmd 0x%02x len %d ret %d %x %x %x %x %x %x\n", __FUNCTION__, cmd, (int) len, ret, usbstr[0], usbstr[1], usbstr[2], usbstr[3], usbstr[4], usbstr[5]); return ret; } int cp210x_set_gpio (CP210X * c, uint8_t gpio) { return cp210x_ctlmsg (c, 0xff, REQTYPE_HOST_TO_DEVICE, 0x37e1, ((uint16_t) gpio << 8) | CP_GPIO_MASK, NULL, 0); } int cp210x_set_portconf (CP210X * c, struct cp210x_port_config *config) { int ret; struct cp210x_port_config be_config; memset (&be_config, 0, sizeof (be_config)); be_config.reset.mode = cpu_to_be16 (config->reset.mode); be_config.reset.low_power = 0; // cpu_to_be16(config->reset.low_power); be_config.reset.latch = cpu_to_be16 (config->reset.latch); be_config.suspend.mode = cpu_to_be16 (config->suspend.mode); be_config.suspend.low_power = 0; // cpu_to_be16(config->suspend.low_power); be_config.suspend.latch = cpu_to_be16 (config->suspend.latch); be_config.enhanced_fxn = config->enhanced_fxn; ret = cp210x_ctlmsg (c, 0xff, REQTYPE_HOST_TO_DEVICE, 0x370c, 0, &be_config, sizeof (be_config)); if (ret == sizeof (be_config)) return 0; else if (ret >= 0) return -EPROTO; else return ret; } int cp210x_get_portconf (CP210X * c, struct cp210x_port_config *config) { int ret; ret = cp210x_ctlmsg (c, 0xff, REQTYPE_DEVICE_TO_HOST, 0x370c, 0, config, sizeof (*config)); if (ret != sizeof (*config)) return (ret >= 0) ? -EPROTO : ret; /* Words from cp2103 are MSB */ config->reset.mode = be16_to_cpu (config->reset.mode); config->reset.low_power = be16_to_cpu (config->reset.low_power); config->reset.latch = be16_to_cpu (config->reset.latch); config->suspend.mode = be16_to_cpu (config->suspend.mode); config->suspend.low_power = be16_to_cpu (config->suspend.low_power); config->suspend.latch = be16_to_cpu (config->suspend.latch); /* apparently not implemented yet */ config->reset.low_power = 0; config->suspend.low_power = 0; return 0; } int cp210x_set_dtr (CP210X * c, int on) { unsigned int control; control = on ? CONTROL_DTR : 0; control |= CONTROL_WRITE_DTR; return cp210x_set_config (c, CP210X_SET_MHS, &control, 2); } int cp210x_set_vid (CP210X * c, uint16_t vid) { return cp210x_set_u16 (c, 1, vid); } int cp210x_set_pid (CP210X * c, uint16_t pid) { return cp210x_set_u16 (c, 2, pid); } int cp210x_set_dev_ver (CP210X * c, uint16_t ver) { return cp210x_set_u16 (c, 7, ver); } ssize_t cp210x_read (CP210X * c, void *buf, size_t len, unsigned int timeout) { int red = 0; int err; timeout /= 1000; err = libusb_bulk_transfer (c->handle, 0x81, buf, len, &red, timeout); switch (err) { case 0: break; case LIBUSB_ERROR_TIMEOUT: return 0; default: return -1; } return red; } int cp210x_set_mfg (CP210X * c, uint8_t *s,int len) { return cp210x_set_string (c, 0x0, s, len, CP210X_MAX_MFG_STRLEN); } int cp210x_set_product (CP210X * c, uint8_t *s,int len) { return cp210x_set_string (c, 0x3, s, len, CP210X_MAX_PRODUCT_STRLEN); } int cp210x_set_serial (CP210X * c, uint8_t *s,int len) { return cp210x_set_string (c, 0x4, s, len, CP210X_MAX_SERIAL_STRLEN); } int cp210x_setup (CP210X * c) { unsigned int bits; unsigned int modem_ctl[4]; uint32_t baud; if (cp210x_set_config_single (c, CP210X_IFC_ENABLE, UART_ENABLE)) { fprintf (stderr, "failed to start uart\n"); return -1; } baud = 115200; if (cp210x_set_config (c, CP210X_SET_BAUDRATE, &baud, sizeof (baud))) { fprintf (stderr, "failed to set baud rate to %u\n", baud); return -1; } cp210x_get_config (c, CP210X_GET_LINE_CTL, &bits, 2); bits &= ~BITS_DATA_MASK; bits |= BITS_DATA_8; if (cp210x_set_config (c, CP210X_SET_LINE_CTL, &bits, 2)) { fprintf (stderr, "Number of data bits requested not supported by device\n"); return -1; } cp210x_get_config (c, CP210X_GET_LINE_CTL, &bits, 2); bits &= ~BITS_PARITY_MASK; bits |= BITS_PARITY_SPACE; if (cp210x_set_config (c, CP210X_SET_LINE_CTL, &bits, 2)) { fprintf (stderr, "Parity mode not supported by device\n"); return -1; } cp210x_get_config (c, CP210X_GET_LINE_CTL, &bits, 2); bits &= ~BITS_STOP_MASK; bits |= BITS_STOP_1; if (cp210x_set_config (c, CP210X_SET_LINE_CTL, &bits, 2)) { fprintf (stderr, "Number of stop bits requested not supported by device\n"); return -1; } cp210x_get_config (c, CP210X_GET_FLOW, modem_ctl, 16); modem_ctl[0] &= ~0x7B; modem_ctl[0] |= 0x01; modem_ctl[1] |= 0x40; if (cp210x_set_config (c, CP210X_SET_FLOW, modem_ctl, 16)) { fprintf (stderr, "Couldn't disable modem control\n"); return -1; } return 0; } static libusb_device * find_device (void) { libusb_device **list; libusb_device *found = NULL; ssize_t cnt; ssize_t i = 0; libusb_init (NULL); cnt = libusb_get_device_list (NULL, &list); if (cnt < 0) return NULL; for (i = 0; i < cnt; i++) { struct libusb_device_descriptor desc = { 0 }; libusb_device *device = list[i]; libusb_get_device_descriptor (device, &desc); if (((desc.idVendor == 0x10c4) && (desc.idProduct == 0xea60)) ||((desc.idVendor == 0x413c) && (desc.idProduct == 0x9500))) { libusb_ref_device (device); libusb_free_device_list (list, 1); return device; break; } } libusb_free_device_list (list, 1); return NULL; } int cp210x_reset (CP210X * c) { libusb_reset_device (c->handle); if (libusb_set_configuration (c->handle, 1) < 0 || libusb_claim_interface (c->handle, 0) < 0) return -1; if (cp210x_setup (c)) return -1; return 0; } CP210X * cp210x_open (void) { CP210X *c = malloc (sizeof (CP210X)); c->device = find_device (); c->handle = NULL; if (!c->device) { free (c); return NULL; } libusb_open (c->device, &c->handle); if (!c->handle) { free (c); return NULL; } libusb_detach_kernel_driver (c->handle, 0); libusb_detach_kernel_driver (c->handle, 1); libusb_reset_device (c->handle); c->interface = 0; if (libusb_set_configuration (c->handle, 1) < 0 || libusb_claim_interface (c->handle, 0) < 0) { libusb_close (c->handle); libusb_unref_device (c->device); free (c); return NULL; } if (cp210x_setup (c)) { libusb_close (c->handle); libusb_unref_device (c->device); free (c); return NULL; } return c; } void cp210x_close (CP210X * c) { libusb_close (c->handle); libusb_unref_device (c->device); free (c); }