diff mbox

[OpenWrt-Devel,kernel] cp201x: Add GPIO ioctl commands (from Silicon Labs)

Message ID A3C746EC1C774B0DA57E652520837D00@fortmeadow.com
State Rejected
Headers show

Commit Message

Ted Hess Aug. 3, 2015, 5:55 p.m. UTC
Silicon Labs driver has ioctl support on devices which have GPIO pins. The 
driver
in the kernel repo does not have this feature.

Ref: 
http://www.silabs.com/Support%20Documents/Software/Linux_CP210x_VCP_3.x.x_Release_Notes.txt

Signed-off-by: Ted Hess <thess@kitschensync.net>
---
.../patches-3.18/824-cp210x_add_gpio_ioctl.patch   | 194 +++++++++++++++++++++
.../patches-4.1/824-cp210x_add_gpio_ioctl.patch    | 194 +++++++++++++++++++++
2 files changed, 388 insertions(+)
create mode 100644 
target/linux/generic/patches-3.18/824-cp210x_add_gpio_ioctl.patch
create mode 100644 
target/linux/generic/patches-4.1/824-cp210x_add_gpio_ioctl.patch

+

Comments

Felix Fietkau Aug. 3, 2015, 6:08 p.m. UTC | #1
On 2015-08-03 19:55, Ted Hess wrote:
> Silicon Labs driver has ioctl support on devices which have GPIO pins. The 
> driver
> in the kernel repo does not have this feature.
> 
> Ref: 
> http://www.silabs.com/Support%20Documents/Software/Linux_CP210x_VCP_3.x.x_Release_Notes.txt
> 
> Signed-off-by: Ted Hess <thess@kitschensync.net>
Wouldn't it make more sense to expose this as a GPIO controller instead
of a driver specific ioctl interface?

- Felix
Ted Hess Aug. 3, 2015, 6:17 p.m. UTC | #2
It would probably make sense to do it that way however, there are apps which already use the ioctl interface on this device and this 
code came directly from the the manufacturer's linux driver.

I'm not sure how to add general GPIO support for a specific USB device? Things to research...

(I think I may have to re-submit this without the gratuitous word-wrapping too)

/ted

-----Original Message----- 
From: Felix Fietkau
Sent: Monday, August 03, 2015 2:08 PM
To: Ted Hess ; OpenWrt developers
Subject: Re: [OpenWrt-Devel] [PATCH] [kernel] cp201x: Add GPIO ioctl commands (from Silicon Labs)

On 2015-08-03 19:55, Ted Hess wrote:
> Silicon Labs driver has ioctl support on devices which have GPIO pins. The
> driver
> in the kernel repo does not have this feature.
>
> Ref:
> http://www.silabs.com/Support%20Documents/Software/Linux_CP210x_VCP_3.x.x_Release_Notes.txt
>
> Signed-off-by: Ted Hess <thess@kitschensync.net>
Wouldn't it make more sense to expose this as a GPIO controller instead
of a driver specific ioctl interface?

- Felix
Felix Fietkau Aug. 3, 2015, 6:33 p.m. UTC | #3
On 2015-08-03 20:17, Ted Hess wrote:
> It would probably make sense to do it that way however, there are apps which already use the ioctl interface on this device and this 
> code came directly from the the manufacturer's linux driver.
> 
> I'm not sure how to add general GPIO support for a specific USB device? Things to research...
Did the manufacturer submit this upstream already, or is somebody else
(maybe you) going to do so?

- Felix
Ted Hess Aug. 3, 2015, 7:27 p.m. UTC | #4
After a bit more research - This is what I know:

The upstream cp210x driver is not completely up-to-date with the driver silabs maintains on
their site. In their own words: "... unfortunately GPIO is not something that has been
committed to the Linux kernel yet for community maintenance. We leave the drivers on our
website, even though they are a bit behind the curve of the maintained kernels, to
demonstrate how to do this quickly using an ioctl() call."

I also noted that the patch I submitted is not up-to-date with their latest devices which are
supported in their driver - whoops.

I could try to submit this upstream even if it was snatched from their driver. Is this OK?

There is a small relay controller package (crelay) that I am going to submit to the packages
repo it this patch gets accepted. It uses the cp210x ioctl interface.

I will also submit this patch again here with the correct format and all the latest fixes from
the old Silicon Labs driver.

/ted

-----Original Message----- 
From: Felix Fietkau
Sent: Monday, August 03, 2015 2:33 PM
To: Ted Hess ; OpenWrt developers
Subject: Re: [OpenWrt-Devel] [PATCH] [kernel] cp201x: Add GPIO ioctl commands (from Silicon Labs)

On 2015-08-03 20:17, Ted Hess wrote:
> It would probably make sense to do it that way however, there are apps which already use the ioctl interface on this device and 
> this
> code came directly from the the manufacturer's linux driver.
>
> I'm not sure how to add general GPIO support for a specific USB device? Things to research...
Did the manufacturer submit this upstream already, or is somebody else
(maybe you) going to do so?

- Felix
Karl Palsson Aug. 8, 2015, 1:14 a.m. UTC | #5
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

"Ted Hess" <thess@kitschensync.net> wrote:
> After a bit more research - This is what I know:
> 
> The upstream cp210x driver is not completely up-to-date with the driver
> silabs maintains on
> their site. In their own words: "... unfortunately GPIO is not something
> that has been
> committed to the Linux kernel yet for community maintenance. We leave
> the drivers on our
> website, even though they are a bit behind the curve of the maintained
> kernels, to
> demonstrate how to do this quickly using an ioctl() call."
> 
> I also noted that the patch I submitted is not up-to-date with their
> latest devices which are
> supported in their driver - whoops.
> 
> I could try to submit this upstream even if it was snatched from their
> driver. Is this OK?

There 's two people working on this on the linux-usb mailing list "right
now"  (I'm on apalling internet right now, so I can't find links for you
to the archives)  The ioctl approach has been totally vetoed, any ioctl
for gpios on this chips will never merge upstream, the silabs driver is
for people who don't care about those sorts of things.  gpios for ftdi
is also landing, and it's finally being accepted that all of these
things are actually, you know, important, so it's finally getting there,
but the ioctl approach is out.  I wouldn't personally be a fan of
openwrt putting in ioctls even for "just for now" as it's something you
can maintain in your own tree if you really need this sort of
functionality.

> 
> There is a small relay controller package (crelay) that I am going to
> submit to the packages
> repo it this patch gets accepted. It uses the cp210x ioctl interface.

What's happening is it's getting the whole gpiolib interface.  so this
app will only ever work with people using the vendor driver.... _if_ you
need to do it in the kernel.  You can always use libusb to make the same
control requests if you like, and if you're only poking a relay, I'd
imagine it's more than sufficient.  (of course depending on what you
want to be doing at the same time and all that)

Cheers,
Karl P

> 
> -----Original Message----- 
> From: Felix Fietkau
> Sent: Monday, August 03, 2015 2:33 PM
> To: Ted Hess ; OpenWrt developers
> Subject: Re: [OpenWrt-Devel] [PATCH] [kernel] cp201x: Add GPIO ioctl
> commands (from Silicon Labs)
> 
> On 2015-08-03 20:17, Ted Hess wrote:
> > It would probably make sense to do it that way however, there are apps which already use the ioctl interface on this device and 
> > this
> > code came directly from the the manufacturer's linux driver.
> >
> > I'm not sure how to add general GPIO support for a specific USB device? Things to research...
> Did the manufacturer submit this upstream already, or is somebody else
> (maybe you) going to do so?
> 
> - Felix 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel

- -- 
Sent using Mailpile, Free Software from www.mailpile.is

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)

iQIcBAEBAgAGBQJVxVfqAAoJEBmotQ/U1cr2SWUP/jpwCwpCoTwidPoR3PbHOy8W
xjDuV1yseAV9HDrFjiCgLC0sOXfoaRWHJrGV7lvADLvYMNJb4nfGzShCzMPqJb3K
xvD1snvZu/mFCFVzuxv8OYImMzdfPw6zVpRjjgwrpnytCejfc6wqADopL418XFSZ
PFgN5n9sxDgrM1BkPEvMJoBB58IrN1w2M/zDq+IWN4uJgEfdSosjiP++sRngB8dA
YAcmqhg1RBbv6tilr8jcXaKlgIc+iriEN2sGpe8cVjkhefkyO5MtNVbo9yFxco7n
nNdcjh1hhf+DoUmrO7YImjmIFVO1UU37yKbg3tMFg4XWh55O/BEkk0Q6+ljfzeZz
nj+mW3rARjYXDNX8AKYFTS3TdlA/4rphtQ/ReleeK77SzZIwlKEr+RLES8q/2Lde
sf8TEP/EpXXgwfhJFeVnpP10p/2DS8khOWb80enPBfM6+N6c+/+LDijj/1UHpi2k
Zoin3Kcwa/vuMRWHill0me3bvBbiK2ZJY+sQCt+VMKisH42CJpDYi7CY5FG9W4aD
oR2Nf4jAPiA/VB4+KNzeoO5CoBGBLDqZPeiYfsZfB5g/OuvNHzbG/on7fyNZTR+Z
q/e5iMXmRYqCT0VmK/AqVdZgyBSkHg/knLvwbC8U1onudYDMz88BrBlRpTztVagr
i5qDhzH6yhRiPZSIo9VM
=2eLL
-----END PGP SIGNATURE-----
Ted Hess Aug. 8, 2015, 1:53 a.m. UTC | #6
Thanks for the info and explanation. No need for the pointers.

I'd already decided on dropping this request 'cause it did seem a bit hacky and was looking at a better solution. Your suggestion to 
take a look at libusb seems like a good thing to do for this app.

/ted

-----Original Message----- 
From: Karl Palsson
Sent: Friday, August 07, 2015 9:14 PM
To: Ted Hess
Cc: Felix Fietkau ; OpenWrt developers
Subject: Re: Re: [OpenWrt-Devel] [PATCH] [kernel] cp201x: Add GPIO ioctl commands (from Silicon Labs)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

"Ted Hess" <thess@kitschensync.net> wrote:
> After a bit more research - This is what I know:
>
> The upstream cp210x driver is not completely up-to-date with the driver
> silabs maintains on
> their site. In their own words: "... unfortunately GPIO is not something
> that has been
> committed to the Linux kernel yet for community maintenance. We leave
> the drivers on our
> website, even though they are a bit behind the curve of the maintained
> kernels, to
> demonstrate how to do this quickly using an ioctl() call."
>
> I also noted that the patch I submitted is not up-to-date with their
> latest devices which are
> supported in their driver - whoops.
>
> I could try to submit this upstream even if it was snatched from their
> driver. Is this OK?

There 's two people working on this on the linux-usb mailing list "right
now"  (I'm on apalling internet right now, so I can't find links for you
to the archives)  The ioctl approach has been totally vetoed, any ioctl
for gpios on this chips will never merge upstream, the silabs driver is
for people who don't care about those sorts of things.  gpios for ftdi
is also landing, and it's finally being accepted that all of these
things are actually, you know, important, so it's finally getting there,
but the ioctl approach is out.  I wouldn't personally be a fan of
openwrt putting in ioctls even for "just for now" as it's something you
can maintain in your own tree if you really need this sort of
functionality.

>
> There is a small relay controller package (crelay) that I am going to
> submit to the packages
> repo it this patch gets accepted. It uses the cp210x ioctl interface.

What's happening is it's getting the whole gpiolib interface.  so this
app will only ever work with people using the vendor driver.... _if_ you
need to do it in the kernel.  You can always use libusb to make the same
control requests if you like, and if you're only poking a relay, I'd
imagine it's more than sufficient.  (of course depending on what you
want to be doing at the same time and all that)

Cheers,
Karl P

>
> -----Original Message----- 
> From: Felix Fietkau
> Sent: Monday, August 03, 2015 2:33 PM
> To: Ted Hess ; OpenWrt developers
> Subject: Re: [OpenWrt-Devel] [PATCH] [kernel] cp201x: Add GPIO ioctl
> commands (from Silicon Labs)
>
> On 2015-08-03 20:17, Ted Hess wrote:
> > It would probably make sense to do it that way however, there are apps which already use the ioctl interface on this device and
> > this
> > code came directly from the the manufacturer's linux driver.
> >
> > I'm not sure how to add general GPIO support for a specific USB device? Things to research...
> Did the manufacturer submit this upstream already, or is somebody else
> (maybe you) going to do so?
>
> - Felix
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel

- -- 
Sent using Mailpile, Free Software from www.mailpile.is

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)

iQIcBAEBAgAGBQJVxVfqAAoJEBmotQ/U1cr2SWUP/jpwCwpCoTwidPoR3PbHOy8W
xjDuV1yseAV9HDrFjiCgLC0sOXfoaRWHJrGV7lvADLvYMNJb4nfGzShCzMPqJb3K
xvD1snvZu/mFCFVzuxv8OYImMzdfPw6zVpRjjgwrpnytCejfc6wqADopL418XFSZ
PFgN5n9sxDgrM1BkPEvMJoBB58IrN1w2M/zDq+IWN4uJgEfdSosjiP++sRngB8dA
YAcmqhg1RBbv6tilr8jcXaKlgIc+iriEN2sGpe8cVjkhefkyO5MtNVbo9yFxco7n
nNdcjh1hhf+DoUmrO7YImjmIFVO1UU37yKbg3tMFg4XWh55O/BEkk0Q6+ljfzeZz
nj+mW3rARjYXDNX8AKYFTS3TdlA/4rphtQ/ReleeK77SzZIwlKEr+RLES8q/2Lde
sf8TEP/EpXXgwfhJFeVnpP10p/2DS8khOWb80enPBfM6+N6c+/+LDijj/1UHpi2k
Zoin3Kcwa/vuMRWHill0me3bvBbiK2ZJY+sQCt+VMKisH42CJpDYi7CY5FG9W4aD
oR2Nf4jAPiA/VB4+KNzeoO5CoBGBLDqZPeiYfsZfB5g/OuvNHzbG/on7fyNZTR+Z
q/e5iMXmRYqCT0VmK/AqVdZgyBSkHg/knLvwbC8U1onudYDMz88BrBlRpTztVagr
i5qDhzH6yhRiPZSIo9VM
=2eLL
-----END PGP SIGNATURE-----
diff mbox

Patch

diff --git a/target/linux/generic/patches-3.18/824-cp210x_add_gpio_ioctl.patch 
b/target/linux/generic/patches-3.18/824-cp210x_add_gpio_ioctl.patch
new file mode 100644
index 0000000..695647a
--- /dev/null
+++ b/target/linux/generic/patches-3.18/824-cp210x_add_gpio_ioctl.patch
@@ -0,0 +1,194 @@ 
+--- a/drivers/usb/serial/cp210x.c
++++ b/drivers/usb/serial/cp210x.c
+@@ -24,13 +24,15 @@
+ #include <linux/uaccess.h>
+ #include <linux/usb/serial.h>
+
+-#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
++#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver (with 
GPIO support)"
+
+ /*
+  * Function Prototypes
+  */
+ 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);
+@@ -185,6 +187,7 @@
+
+ struct cp210x_serial_private {
+     __u8            bInterfaceNumber;
++    __u8            bPartNumber;
+ };
+
+ static struct usb_serial_driver cp210x_device = {
+@@ -198,6 +201,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,
+@@ -211,6 +215,17 @@
+     &cp210x_device, NULL
+ };
+
++/* Part number definitions */
++#define CP2101_PARTNUM        0x01
++#define CP2102_PARTNUM        0x02
++#define CP2103_PARTNUM        0x03
++#define CP2104_PARTNUM        0x04
++#define CP2105_PARTNUM        0x05
++
++/* IOCTLs */
++#define IOCTL_GPIOGET        0x8000
++#define IOCTL_GPIOSET        0x8001
++
+ /* Config request types */
+ #define REQTYPE_HOST_TO_INTERFACE    0x41
+ #define REQTYPE_INTERFACE_TO_HOST    0xc1
+@@ -244,11 +259,17 @@
+ #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
++#define CP210X_GET_PARTNUM    0x370B
++
+ /* CP210X_(SET|GET)_BAUDDIV */
+ #define BAUD_RATE_GEN_FREQ    0x384000
+
+@@ -469,6 +490,96 @@
+     cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
+ }
+
++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;
++    struct cp210x_serial_private *port_priv = usb_get_serial_data(serial);
++    int result = 0;
++    unsigned int latch_setting = 0;
++
++    switch (cmd) {
++
++    case IOCTL_GPIOGET:
++        if ((port_priv->bPartNumber == CP2103_PARTNUM) ||
++            (port_priv->bPartNumber == CP2104_PARTNUM)) {
++            result = usb_control_msg(port->serial->dev,
++                    usb_rcvctrlpipe(port->serial->dev, 0),
++                    CP210X_VENDOR_SPECIFIC,
++                    REQTYPE_DEVICE_TO_HOST,
++                    CP210X_READ_LATCH,
++                    port_priv->bInterfaceNumber,
++                    &latch_setting, 1,
++                    USB_CTRL_GET_TIMEOUT);
++            if (result != 1)
++                return -EPROTO;
++            *(unsigned long *)arg = (unsigned long)latch_setting;
++            return 0;
++        } else if (port_priv->bPartNumber == CP2105_PARTNUM) {
++            result = usb_control_msg(port->serial->dev,
++                    usb_rcvctrlpipe(port->serial->dev, 0),
++                    CP210X_VENDOR_SPECIFIC,
++                    REQTYPE_INTERFACE_TO_HOST,
++                    CP210X_READ_LATCH,
++                    port_priv->bInterfaceNumber,
++                    &latch_setting, 1,
++                    USB_CTRL_GET_TIMEOUT);
++            if (result != 1)
++                return -EPROTO;
++            *(unsigned long *)arg = (unsigned long)latch_setting;
++            return 0;
++        } else {
++            return -ENOTSUPP;
++        }
++        break;
++
++    case IOCTL_GPIOSET:
++        if ((port_priv->bPartNumber == CP2103_PARTNUM) ||
++            (port_priv->bPartNumber == CP2104_PARTNUM)) {
++            latch_setting =
++                *(unsigned int *)arg & 0x000000FF;
++            latch_setting |=
++                (*(unsigned int *)arg & 0x00FF0000) >> 8;
++            result = usb_control_msg(port->serial->dev,
++                    usb_sndctrlpipe(port->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;
++        } else if (port_priv->bPartNumber == CP2105_PARTNUM) {
++            latch_setting =
++                *(unsigned int *)arg & 0x000000FF;
++            latch_setting |=
++                (*(unsigned int *)arg & 0x00FF0000) >> 8;
++            result = usb_control_msg(port->serial->dev,
++                    usb_sndctrlpipe(port->serial->dev, 0),
++                    CP210X_VENDOR_SPECIFIC,
++                    REQTYPE_HOST_TO_INTERFACE,
++                    CP210X_WRITE_LATCH,
++                    port_priv->bInterfaceNumber,
++                    &latch_setting, 2,
++                    USB_CTRL_SET_TIMEOUT);
++            if (result != 2)
++                return -EPROTO;
++            return 0;
++        } else {
++            return -ENOTSUPP;
++        }
++        break;
++
++    default:
++        break;
++    }
++
++    return -ENOIOCTLCMD;
++}
++
+ /*
+  * cp210x_get_termios
+  * Reads the baud rate, data bits, parity, stop bits and flow control mode
+@@ -862,6 +973,7 @@
+ {
+     struct usb_host_interface *cur_altsetting;
+     struct cp210x_serial_private *spriv;
++    unsigned int partNum;
+
+     /* cp210x buffers behave strangely unless device is reset */
+     usb_reset_device(serial->dev);
+@@ -875,6 +987,17 @@
+
+     usb_set_serial_data(serial, spriv);
+
++    /* Get the 1-byte part number of the cp210x device */
++    usb_control_msg(serial->dev,
++        usb_rcvctrlpipe(serial->dev, 0),
++        CP210X_VENDOR_SPECIFIC,
++        REQTYPE_DEVICE_TO_HOST,
++        CP210X_GET_PARTNUM,
++        spriv->bInterfaceNumber,
++        &partNum, 1,
++        USB_CTRL_GET_TIMEOUT);
++    spriv->bPartNumber = partNum & 0xFF;
++
+     return 0;
+ }
+
diff --git a/target/linux/generic/patches-4.1/824-cp210x_add_gpio_ioctl.patch 
b/target/linux/generic/patches-4.1/824-cp210x_add_gpio_ioctl.patch
new file mode 100644
index 0000000..f737140
--- /dev/null
+++ b/target/linux/generic/patches-4.1/824-cp210x_add_gpio_ioctl.patch
@@ -0,0 +1,194 @@ 
+--- a/drivers/usb/serial/cp210x.c
++++ b/drivers/usb/serial/cp210x.c
+@@ -24,13 +24,15 @@
+ #include <linux/uaccess.h>
+ #include <linux/usb/serial.h>
+
+-#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
++#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver (with 
GPIO support)"
+
+ /*
+  * Function Prototypes
+  */
+ 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);
+@@ -198,6 +200,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
+
+ struct cp210x_serial_private {
+     __u8            bInterfaceNumber;
++    __u8            bPartNumber;
+ };
+
+ static struct usb_serial_driver cp210x_device = {
+@@ -211,6 +214,7 @@ static struct usb_serial_driver cp210x_d
+     .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,
+@@ -224,6 +228,17 @@ static struct usb_serial_driver * const
+     &cp210x_device, NULL
+ };
+
++/* Part number definitions */
++#define CP2101_PARTNUM        0x01
++#define CP2102_PARTNUM        0x02
++#define CP2103_PARTNUM        0x03
++#define CP2104_PARTNUM        0x04
++#define CP2105_PARTNUM        0x05
++
++/* IOCTLs */
++#define IOCTL_GPIOGET        0x8000
++#define IOCTL_GPIOSET        0x8001
++
+ /* Config request types */
+ #define REQTYPE_HOST_TO_INTERFACE    0x41
+ #define REQTYPE_INTERFACE_TO_HOST    0xc1
+@@ -257,11 +272,17 @@ static struct usb_serial_driver * const
+ #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
++#define CP210X_GET_PARTNUM    0x370B
++
+ /* CP210X_(SET|GET)_BAUDDIV */
+ #define BAUD_RATE_GEN_FREQ    0x384000
+
+@@ -478,6 +499,96 @@ static void cp210x_close(struct usb_seri
+     cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
+ }
+
++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;
++    struct cp210x_serial_private *port_priv = usb_get_serial_data(serial);
++    int result = 0;
++    unsigned int latch_setting = 0;
++
++    switch (cmd) {
++
++    case IOCTL_GPIOGET:
++        if ((port_priv->bPartNumber == CP2103_PARTNUM) ||
++            (port_priv->bPartNumber == CP2104_PARTNUM)) {
++            result = usb_control_msg(port->serial->dev,
++                    usb_rcvctrlpipe(port->serial->dev, 0),
++                    CP210X_VENDOR_SPECIFIC,
++                    REQTYPE_DEVICE_TO_HOST,
++                    CP210X_READ_LATCH,
++                    port_priv->bInterfaceNumber,
++                    &latch_setting, 1,
++                    USB_CTRL_GET_TIMEOUT);
++            if (result != 1)
++                return -EPROTO;
++            *(unsigned long *)arg = (unsigned long)latch_setting;
++            return 0;
++        } else if (port_priv->bPartNumber == CP2105_PARTNUM) {
++            result = usb_control_msg(port->serial->dev,
++                    usb_rcvctrlpipe(port->serial->dev, 0),
++                    CP210X_VENDOR_SPECIFIC,
++                    REQTYPE_INTERFACE_TO_HOST,
++                    CP210X_READ_LATCH,
++                    port_priv->bInterfaceNumber,
++                    &latch_setting, 1,
++                    USB_CTRL_GET_TIMEOUT);
++            if (result != 1)
++                return -EPROTO;
++            *(unsigned long *)arg = (unsigned long)latch_setting;
++            return 0;
++        } else {
++            return -ENOTSUPP;
++        }
++        break;
++
++    case IOCTL_GPIOSET:
++        if ((port_priv->bPartNumber == CP2103_PARTNUM) ||
++            (port_priv->bPartNumber == CP2104_PARTNUM)) {
++            latch_setting =
++                *(unsigned int *)arg & 0x000000FF;
++            latch_setting |=
++                (*(unsigned int *)arg & 0x00FF0000) >> 8;
++            result = usb_control_msg(port->serial->dev,
++                    usb_sndctrlpipe(port->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;
++        } else if (port_priv->bPartNumber == CP2105_PARTNUM) {
++            latch_setting =
++                *(unsigned int *)arg & 0x000000FF;
++            latch_setting |=
++                (*(unsigned int *)arg & 0x00FF0000) >> 8;
++            result = usb_control_msg(port->serial->dev,
++                    usb_sndctrlpipe(port->serial->dev, 0),
++                    CP210X_VENDOR_SPECIFIC,
++                    REQTYPE_HOST_TO_INTERFACE,
++                    CP210X_WRITE_LATCH,
++                    port_priv->bInterfaceNumber,
++                    &latch_setting, 2,
++                    USB_CTRL_SET_TIMEOUT);
++            if (result != 2)
++                return -EPROTO;
++            return 0;
++        } else {
++            return -ENOTSUPP;
++        }
++        break;
++
++    default:
++        break;
++    }
++
++    return -ENOIOCTLCMD;
++}
++
+ /*
+  * cp210x_get_termios
+  * Reads the baud rate, data bits, parity, stop bits and flow control mode
+@@ -866,6 +977,7 @@ static int cp210x_startup(struct usb_ser
+ {
+     struct usb_host_interface *cur_altsetting;
+     struct cp210x_serial_private *spriv;
++    unsigned int partNum;
+
+     spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
+     if (!spriv)
+@@ -876,6 +988,17 @@ static int cp210x_startup(struct usb_ser
+
+     usb_set_serial_data(serial, spriv);
+
++    /* Get the 1-byte part number of the cp210x device */
++    usb_control_msg(serial->dev,
++        usb_rcvctrlpipe(serial->dev, 0),
++        CP210X_VENDOR_SPECIFIC,
++        REQTYPE_DEVICE_TO_HOST,
++        CP210X_GET_PARTNUM,
++        spriv->bInterfaceNumber,
++        &partNum, 1,
++        USB_CTRL_GET_TIMEOUT);
++    spriv->bPartNumber = partNum & 0xFF;
++
+     return 0;
+ }