Commit 9f164683 authored by Oleg Dzhimiev's avatar Oleg Dzhimiev
Browse files

initial commit

parents
Loading
Loading
Loading
Loading

Makefile

0 → 100644
+7 −0
Original line number Diff line number Diff line
obj-m = cp210x.o
KVERSION = $(shell uname -r)
all:
	make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
	make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

README.md

0 → 100644
+42 −0
Original line number Diff line number Diff line
# CP210x GPIO control
A patch to the CP210x USB to UART Bridges driver that adds GPIO pins control by implementing ioctl function and a python script to program devices.

## Supported OS
* Kubuntu Linux 13.04 (kernel 3.8.0-32)

## Tested devices
* CP2103
* CP2104

## Install
1. Build:

        ./build_driver

2. Reload driver (from the built directory):

        sudo rmmod 210x
        sudo insmod 210x.ko

## Program GPIO_MAS
1. Examples:
    * Set all 1's:

            ./cp210x_gpio.py -d /dev/ttyUSB0 -m 0xff 0xff

    * Set GPIO[3]=1, others - 0:
    
            ./cp210x_gpio.py -d /dev/ttyUSB1 -m 0xff 0x08
    
## Changes in the driver
* Changes in the driver cp210x.c are based on the patch found [here](https://lkml.org/lkml/2012/5/1/2)
* GPIO_VALUE=arg[23:16], GPIO_MASK=arg[7:0]

## Unresolved questions
* Silabs drivers in 2.6.x and 3.x.x for GPIOs differ: 
    * 2.6.x: GPIO_VALUE=arg[23:16], GPIO_MASK=arg[7:0] - this matches the patch
    * 3.x.x: GPIO_VALUE=arg[15: 8], GPIO_MASK=arg[7:0]

## Links
* [Silabs CP210x USB to UART Bridge VCP Drivers](http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx) - supported kernels 2.6.x, 3.x.x (but 3.x.x are not up-to-date - cannot be built in 3.8.0)
* [cp210x: Add ioctl for GPIO support](https://lkml.org/lkml/2012/5/1/2) - patch
 No newline at end of file

build_driver.sh

0 → 100755
+32 −0
Original line number Diff line number Diff line
#!/bin/bash

CP210X_NAME="cp210x.c"
CP210X_ADDRESS="https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/plain/drivers/usb/serial/cp210x.c?id=refs/tags/v"

PATCH_NAME="cp210x.patch"

LINUX_KERNEL_VERSION=$(uname -r | cut -f1 -d"-")
LKV_P1=$(echo $LINUX_KERNEL_VERSION | cut -f1 -d".")
LKV_P2=$(echo $LINUX_KERNEL_VERSION | cut -f2 -d".")
LKV_P3=$(echo $LINUX_KERNEL_VERSION | cut -f3 -d".")

if [ $LKV_P3 = 0 ]; then
  CP210X_VERSION=$(echo "$LKV_P1.$LKV_P2")
else
  CP210X_VERSION=$(echo "$LKV_P1.$LKV_P2.$LKV_P3")
fi

#uncomment if patch doesn't work 
#CP210X_VERSION="3.8.0"

#get the file to patch
if [ ! -f $CP210X_NAME ]; then
  wget -O $CP210X_NAME "${CP210X_ADDRESS}${CP210X_VERSION}"
fi

#patch
patch -r - -Np1 < $PATCH_NAME

#run make
make clean
make

cp210x.c

0 → 100644
+932 −0

File added.

Preview size limit exceeded, changes collapsed.

cp210x.patch

0 → 100644
+102 −0
Original line number Diff line number Diff line
--- a/cp210x.c	2013-10-30 18:43:43.000000000 -0600
+++ b/cp210x.c	2013-10-30 19:23:02.410147064 -0600
@@ -31,6 +31,8 @@
  */
 static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *);
 static void cp210x_close(struct usb_serial_port *);
+static int cp210x_ioctl(struct tty_struct *tty,
+	unsigned int cmd, unsigned long arg);
 static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *);
 static void cp210x_get_termios_port(struct usb_serial_port *port,
 	unsigned int *cflagp, unsigned int *baudp);
@@ -174,6 +176,7 @@
 	.bulk_out_size		= 256,
 	.open			= cp210x_open,
 	.close			= cp210x_close,
+	.ioctl			= cp210x_ioctl,
 	.break_ctl		= cp210x_break_ctl,
 	.set_termios		= cp210x_set_termios,
 	.tiocmget		= cp210x_tiocmget,
@@ -187,6 +190,10 @@
 	&cp210x_device, NULL
 };
 
+/* IOCTLs */
+#define IOCTL_GPIOGET	 0x8000
+#define IOCTL_GPIOSET	 0x8001
+
 /* Config request types */
 #define REQTYPE_HOST_TO_INTERFACE	0x41
 #define REQTYPE_INTERFACE_TO_HOST	0xc1
@@ -220,11 +227,16 @@
 #define CP210X_SET_CHARS	0x19
 #define CP210X_GET_BAUDRATE	0x1D
 #define CP210X_SET_BAUDRATE	0x1E
+#define CP210X_VENDOR_SPECIFIC	0xFF
 
 /* CP210X_IFC_ENABLE */
 #define UART_ENABLE		0x0001
 #define UART_DISABLE		0x0000
 
+/* CP210X_VENDOR_SPECIFIC */
+#define CP210X_WRITE_LATCH	0x37E1
+#define CP210X_READ_LATCH	0x00C2
+
 /* CP210X_(SET|GET)_BAUDDIV */
 #define BAUD_RATE_GEN_FREQ	0x384000
 
@@ -449,6 +461,54 @@
 	mutex_unlock(&port->serial->disc_mutex);
 }
 
+static int cp210x_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = tty->driver_data;
+	struct usb_serial *serial = port->serial;
+	int result = 0;
+	struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
+	unsigned int latch_setting = 0;
+
+	switch (cmd) {
+	    case IOCTL_GPIOGET:
+		result = usb_control_msg(
+			      serial->dev,
+			      usb_rcvctrlpipe(serial->dev, 0),
+			      CP210X_VENDOR_SPECIFIC,
+			      REQTYPE_DEVICE_TO_HOST,
+			      CP210X_READ_LATCH,
+			      spriv->bInterfaceNumber,
+			      &latch_setting, 1,
+			      USB_CTRL_GET_TIMEOUT);
+		if (result != 1)
+		    return -EPROTO;
+		*(unsigned long *)arg = (unsigned long)latch_setting;
+		return 0;
+	    break;
+	    case IOCTL_GPIOSET:
+		latch_setting = *(unsigned int *)arg & 0x000000FF;
+		//why [23:16] bits are taken for value is unknown
+		latch_setting |= (*(unsigned int *)arg & 0x00FF0000) >> 8;
+		
+		result = usb_control_msg(
+			      serial->dev,
+			      usb_sndctrlpipe(serial->dev, 0),
+			      CP210X_VENDOR_SPECIFIC,
+			      REQTYPE_HOST_TO_DEVICE,
+			      CP210X_WRITE_LATCH,
+			      latch_setting,
+			      NULL, 0,
+			      USB_CTRL_SET_TIMEOUT);
+		if (result != 0)
+		    return -EPROTO;
+		return 0;
+	    break;
+	    default:
+	    break;
+	}
+	return -ENOIOCTLCMD;
+}
+
 /*
  * cp210x_get_termios
  * Reads the baud rate, data bits, parity, stop bits and flow control mode
Loading