From 21b2531f5a9474b90f6b984a9f71b027678a0ef7 Mon Sep 17 00:00:00 2001 From: smckown Date: Wed, 7 Nov 2007 15:52:45 +0000 Subject: [PATCH] This is the version of the driver modified by TMI to support setting of the 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 | 22 ++ cp2101.c | 508 ++++++++++++++++++++++++++++++++++++++++++- examples/Makefile | 11 + examples/README.txt | 1 + examples/cpio.c | 85 ++++++++ examples/cpmfg.c | 159 ++++++++++++++ examples/cptwiddle.c | 167 ++++++++++++++ 7 files changed, 941 insertions(+), 12 deletions(-) create mode 100644 README.txt create mode 100644 examples/Makefile create mode 100644 examples/README.txt create mode 100644 examples/cpio.c create mode 100644 examples/cpmfg.c create mode 100644 examples/cptwiddle.c diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..932cfcb --- /dev/null +++ b/README.txt @@ -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 diff --git a/cp2101.c b/cp2101.c index 6664cb3..8f371d2 100644 --- a/cp2101.c +++ b/cp2101.c @@ -11,13 +11,15 @@ * 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 #include #include #include @@ -27,6 +29,7 @@ #include #include #include +#include #ifndef KERNEL_VERSION #define KERNEL_VERSION(a,b,c) ((a)*65536 + (b)*256 + (c)) @@ -42,11 +45,15 @@ #ifdef LINUX26 #include +#include #define DEV_ERR(dev,format,arg...) dev_err(dev,format, ## arg) #define USB_KILL_URB usb_kill_urb #else /* !LINUX26 */ +#include +#include "usb-serial.h" + #ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1; #else @@ -58,13 +65,11 @@ #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 index 0000000..85f2190 --- /dev/null +++ b/examples/Makefile @@ -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 index 0000000..c80db59 --- /dev/null +++ b/examples/README.txt @@ -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 index 0000000..f0f6f38 --- /dev/null +++ b/examples/cpio.c @@ -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 +#include +#include +#include +#include +#include + +#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 index 0000000..ed8524a --- /dev/null +++ b/examples/cpmfg.c @@ -0,0 +1,159 @@ +/** + * cpcfg.c + * + * Configure the USB descriptor and GPIO configurations for a cp2103-equipped + * device. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 \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 index 0000000..068809e --- /dev/null +++ b/examples/cptwiddle.c @@ -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 +#include +#include +#include +#include +#include +#include + +/* 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; +} -- 2.39.2