]> oss.titaniummirror.com Git - cp210x.git/commitdiff
This is the version of the driver modified by TMI to support setting of the
authorsmckown <smckown@986fd584-583e-0410-b54d-b9fe63dff8e5>
Wed, 7 Nov 2007 15:52:45 +0000 (15:52 +0000)
committersmckown <smckown@986fd584-583e-0410-b54d-b9fe63dff8e5>
Wed, 7 Nov 2007 15:52:45 +0000 (15:52 +0000)
USB descriptors, configure GPIO, and read/write GPIO pins.  Some examples are
included, as well as a README.txt to build the code.  The rpm construction
tools have not been updated.

README.txt [new file with mode: 0644]
cp2101.c
examples/Makefile [new file with mode: 0644]
examples/README.txt [new file with mode: 0644]
examples/cpio.c [new file with mode: 0644]
examples/cpmfg.c [new file with mode: 0644]
examples/cptwiddle.c [new file with mode: 0644]

diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..932cfcb
--- /dev/null
@@ -0,0 +1,22 @@
+To make the cp2101 driver from this code base:
+
+cp -a cp2103 cp2103.build
+cd cp2103.build
+./configure
+vi cp2101.c
+  replace #include "usb-serial.h" with
+  #include "/path/to/usb-serial.h"
+  The file is found in linux source; may have to download it
+make -f Makefile.go all
+sudo make -f Makefile.go install
+to test: sudo modprobe cp2101, or plug in a device
+
+For newer kernels (2.6.20+):
+
+cp -a cp2103 cp2103.build
+cd cp2103.build
+./configure -kver `uname -r`
+make -f Makefile.go all
+chmod a+x installmod
+sudo make -f Makefile.go install
+to test: sudo modprobe cp2101, or plug in a device
index 6664cb32dcf9a71ec00b69f0bc201b05b17c2206..8f371d266532d1c074fecc7c762ffdccd9b9882b 100644 (file)
--- a/cp2101.c
+++ b/cp2101.c
  * thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
  * control thanks to Munir Nassar nassarmu@real-time.com
  *
+ * Port configuration, usb descriptor and gpio management by R. Steve McKown
+ * (smckown@titaniummirror.com).
+ *
  * Outstanding Issues:
  *  Buffers are not flushed when the port is opened.
  *  Multiple calls to write() may fail with "Resource temporarily unavailable"
  *
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
@@ -27,6 +29,7 @@
 #include <linux/usb.h>
 #include <linux/version.h>
 #include <asm/uaccess.h>
+#include <linux/byteorder/generic.h>
 
 #ifndef KERNEL_VERSION
 #define KERNEL_VERSION(a,b,c) ((a)*65536 + (b)*256 + (c))
 #ifdef LINUX26
 
 #include <linux/moduleparam.h>
+#include <linux/usb/serial.h>
 #define DEV_ERR(dev,format,arg...) dev_err(dev,format, ## arg)
 #define USB_KILL_URB usb_kill_urb
 
 #else /* !LINUX26 */
 
+#include <linux/config.h>
+#include "usb-serial.h"
+
 #ifdef CONFIG_USB_SERIAL_DEBUG
        static int debug = 1;
 #else
 
 #endif /* LINUX26 */
 
-#include "usb-serial.h"
-
 /*
  * Version Information
  * RJM: updated to 0.11 for 2.6.15 and later kernels
  */
-#define DRIVER_VERSION "v0.11"
+#define DRIVER_VERSION "v0.11rsm"
 #define DRIVER_DESC "Silicon Labs CP2101/CP2102/CP2103 RS232 serial adaptor driver"
 
 /*
@@ -85,9 +90,26 @@ static int   cp2101_tiocmset (struct usb_serial_port *, struct file *, unsigned i
 static int debug;
 
 static struct usb_device_id id_table [] = {
-       { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
-       { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
-       { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+       { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
+       { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
+       { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+       { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
+       { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
+       { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
+       { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
+       { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+       { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
+       { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
+       { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */
+       { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */
+       { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
+       { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */
+       { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
+       { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */
+       { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
+       { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
+       { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
+       { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
        { } /* Terminating Entry */
 };
 
@@ -130,7 +152,7 @@ static struct usb_serial_device_type cp2101_device = {
        .num_ports                      = 1,
        .open                           = cp2101_open,
        .close                          = cp2101_close,
-       .ioctl                          = cp2101_ioctl,     /* GLA, Added backport */
+       .ioctl                          = cp2101_ioctl,     /* GLA, Added backport */
        .break_ctl                      = cp2101_break_ctl,
        .set_termios                    = cp2101_set_termios,
 #ifdef LINUX26
@@ -197,6 +219,95 @@ static struct usb_serial_device_type cp2101_device = {
 #define CONTROL_WRITE_DTR          0x0100
 #define CONTROL_WRITE_RTS          0x0200
 
+/* CP2103 ioctls */
+#define IOCTL_GPIOGET          0x8000  /* Get gpio bits */
+#define IOCTL_GPIOSET          0x8001  /* Set gpio bits */
+#define IOCTL_GPIOBIC          0x8002  /* Clear specific gpio bit(s) */
+#define IOCTL_GPIOBIS          0x8003  /* Set specific gpio bit(s) */
+
+/* CP210x ioctls principally used during initial device configuration */
+#define IOCTL_DEVICERESET      0x8004  /* Reset the cp210x */
+#define IOCTL_PORTCONFGET      0x8005  /* Get port configuration */
+#define IOCTL_PORTCONFSET      0x8006  /* Set port configuration */
+#define IOCTL_SETVID           0x8007  /* Set vendor id */
+#define IOCTL_SETPID           0x8008  /* Set product id */
+#define IOCTL_SETMFG           0x8009  /* Set manufacturer string */
+#define IOCTL_SETPRODUCT       0x800a  /* Set product string */
+#define IOCTL_SETSERIAL                0x800b  /* Set serial number string */
+#define IOCTL_SETDEVVER                0x800c  /* set device version id */
+/* FIXME: where is IOCTL_SETMFG? */
+
+/* CP2103 GPIO */
+#define GPIO_0         0x01
+#define GPIO_1         0x02
+#define GPIO_2         0x04
+#define GPIO_3         0x08
+#define GPIO_MASK      (GPIO_0|GPIO_1|GPIO_2|GPIO_3)
+
+/* GetDeviceVersion() return codes */
+#define                CP210x_CP2101_VERSION                           0x01
+#define                CP210x_CP2102_VERSION                           0x02
+#define                CP210x_CP2103_VERSION                           0x03
+                                                                                       
+/* Return codes */
+#define                CP210x_SUCCESS                                          0x00
+#define                CP210x_DEVICE_NOT_FOUND                         0xFF
+#define                CP210x_INVALID_HANDLE                           0x01
+#define                CP210x_INVALID_PARAMETER                        0x02
+#define                CP210x_DEVICE_IO_FAILED                         0x03
+#define                CP210x_FUNCTION_NOT_SUPPORTED           0x04
+#define                CP210x_GLOBAL_DATA_ERROR                        0x05
+#define                CP210x_FILE_ERROR                                       0x06
+#define                CP210x_COMMAND_FAILED                           0x08
+#define                CP210x_INVALID_ACCESS_TYPE                      0x09
+
+/* USB descriptor sizes */
+#define                CP210x_MAX_DEVICE_STRLEN                        256
+#define                CP210x_MAX_PRODUCT_STRLEN                       126
+#define                CP210x_MAX_SERIAL_STRLEN                        63
+#define                CP210x_MAX_MAXPOWER                                     250
+
+/* Used to pass variable sized buffers between user and kernel space (ioctls) */
+typedef struct {
+       char* buf;
+       size_t len;
+} cp2101_buffer_t;
+
+/* Port config definitions */
+typedef struct {
+    uint16_t mode;             /* Push-pull = 1, Open-drain = 0 */
+    uint16_t lowPower;
+    uint16_t latch;            /* Logic high = 1, Logic low = 0 */
+} cp2101_port_state_t;
+
+typedef struct {
+    cp2101_port_state_t reset;
+    cp2101_port_state_t suspend;
+    uint8_t enhancedFxn;
+} cp2101_port_config_t;
+
+#define PORT_CONFIG_LEN        13      /* Because sizeof() will pad to full words */
+
+
+/*
+ * cp2101_buf_from_user
+ * Copy a buffer from user space, returning the number of bytes copied
+ * from ubuf.buf into kbuf.  klen is the size of the buffer at kbuf.
+ */
+size_t copy_buf_from_user(char* kbuf, unsigned long ubuf, size_t klen)
+{
+    cp2101_buffer_t t;
+
+       if (!kbuf || !ubuf || !klen || copy_from_user(&t, (cp2101_buffer_t *)ubuf,
+                       sizeof(t)))
+               return 0;
+       if (t.len < klen)
+               klen = t.len;
+       if (!t.buf || !t.len || copy_from_user(kbuf, t.buf, klen))
+               return 0;
+       return klen;
+}
+
 /*
  * cp2101_get_config
  * Reads from the CP2101 configuration registers
@@ -336,6 +447,132 @@ static inline int cp2101_set_config_single(struct usb_serial_port* port,
        return cp2101_set_config(port, request, &data, 2);
 }
 
+/*
+ * cp2101_ctlmsg
+ * A generic usb control message interface.
+ * Returns the actual size of the data read or written within the message, 0
+ * if no data were read or written, or a negative value to indicate an error.
+ */
+static int cp2101_ctlmsg(struct usb_serial_port* port, u8 request,
+               u8 requestype, u16 value, u16 index, void* data, u16 size)
+{
+       struct usb_device *dev = port->serial->dev;
+       u8 *tbuf;
+       int ret;
+
+       if (!(tbuf = kmalloc(size, GFP_KERNEL)))
+               return -ENOMEM;
+       if (requestype & 0x80) {
+               ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
+                               requestype, value, index, tbuf, size, 300);
+               if (ret > 0 && size)
+                       memcpy(data, tbuf, size);
+       } else {
+               if (size)
+                       memcpy(tbuf, data, size);
+               ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+                               requestype, value, index, tbuf, size, 300);
+       }
+       kfree(tbuf);
+       if (ret < 0 && ret != -EPIPE) {
+               dev_printk(KERN_DEBUG, &dev->dev, "cp2101: control failed cmd rqt %u "
+                               "rq %u len %u ret %d\n", requestype, request, size, ret);
+       }
+       return ret;
+}
+
+static int cp2101_reset(struct usb_serial_port *port)
+{
+       dbg("%s", __FUNCTION__);
+
+#if 1
+    /* Is this better than usb_device_reset?  It may be.  Once a client issues
+        * the reset ioctl, it must disconnect and reconnect, since the USB
+        * connections are torn down.  We also ignore the error return, since the
+        * part resets and doesn't send one...
+        */
+       cp2101_ctlmsg(port, 0xff, 0x40, 0x0008, 0x00, 0, 0);
+#else
+       usb_reset_device(port->serial->dev);
+#endif
+       return 0;
+}
+
+static int cp2101_get_partnum(struct usb_serial_port *port)
+{
+       static u8 _partnum = 0;
+       int ret = CP210x_SUCCESS;
+       if (!_partnum) {
+               u8 addr = port->serial->dev->actconfig->interface[0]->cur_altsetting->
+                       endpoint[0].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+               if (addr == 0x03 || addr == 0x02) /* The part is a cp2101 */
+                       _partnum = 0x01;
+               else if (addr == 0x01) /* Must query part to determine part number */
+                       ret = (cp2101_ctlmsg(port, 0xff, 0xc0, 0x370b, 0x00, &_partnum, 1)
+                                       == 1) ? CP210x_SUCCESS : CP210x_DEVICE_IO_FAILED;
+               else
+                       ret = CP210x_DEVICE_IO_FAILED;
+       }
+       dbg("%s - partnum %u err %d", __FUNCTION__, _partnum, ret);
+       return (ret == CP210x_SUCCESS) ? _partnum : 0;
+}
+
+inline int cp2101_setu16(struct usb_serial_port *port, int cmd,
+               unsigned int value)
+{
+       return cp2101_ctlmsg(port, 0xff, 0x40, 3700 | (cmd & 0xff), value, 0, 0);
+}
+
+/* Populates usbstr with: (len) + (0x03) + unicode(str).  Each char in str
+ * takes up two bytes in unicode format, so the resulting len(usbstr) is
+ * 2 * len(str) + 2.
+ * Returns the resulting length of the string in usbstr.
+ * This function can accept overlapping usbstr and str as long as the overlap
+ * does not cause data written to usbstr to overwrite data not yet read from
+ * str.
+ */
+static int make_usb_string(char* usbstr, size_t usblen, char* src,
+               size_t srclen)
+{
+       int len = 0;
+
+       if (usbstr && usblen >= 2 && src && *src && srclen) {
+               char* p;
+
+               /* The usb string format uses a single byte to represent length */
+               if (usblen > 256)
+                       usblen = 256;
+
+               p = usbstr + 1;
+               *p++ = 0x03;
+               len = 2;
+               while (srclen && len < usblen) {
+                       *p++ = *src++;
+                       *p++ = 0;
+                       len += 2;
+                       srclen--;
+               }
+               *usbstr = (char)len;
+       }
+       return len;
+}
+
+/*
+ * cp2101_setstr
+ *
+ * Set a USB string descriptor using proprietary cp210x control messages.
+ * Return the number of characters actually written.
+ */
+inline int cp2101_setstr(struct usb_serial_port *port, int cmd, char *usbstr)
+{
+       unsigned len = usbstr[0];
+       int ret = cp2101_ctlmsg(port, 0xff, 0x40, 0x3700 | (cmd & 0xff), 0, usbstr,
+                       len);
+       dbg("%s - cmd 0x%02x len %d ret %d", __FUNCTION__, cmd, len, ret);
+       return ret;
+}
+
 static int cp2101_open (struct usb_serial_port *port, struct file *filp)
 {
        struct usb_serial *serial = port->serial;
@@ -809,7 +1046,120 @@ static void cp2101_break_ctl (struct usb_serial_port *port, int break_state)
        cp2101_set_config(port, CP2101_BREAK, &state, 2);
 }
 
-static int cp2101_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
+/* Set all gpio simultaneously */
+static int cp2101_gpioset(struct usb_serial_port *port, u8 gpio)
+{
+       dbg("%s - port %d, gpio = 0x%.2x", __FUNCTION__, port->number, gpio);
+
+    return cp2101_ctlmsg(port, 0xff, 0x40, 0x37e1,
+                       ((uint16_t)gpio << 8) | GPIO_MASK, 0, 0);
+}
+
+/* Set select gpio bits */
+static int cp2101_gpiosetb(struct usb_serial_port *port, u8 set, u8 clear)
+{
+       u16 gpio = 0;
+
+       /* The bitmask is in the LSB, the values in the MSB */
+       if (set & GPIO_0)
+               gpio |= (GPIO_0 << 8)|GPIO_0;
+       if (set & GPIO_1)
+               gpio |= (GPIO_1 << 8)|GPIO_1;
+       if (set & GPIO_2)
+               gpio |= (GPIO_2 << 8)|GPIO_2;
+       if (set & GPIO_3)
+               gpio |= (GPIO_3 << 8)|GPIO_3;
+       if (clear & GPIO_0)
+               gpio = (gpio & ~(GPIO_0 << 8))|GPIO_0;
+       if (clear & GPIO_1)
+               gpio = (gpio & ~(GPIO_1 << 8))|GPIO_1;
+       if (clear & GPIO_2)
+               gpio = (gpio & ~(GPIO_2 << 8))|GPIO_2;
+       if (clear & GPIO_3)
+               gpio = (gpio & ~(GPIO_3 << 8))|GPIO_3;
+
+       dbg("%s - port %d, gpiob = 0x%.4x", __FUNCTION__, port->number, gpio);
+
+       /* FIXME: how about REQTYPE_HOST_TO_DEVICE instead of 0x40? */
+    return cp2101_ctlmsg(port, 0xff, 0x40, 0x37e1, gpio, 0, 0);
+}
+
+static int cp2101_gpioget(struct usb_serial_port *port, u8* gpio)
+{
+       int ret;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* FIXME: how about REQTYPE_DEVICE_TO_HOST instead of 0xc0? */
+       ret = cp2101_ctlmsg(port, 0xff, 0xc0, 0x00c2, 0, gpio, 1);
+
+       dbg("%s - gpio = 0x%.2x (%d)", __FUNCTION__, *gpio, ret);
+
+       return (ret == 1) ? 0 : -1;
+}
+
+static int cp2101_portconfset(struct usb_serial_port *port,
+               cp2101_port_config_t* config)
+{
+       cp2101_port_config_t lconfig;
+       int ret;
+
+       dbg("%s", __FUNCTION__);
+
+       memcpy(&lconfig, config, sizeof(lconfig));
+
+       /* apparently not implemented yet */
+       lconfig.suspend.lowPower = 0;
+       lconfig.reset.lowPower = 0;
+
+       /* Words from cp2103 are MSB */
+       lconfig.reset.mode = cpu_to_be16(config->reset.mode);
+       /* lconfig.reset.lowPower = cpu_to_be16(config->reset.lowPower); */
+       lconfig.reset.latch = cpu_to_be16(config->reset.latch);
+       lconfig.suspend.mode = cpu_to_be16(config->suspend.mode);
+       /* lconfig.suspend.lowPower = cpu_to_be16(config->suspend.lowPower); */
+       lconfig.suspend.latch = cpu_to_be16(config->suspend.latch);
+
+       ret = cp2101_ctlmsg(port, 0xff, 0x40, 0x370c, 0, &lconfig, PORT_CONFIG_LEN);
+       if (ret == PORT_CONFIG_LEN)
+               return 0;
+       else if (ret >= 0)
+               return -1;
+       else
+               return ret;
+}
+
+static int cp2101_portconfget(struct usb_serial_port *port,
+               cp2101_port_config_t* config)
+{
+       int ret;
+
+       dbg("%s", __FUNCTION__);
+
+       ret = cp2101_ctlmsg(port, 0xff, 0xc0, 0x370c, 0, config, PORT_CONFIG_LEN);
+       if (ret == PORT_CONFIG_LEN) {
+               /* Words from cp2103 are MSB */
+               config->reset.mode = be16_to_cpu(config->reset.mode);
+               config->reset.lowPower = be16_to_cpu(config->reset.lowPower);
+               config->reset.latch = be16_to_cpu(config->reset.latch);
+               config->suspend.mode = be16_to_cpu(config->suspend.mode);
+               config->suspend.lowPower = be16_to_cpu(config->suspend.lowPower);
+               config->suspend.latch = be16_to_cpu(config->suspend.latch);
+
+               /* apparently not implemented yet */
+               config->reset.lowPower = 0;
+               config->suspend.lowPower = 0;
+
+               return 0;
+       } else if (ret >= 0)
+               return -1;
+       else
+               return ret;
+
+}
+
+static int cp2101_ioctl (struct usb_serial_port *port, struct file *file,
+               unsigned int cmd, unsigned long arg)
 {
        dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
 
@@ -821,6 +1171,7 @@ static int cp2101_ioctl (struct usb_serial_port *port, struct file *file, unsign
                 int result = cp2101_tiocmget(port, file);
                 if (copy_to_user(&arg, &result, sizeof(int)))
                                return -EFAULT; 
+                               return 0;
             }
             break;
             
@@ -830,15 +1181,143 @@ static int cp2101_ioctl (struct usb_serial_port *port, struct file *file, unsign
                case TIOCMBIC:
             {
                 int val = 0;
+
+                               if (copy_from_user(&val, &arg, sizeof(int)))
+                                       return -EFAULT;
                 
-                   if (copy_from_user(&val, &arg, sizeof(int)))
-                           return -EFAULT;
-            
+/* this looks wrong:  TIOCMSET isn't going to work right */
                 if(cp2101_tiocmset(port, file, cmd==TIOCMBIC?0:val,  cmd==TIOCMBIC?val:0))
                     return -EFAULT;
+                               return 0;
+            }
+            break;
+#endif
+
+               case IOCTL_GPIOGET:
+                       if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION) {
+                               u8 gpio = 0;
+                if (!cp2101_gpioget(port, &gpio) &&
+                                               !copy_to_user((u8*)arg, &gpio, sizeof(gpio)))
+                                       return 0;
+            }
+                       return -EFAULT;
+            break;
+            
+               case IOCTL_GPIOSET:
+                       if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION &&
+                                       !cp2101_gpioset(port, arg))
+                               return 0;
+                       return -EFAULT;
+            break;
+
+               case IOCTL_GPIOBIC:
+               case IOCTL_GPIOBIS:
+                       if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION &&
+                                       !cp2101_gpiosetb(port, (cmd==IOCTL_GPIOBIC) ? 0 : arg,
+                                               (cmd==IOCTL_GPIOBIC) ? arg : 0))
+                               return 0;
+                       return -EFAULT;
+            break;
+
+               case IOCTL_DEVICERESET:
+                       return cp2101_reset(port);
+                       break;
+
+               case IOCTL_PORTCONFGET:
+                       {
+                               cp2101_port_config_t config;
+                               if (!cp2101_portconfget(port, &config) &&
+                                               !copy_to_user((cp2101_port_config_t*)arg, &config,
+                                                       sizeof(config)))
+                                       return 0;
+            }
+                       return -EFAULT;
+                       break;
+
+               case IOCTL_PORTCONFSET:
+                       {
+                               cp2101_port_config_t config;
+                   if (!copy_from_user(&config, (cp2101_port_config_t*)arg,
+                                                       sizeof(config)) &&
+                                               !cp2101_portconfset(port, &config))
+                                       return 0;
+                               return -EFAULT;
+                       }
+                       break;
+
+               case IOCTL_SETVID:
+                       {
+                               unsigned int vid;
+                   if (!copy_from_user(&vid, (unsigned int *)arg,
+                                                       sizeof(unsigned int)) &&
+                                               !cp2101_setu16(port, 0x01, vid))
+                                       return 0;
+                               return -EFAULT;
+            }
+            break;
+
+               case IOCTL_SETPID:
+                       {
+                               unsigned int pid;
+                   if (!copy_from_user(&pid, (unsigned int *)arg,
+                                                       sizeof(unsigned int)) &&
+                                               !cp2101_setu16(port, 0x02, pid))
+                                       return 0;
+                               return -EFAULT;
             }
             break;
+
+               case IOCTL_SETMFG:
+#if 0 /* Don't know how to set manufacturer desc yet */
+                       {
+                               char usbstr[CP210x_MAX_MFG_STRLEN * 2 + 2];
+                   size_t len = copy_buf_from_user(
+                                               usbstr + sizeof(usbstr) - CP210x_MAX_MFG_STRLEN, arg,
+                                               CP210x_MAX_MFG_STRLEN);
+                               len = make_usb_string(usbstr, sizeof(usbstr), str, len);
+                               if (len && cp2101_setstr(port, 0x00, usbstr) == len)
+                                       return 0;
+                               return -EFAULT;
+            }
 #endif
+            break;
+
+               case IOCTL_SETPRODUCT:
+                       {
+                               char usbstr[CP210x_MAX_PRODUCT_STRLEN * 2 + 2];
+                               char* str = usbstr + sizeof(usbstr) - CP210x_MAX_PRODUCT_STRLEN;
+                   size_t len = copy_buf_from_user(str, arg,
+                                               CP210x_MAX_PRODUCT_STRLEN);
+                               len = make_usb_string(usbstr, sizeof(usbstr), str, len);
+                               if (len && cp2101_setstr(port, 0x03, usbstr) == len)
+                                       return 0;
+                               return -EFAULT;
+            }
+            break;
+
+               case IOCTL_SETSERIAL:
+                       {
+                               char usbstr[CP210x_MAX_SERIAL_STRLEN * 2 + 2];
+                               char* str = usbstr + sizeof(usbstr) - CP210x_MAX_SERIAL_STRLEN;
+                   size_t len = copy_buf_from_user(str, arg,
+                                               CP210x_MAX_SERIAL_STRLEN);
+                               len = make_usb_string(usbstr, sizeof(usbstr), str, len);
+                               if (len && cp2101_setstr(port, 0x04, usbstr) == len)
+                                       return 0;
+                               return -EFAULT;
+            }
+            break;
+
+               case IOCTL_SETDEVVER:
+                       {
+                               unsigned int ver;
+                   if (!copy_from_user(&ver, (unsigned int *)arg,
+                                                       sizeof(unsigned int)) &&
+                                               !cp2101_setu16(port, 0x07, ver))
+                                       return 0;
+                               return -EFAULT;
+            }
+            break;
 
                default:
                        dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
@@ -913,3 +1392,8 @@ module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM(debug, "i");
 #endif
 MODULE_PARM_DESC(debug, "Enable verbose debugging messages");
+
+/*
+vi:ts=4
+*/
+
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644 (file)
index 0000000..85f2190
--- /dev/null
@@ -0,0 +1,11 @@
+.PHONY: all
+
+PROGS = cpmfg cpio cptwiddle
+# LDFLAGS += -lusb
+
+all: $(PROGS)
+
+$(PROGS): %: %.o
+
+clean:
+       @rm -rf $(PROGS) $(PROGS:%=%.o)
diff --git a/examples/README.txt b/examples/README.txt
new file mode 100644 (file)
index 0000000..c80db59
--- /dev/null
@@ -0,0 +1 @@
+Simple test applications for using the enhanced IOCTL's of the cp2101 driver.
diff --git a/examples/cpio.c b/examples/cpio.c
new file mode 100644 (file)
index 0000000..f0f6f38
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * cpio.c
+ *
+ * Set cp2103 GPIO_0 and GPIO_1 for LED connection to get RX/TX activity.
+ * Watch out, the tty is hardcoded.
+ */
+
+#include <netinet/in.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define IOCTL_DEVICERESET      0x8004  /* Get port configuration */
+#define IOCTL_PORTCONFGET      0x8005  /* Get port configuration */
+#define IOCTL_PORTCONFSET      0x8006  /* Set port configuration */
+
+typedef struct {
+    uint16_t mode;
+    uint16_t lowPower;
+    uint16_t latch;
+} cp2103_port_state_t;
+
+typedef struct {
+    cp2103_port_state_t reset;
+    cp2103_port_state_t suspend;
+    uint8_t enhancedFxn;
+} cp2103_port_config_t;
+
+int cpConnect()
+{
+    int ret, ioval;
+    int fd = open("/dev/usb/tts/0", O_RDWR);
+    if (fd < 0) {
+       fprintf(stderr, "cannot open tty\n");
+       return -1;
+    }
+    printf("tty opened\n");
+    return fd;
+}
+
+void cpDisconnect(int fd)
+{
+    if (fd >= 0)
+       close(fd);
+}
+
+int main()
+{
+    int fd;
+    cp2103_port_config_t config;
+    int ret;
+
+    /* open */
+    if ((fd = cpConnect()) < 0)
+       return 1;
+
+    /* Read the current port configuration */
+    if ((ret = ioctl(fd, IOCTL_PORTCONFGET, &config))) {
+       fprintf(stderr, "portconfget ioctl failed %d\n", ret);
+       return 1;
+    }
+
+    /* Set the current port configuration; turn on GPIO_0 and GPIO_1 to get
+     * activity LEDs.
+     */
+    config.reset.mode &= ~0x0300;
+    config.suspend.mode &= ~0x0300;
+    config.reset.latch |= 0x0300;
+    config.enhancedFxn &= ~0x03;
+    if ((ret = ioctl(fd, IOCTL_PORTCONFSET, &config))) {
+       fprintf(stderr, "portconfset ioctl failed %d\n", ret);
+       return 1;
+    }
+
+    /* Reset the part so the changes take effect. */
+    if ((ret = ioctl(fd, IOCTL_DEVICERESET, 0))) {
+       fprintf(stderr, "device reset ioctl %d\n", ret);
+       return 1;
+    }
+
+    cpDisconnect(fd);
+    return 0;
+}
diff --git a/examples/cpmfg.c b/examples/cpmfg.c
new file mode 100644 (file)
index 0000000..ed8524a
--- /dev/null
@@ -0,0 +1,159 @@
+/**
+ * cpcfg.c
+ *
+ * Configure the USB descriptor and GPIO configurations for a cp2103-equipped
+ * device.
+ */
+
+#include <netinet/in.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define VID    0x10c4
+#define PID    0xea60
+
+/* CP2103 ioctls */
+#define IOCTL_GPIOGET          0x8000  /* Get gpio bits */
+#define IOCTL_GPIOSET          0x8001  /* Set gpio bits */
+#define IOCTL_GPIOBIC          0x8002  /* Clear specific gpio bit(s) */
+#define IOCTL_GPIOBIS          0x8003  /* Set specific gpio bit(s) */
+
+/* CP210x ioctls principally used during initial device configuration */
+#define IOCTL_DEVICERESET      0x8004  /* Reset the cp210x */
+#define IOCTL_PORTCONFGET      0x8005  /* Get port configuration */
+#define IOCTL_PORTCONFSET      0x8006  /* Set port configuration */
+#define IOCTL_SETVID           0x8007  /* Set vendor id */
+#define IOCTL_SETPID           0x8008  /* Set product id */
+#define IOCTL_SETMFG           0x8009  /* Set manufacturer string */
+#define IOCTL_SETPRODUCT       0x800a  /* Set product string */
+#define IOCTL_SETSERIAL                0x800b  /* Set serial number string */
+#define IOCTL_SETDEVVER                0x800c  /* set device version id */
+/* FIXME: where is IOCTL_SETMFG? */
+
+/* CP2103 GPIO */
+#define GPIO_0         0x01
+#define GPIO_1         0x02
+#define GPIO_2         0x04
+#define GPIO_3         0x08
+#define GPIO_MASK      (GPIO_0|GPIO_1|GPIO_2|GPIO_3)
+
+/* Port config definitions */
+typedef struct {
+    uint16_t mode;             /* Push-pull = 1, Open-drain = 0 */
+    uint16_t lowPower;
+    uint16_t latch;            /* Logic high = 1, Logic low = 0 */
+} cp2101_port_state_t;
+
+typedef struct {
+    cp2101_port_state_t reset;
+    cp2101_port_state_t suspend;
+    uint8_t enhancedFxn;
+} cp2101_port_config_t;
+
+#define PORT_CONFIG_LEN        13      /* Because sizeof() will pad to full words */
+
+/* Used to pass variable sized buffers between user and kernel space (ioctls) */
+typedef struct {
+       char* buf;
+       size_t len;
+} cp210x_buffer_t;
+
+void exit(int);
+
+int cpConnect(char* device)
+{
+    int ret, ioval;
+    int fd = open(device, O_RDWR);
+    if (fd < 0) {
+       fprintf(stderr, "cannot open %s\n", device);
+       exit(1);
+    }
+    printf("%s opened\n", device);
+    return fd;
+}
+
+void cpDisconnect(int fd)
+{
+    if (fd >= 0)
+       close(fd);
+}
+
+void cpSetStr(int fd, unsigned int ioctlno, char* string, size_t len)
+{
+    int ret;
+    cp210x_buffer_t buf = { buf: string, len: len };
+
+    ret = ioctl(fd, ioctlno, &buf);
+    if (ret) {
+       fprintf(stderr, "failed to set via ioctl 0x%04x, err %d\n", ioctlno,
+               ret);
+       exit(1);
+    }
+}
+
+void cpSetLeds(int fd)
+{
+    int ret;
+    cp2101_port_config_t config;
+
+    /* Read the current port configuration */
+    if ((ret = ioctl(fd, IOCTL_PORTCONFGET, &config))) {
+       fprintf(stderr, "portconfget ioctl failed %d\n", ret);
+       exit(1);
+    }
+
+    /* Set the current port configuration; turn on GPIO_0 and GPIO_1 to get
+     * activity LEDs.  GPIO_2 and GPIO_3 are set for 'regular' gpio.
+     */
+    config.reset.mode &= ~0x0300;
+    config.suspend.mode &= ~0x0300;
+    config.reset.latch |= 0x0300;
+    config.enhancedFxn |= 0x03;
+    config.enhancedFxn &= ~0x10; /* turn off weak pullups */
+    if ((ret = ioctl(fd, IOCTL_PORTCONFSET, &config))) {
+       fprintf(stderr, "portconfset ioctl failed %d\n", ret);
+       exit(1);
+    }
+}
+
+void cpReset(int fd)
+{
+    int ret;
+
+    /* Reset the part */
+    if ((ret = ioctl(fd, IOCTL_DEVICERESET, 0))) {
+       fprintf(stderr, "device reset ioctl %d\n", ret);
+       exit(1);
+    }
+}
+
+int main(int argc, char* argv[])
+{
+    /* char newmfg[255] = "Company Name"; */
+    char newprod[255] = "CompanyName ";
+    char newsn[255];
+    int fd;
+
+    if (argc != 4) {
+      fprintf(stderr, "usage: %s <tty> <part#> <sn#>\n", argv[0]);
+      exit(1);
+    }
+
+    strcat(newprod, argv[2]);
+    strcpy(newsn, argv[3]);
+    fd = cpConnect(argv[1]);
+    /* SiLabs doesn't allow set of mfg string on cp210x.
+     * cpSetStr(fd, IOCTL_SETMFG, newmfg, strlen(newmfg));
+     */
+    cpSetStr(fd, IOCTL_SETPRODUCT, newprod, strlen(newprod));
+    cpSetStr(fd, IOCTL_SETSERIAL, newsn, strlen(newsn));
+    cpSetLeds(fd);
+    cpReset(fd);
+    cpDisconnect(fd);
+    printf("done\n");
+    return 0;
+}
diff --git a/examples/cptwiddle.c b/examples/cptwiddle.c
new file mode 100644 (file)
index 0000000..068809e
--- /dev/null
@@ -0,0 +1,167 @@
+/**
+ * cptwiddle.c
+ *
+ * Twiddle the leds connected to the cp2103's GPIO_0 and GPIO_1 pins on the
+ * USB device.  Twiddle does the following:
+ *
+ *  - Saves the current latch and port config settings
+ *  - Reconfigures for leds as IO
+ *  - Resets the device and reconnects to it
+ *  - Twiddles the LEDs
+ *  - Restores the original configuration
+ *  - Resets the device again so that the original config is immediately active
+ *
+ * Watch out; the tty device is hard-coded.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* CP2103 ioctls */
+#define IOCTL_GPIOGET          0x8000  /* Get gpio bits */
+#define IOCTL_GPIOSET          0x8001  /* Set gpio bits */
+#define IOCTL_GPIOBIC          0x8002  /* Clear specific gpio bit(s) */
+#define IOCTL_GPIOBIS          0x8003  /* Set specific gpio bit(s) */
+
+/* CP210x ioctls principally used during initial device configuration */
+#define IOCTL_DEVICERESET      0x8004  /* Reset the cp210x */
+#define IOCTL_PORTCONFGET      0x8005  /* Get port configuration */
+#define IOCTL_PORTCONFSET      0x8006  /* Set port configuration */
+#define IOCTL_SETVID           0x8007  /* Set vendor id */
+#define IOCTL_SETPID           0x8008  /* Set product id */
+#define IOCTL_SETMFG           0x8009  /* Set manufacturer string */
+#define IOCTL_SETPRODUCT       0x800a  /* Set product string */
+#define IOCTL_SETSERIAL                0x800b  /* Set serial number string */
+#define IOCTL_SETDEVVER                0x800c  /* set device version id */
+/* FIXME: where is IOCTL_SETMFG? */
+
+
+typedef struct {
+    uint16_t mode;
+    uint16_t lowPower;
+    uint16_t latch;
+} cp2103_port_state_t;
+
+typedef struct {
+    cp2103_port_state_t reset;
+    cp2103_port_state_t suspend;
+    uint8_t enhancedFxn;
+} cp2103_port_config_t;
+
+int cpConnect()
+{
+    int ret, ioval;
+    int fd = open("/dev/usb/tts/0", O_RDWR);
+    if (fd < 0) {
+       fprintf(stderr, "cannot open tty\n");
+       return -1;
+    }
+    printf("tty opened\n");
+    return fd;
+}
+
+void cpDisconnect(int fd)
+{
+    if (fd >= 0)
+       close(fd);
+}
+
+int main()
+{
+    int fd;
+    cp2103_port_config_t config;
+    cp2103_port_config_t saveConfig;
+    uint8_t saveGpio;
+    int ret;
+    int i;
+
+    if ((fd = cpConnect()) < 0)
+       return 1;
+
+    /* Read the current port configuration */
+    if ((ret = ioctl(fd, IOCTL_PORTCONFGET, &config))) {
+       fprintf(stderr, "portconfget ioctl failed %d\n", ret);
+       return 1;
+    }
+    memcpy(&saveConfig, &config, sizeof(config));
+    printf("port config received\n");
+
+    /* Read the current gpio latches */
+    if ((ret = ioctl(fd, IOCTL_GPIOGET, &saveGpio))) {
+       fprintf(stderr, "gpioget ioctl failed %d\n", ret);
+       return 1;
+    }
+    printf("saved gpio latches 0x%02x\n", saveGpio);
+
+    /* Set the current port configuration; set GPIO_0 and GPIO_1 as outputs */
+    config.reset.mode &= ~0x0300;
+    config.suspend.mode &= ~0x0300;
+    config.reset.latch |= 0x0300;
+    config.enhancedFxn &= ~0x03;
+    if ((ret = ioctl(fd, IOCTL_PORTCONFSET, &config))) {
+       fprintf(stderr, "portconfset ioctl failed %d\n", ret);
+       return 1;
+    }
+    printf("port config altered\n");
+
+    /* Reset the part */
+    if ((ret = ioctl(fd, IOCTL_DEVICERESET, 0))) {
+       fprintf(stderr, "device reset ioctl failed %d\n", ret);
+       return 1;
+    }
+    printf("device reset\n");
+
+    /* Disconnect then reconnect */
+    cpDisconnect(fd);
+    printf("disconnected\n");
+    for (i = 0; i < 10; i++) {
+       sleep(1);
+       if ((fd = cpConnect()) >= 0)
+           break;
+    }
+    if (i == 100) {
+       printf("failed to reconnect\n");
+       return 1;
+    }
+    printf("reconnected\n");
+
+    /* Now, twiddle some bits */
+    for (i = 0; i < 8; i++) {
+       uint8_t leds = i & 0x03;
+       if ((ret = ioctl(fd, IOCTL_GPIOSET, leds))) {
+           fprintf(stderr, "gpio set ioctl failed %d\n", ret);
+           return 1;
+       }
+       printf("set leds %u\n", leds);
+       sleep(1);
+    }
+
+    /* Restore the original latches */
+    if ((ret = ioctl(fd, IOCTL_GPIOSET, saveGpio))) {
+       fprintf(stderr, "gpio set ioctl failed %d\n", ret);
+       return 1;
+    }
+    printf("gpio latches restored\n");
+
+    /* Return the original configuration */
+    if ((ret = ioctl(fd, IOCTL_PORTCONFSET, &saveConfig))) {
+       fprintf(stderr, "portconfset ioctl failed %d\n", ret);
+       return 1;
+    }
+    printf("port config restored\n");
+
+    /* Reset the part again */
+    if ((ret = ioctl(fd, IOCTL_DEVICERESET, 0))) {
+       fprintf(stderr, "device reset ioctl failed %d\n", ret);
+       return 1;
+    }
+    printf("device reset again\n");
+
+    cpDisconnect(fd);
+    return 0;
+}