diff options
Diffstat (limited to 'userland/cp210x.c')
-rw-r--r-- | userland/cp210x.c | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/userland/cp210x.c b/userland/cp210x.c new file mode 100644 index 0000000..2c1892b --- /dev/null +++ b/userland/cp210x.c @@ -0,0 +1,534 @@ +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <malloc.h> +#include <time.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <endian.h> + +#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); +} |