]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/usb/serial/ftdi_sio.c
USB: serial: ftdi: add CONTEC vendor and product id
[karo-tx-linux.git] / drivers / usb / serial / ftdi_sio.c
index ffc79eea963d81cd440d40be2569d2cc04554c7b..96686471472d07aec9503bbe44d55f2e2a14e6d3 100644 (file)
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/uaccess.h>
 #include <linux/usb.h>
 #include <linux/serial.h>
@@ -92,6 +92,7 @@ struct ftdi_private {
        unsigned long tx_outstanding_bytes;
        unsigned long tx_outstanding_urbs;
        unsigned short max_packet_size;
+       struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() */
 };
 
 /* struct ftdi_sio_quirk is used by devices requiring special attention. */
@@ -614,6 +615,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_OCEANIC_PID) },
        { USB_DEVICE(TTI_VID, TTI_QL355P_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) },
+       { USB_DEVICE(CONTEC_VID, CONTEC_COM1USBH_PID) },
        { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
        { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
        { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) },
@@ -737,6 +739,10 @@ static struct usb_device_id id_table_combined [] = {
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) },
        { USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) },
+       { USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) },
+       { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) },
+       { USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) },
+       { USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) },
        { },                                    /* Optional parameter entry */
        { }                                     /* Terminating entry */
 };
@@ -812,7 +818,7 @@ static struct usb_serial_driver ftdi_sio_device = {
                .name =         "ftdi_sio",
        },
        .description =          "FTDI USB Serial Device",
-       .usb_driver =           &ftdi_driver ,
+       .usb_driver =           &ftdi_driver,
        .id_table =             id_table_combined,
        .num_ports =            1,
        .probe =                ftdi_sio_probe,
@@ -828,8 +834,8 @@ static struct usb_serial_driver ftdi_sio_device = {
        .chars_in_buffer =      ftdi_chars_in_buffer,
        .read_bulk_callback =   ftdi_read_bulk_callback,
        .write_bulk_callback =  ftdi_write_bulk_callback,
-       .tiocmget =             ftdi_tiocmget,
-       .tiocmset =             ftdi_tiocmset,
+       .tiocmget =             ftdi_tiocmget,
+       .tiocmset =             ftdi_tiocmset,
        .ioctl =                ftdi_ioctl,
        .set_termios =          ftdi_set_termios,
        .break_ctl =            ftdi_break_ctl,
@@ -935,7 +941,6 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
                                                        unsigned int clear)
 {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
-       char *buf;
        unsigned urb_value;
        int rv;
 
@@ -944,10 +949,6 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
                return 0;       /* no change */
        }
 
-       buf = kmalloc(1, GFP_NOIO);
-       if (!buf)
-               return -ENOMEM;
-
        clear &= ~set;  /* 'set' takes precedence over 'clear' */
        urb_value = 0;
        if (clear & TIOCM_DTR)
@@ -963,9 +964,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
                               FTDI_SIO_SET_MODEM_CTRL_REQUEST,
                               FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
                               urb_value, priv->interface,
-                              buf, 0, WDR_TIMEOUT);
-
-       kfree(buf);
+                              NULL, 0, WDR_TIMEOUT);
        if (rv < 0) {
                dbg("%s Error from MODEM_CTRL urb: DTR %s, RTS %s",
                                __func__,
@@ -1124,16 +1123,11 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
 static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
 {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
-       char *buf;
        __u16 urb_value;
        __u16 urb_index;
        __u32 urb_index_value;
        int rv;
 
-       buf = kmalloc(1, GFP_NOIO);
-       if (!buf)
-               return -ENOMEM;
-
        urb_index_value = get_ftdi_divisor(tty, port);
        urb_value = (__u16)urb_index_value;
        urb_index = (__u16)(urb_index_value >> 16);
@@ -1146,9 +1140,7 @@ static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
                            FTDI_SIO_SET_BAUDRATE_REQUEST,
                            FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
                            urb_value, urb_index,
-                           buf, 0, WDR_SHORT_TIMEOUT);
-
-       kfree(buf);
+                           NULL, 0, WDR_SHORT_TIMEOUT);
        return rv;
 }
 
@@ -1156,8 +1148,7 @@ static int write_latency_timer(struct usb_serial_port *port)
 {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        struct usb_device *udev = port->serial->dev;
-       char buf[1];
-       int rv = 0;
+       int rv;
        int l = priv->latency;
 
        if (priv->flags & ASYNC_LOW_LATENCY)
@@ -1170,8 +1161,7 @@ static int write_latency_timer(struct usb_serial_port *port)
                             FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
                             FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
                             l, priv->interface,
-                            buf, 0, WDR_TIMEOUT);
-
+                            NULL, 0, WDR_TIMEOUT);
        if (rv < 0)
                dev_err(&port->dev, "Unable to write latency timer: %i\n", rv);
        return rv;
@@ -1181,24 +1171,29 @@ static int read_latency_timer(struct usb_serial_port *port)
 {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        struct usb_device *udev = port->serial->dev;
-       unsigned short latency = 0;
-       int rv = 0;
-
+       unsigned char *buf;
+       int rv;
 
        dbg("%s", __func__);
 
+       buf = kmalloc(1, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
        rv = usb_control_msg(udev,
                             usb_rcvctrlpipe(udev, 0),
                             FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
                             FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
                             0, priv->interface,
-                            (char *) &latency, 1, WDR_TIMEOUT);
-
-       if (rv < 0) {
+                            buf, 1, WDR_TIMEOUT);
+       if (rv < 0)
                dev_err(&port->dev, "Unable to read latency timer: %i\n", rv);
-               return -EIO;
-       }
-       return latency;
+       else
+               priv->latency = buf[0];
+
+       kfree(buf);
+
+       return rv;
 }
 
 static int get_serial_info(struct usb_serial_port *port,
@@ -1229,7 +1224,7 @@ static int set_serial_info(struct tty_struct *tty,
        if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
                return -EFAULT;
 
-       lock_kernel();
+       mutex_lock(&priv->cfg_lock);
        old_priv = *priv;
 
        /* Do error checking and permission checking */
@@ -1237,7 +1232,7 @@ static int set_serial_info(struct tty_struct *tty,
        if (!capable(CAP_SYS_ADMIN)) {
                if (((new_serial.flags & ~ASYNC_USR_MASK) !=
                     (priv->flags & ~ASYNC_USR_MASK))) {
-                       unlock_kernel();
+                       mutex_unlock(&priv->cfg_lock);
                        return -EPERM;
                }
                priv->flags = ((priv->flags & ~ASYNC_USR_MASK) |
@@ -1248,7 +1243,7 @@ static int set_serial_info(struct tty_struct *tty,
 
        if ((new_serial.baud_base != priv->baud_base) &&
            (new_serial.baud_base < 9600)) {
-               unlock_kernel();
+               mutex_unlock(&priv->cfg_lock);
                return -EINVAL;
        }
 
@@ -1278,11 +1273,11 @@ check_and_exit:
             (priv->flags & ASYNC_SPD_MASK)) ||
            (((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) &&
             (old_priv.custom_divisor != priv->custom_divisor))) {
-               unlock_kernel();
+               mutex_unlock(&priv->cfg_lock);
                change_speed(tty, port);
        }
        else
-               unlock_kernel();
+               mutex_unlock(&priv->cfg_lock);
        return 0;
 
 } /* set_serial_info */
@@ -1338,20 +1333,20 @@ static void ftdi_determine_type(struct usb_serial_port *port)
                                        __func__);
                }
        } else if (version < 0x200) {
-               /* Old device.  Assume its the original SIO. */
+               /* Old device.  Assume it's the original SIO. */
                priv->chip_type = SIO;
                priv->baud_base = 12000000 / 16;
                priv->write_offset = 1;
        } else if (version < 0x400) {
-               /* Assume its an FT8U232AM (or FT8U245AM) */
+               /* Assume it's an FT8U232AM (or FT8U245AM) */
                /* (It might be a BM because of the iSerialNumber bug,
                 * but it will still work as an AM device.) */
                priv->chip_type = FT8U232AM;
        } else if (version < 0x600) {
-               /* Assume its an FT232BM (or FT245BM) */
+               /* Assume it's an FT232BM (or FT245BM) */
                priv->chip_type = FT232BM;
        } else {
-               /* Assume its an FT232R  */
+               /* Assume it's an FT232R */
                priv->chip_type = FT232RL;
        }
        dev_info(&udev->dev, "Detected %s\n", ftdi_chip_name[priv->chip_type]);
@@ -1371,7 +1366,7 @@ static void ftdi_set_max_packet_size(struct usb_serial_port *port)
        struct usb_endpoint_descriptor *ep_desc = &interface->cur_altsetting->endpoint[1].desc;
 
        unsigned num_endpoints;
-       int i = 0;
+       int i;
 
        num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
        dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints);
@@ -1423,7 +1418,7 @@ static ssize_t store_latency_timer(struct device *dev,
        struct usb_serial_port *port = to_usb_serial_port(dev);
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        int v = simple_strtoul(valbuf, NULL, 10);
-       int rv = 0;
+       int rv;
 
        priv->latency = v;
        rv = write_latency_timer(port);
@@ -1440,9 +1435,8 @@ static ssize_t store_event_char(struct device *dev,
        struct usb_serial_port *port = to_usb_serial_port(dev);
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        struct usb_device *udev = port->serial->dev;
-       char buf[1];
        int v = simple_strtoul(valbuf, NULL, 10);
-       int rv = 0;
+       int rv;
 
        dbg("%s: setting event char = %i", __func__, v);
 
@@ -1451,8 +1445,7 @@ static ssize_t store_event_char(struct device *dev,
                             FTDI_SIO_SET_EVENT_CHAR_REQUEST,
                             FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
                             v, priv->interface,
-                            buf, 0, WDR_TIMEOUT);
-
+                            NULL, 0, WDR_TIMEOUT);
        if (rv < 0) {
                dbg("Unable to write event character: %i", rv);
                return -EIO;
@@ -1551,6 +1544,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
 
        kref_init(&priv->kref);
        spin_lock_init(&priv->tx_lock);
+       mutex_init(&priv->cfg_lock);
        init_waitqueue_head(&priv->delta_msr_wait);
 
        priv->flags = ASYNC_LOW_LATENCY;
@@ -1584,7 +1578,8 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
 
        ftdi_determine_type(port);
        ftdi_set_max_packet_size(port);
-       read_latency_timer(port);
+       if (read_latency_timer(port) < 0)
+               priv->latency = 16;
        create_sysfs_attrs(port);
        return 0;
 }
@@ -1629,8 +1624,6 @@ static int ftdi_NDI_device_setup(struct usb_serial *serial)
 {
        struct usb_device *udev = serial->dev;
        int latency = ndi_latency_timer;
-       int rv = 0;
-       char buf[1];
 
        if (latency == 0)
                latency = 1;
@@ -1640,10 +1633,11 @@ static int ftdi_NDI_device_setup(struct usb_serial *serial)
        dbg("%s setting NDI device latency to %d", __func__, latency);
        dev_info(&udev->dev, "NDI device with a latency value of %d", latency);
 
-       rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+       /* FIXME: errors are not returned */
+       usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                                FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
                                FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
-                               latency, 0, buf, 0, WDR_TIMEOUT);
+                               latency, 0, NULL, 0, WDR_TIMEOUT);
        return 0;
 }
 
@@ -1731,9 +1725,7 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
        struct usb_device *dev = port->serial->dev;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        unsigned long flags;
-
-       int result = 0;
-       char buf[1]; /* Needed for the usb_control_msg I think */
+       int result;
 
        dbg("%s", __func__);
 
@@ -1748,7 +1740,7 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
        usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                        FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
                        FTDI_SIO_RESET_SIO,
-                       priv->interface, buf, 0, WDR_TIMEOUT);
+                       priv->interface, NULL, 0, WDR_TIMEOUT);
 
        /* Termios defaults are set by usb_serial_init. We don't change
           port->tty->termios - this would lose speed settings, etc.
@@ -1776,7 +1768,6 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
 static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
 {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
-       char buf[1];
 
        mutex_lock(&port->serial->disc_mutex);
        if (!port->serial->disconnected) {
@@ -1785,7 +1776,7 @@ static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
                            usb_sndctrlpipe(port->serial->dev, 0),
                            FTDI_SIO_SET_FLOW_CTRL_REQUEST,
                            FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
-                           0, priv->interface, buf, 0,
+                           0, priv->interface, NULL, 0,
                            WDR_TIMEOUT) < 0) {
                            dev_err(&port->dev, "error from flowcontrol urb\n");
                }
@@ -1846,7 +1837,7 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,
        spin_lock_irqsave(&priv->tx_lock, flags);
        if (priv->tx_outstanding_urbs > URB_UPPER_LIMIT) {
                spin_unlock_irqrestore(&priv->tx_lock, flags);
-               dbg("%s - write limit hit\n", __func__);
+               dbg("%s - write limit hit", __func__);
                return 0;
        }
        priv->tx_outstanding_urbs++;
@@ -2153,8 +2144,7 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
 {
        struct usb_serial_port *port = tty->driver_data;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
-       __u16 urb_value = 0;
-       char buf[1];
+       __u16 urb_value;
 
        /* break_state = -1 to turn on break, and 0 to turn off break */
        /* see drivers/char/tty_io.c to see it used */
@@ -2170,7 +2160,7 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
                        FTDI_SIO_SET_DATA_REQUEST,
                        FTDI_SIO_SET_DATA_REQUEST_TYPE,
                        urb_value , priv->interface,
-                       buf, 0, WDR_TIMEOUT) < 0) {
+                       NULL, 0, WDR_TIMEOUT) < 0) {
                dev_err(&port->dev, "%s FAILED to enable/disable break state "
                        "(state was %d)\n", __func__, break_state);
        }
@@ -2194,7 +2184,6 @@ static void ftdi_set_termios(struct tty_struct *tty,
        struct ktermios *termios = tty->termios;
        unsigned int cflag = termios->c_cflag;
        __u16 urb_value; /* will hold the new flags */
-       char buf[1]; /* Perhaps I should dynamically alloc this? */
 
        /* Added for xon/xoff support */
        unsigned int iflag = termios->c_iflag;
@@ -2245,12 +2234,10 @@ static void ftdi_set_termios(struct tty_struct *tty,
        }
        if (cflag & CSIZE) {
                switch (cflag & CSIZE) {
-               case CS5: urb_value |= 5; dbg("Setting CS5"); break;
-               case CS6: urb_value |= 6; dbg("Setting CS6"); break;
                case CS7: urb_value |= 7; dbg("Setting CS7"); break;
                case CS8: urb_value |= 8; dbg("Setting CS8"); break;
                default:
-                       dev_err(&port->dev, "CSIZE was set but not CS5-CS8\n");
+                       dev_err(&port->dev, "CSIZE was set but not CS7-CS8\n");
                }
        }
 
@@ -2262,7 +2249,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
                            FTDI_SIO_SET_DATA_REQUEST,
                            FTDI_SIO_SET_DATA_REQUEST_TYPE,
                            urb_value , priv->interface,
-                           buf, 0, WDR_SHORT_TIMEOUT) < 0) {
+                           NULL, 0, WDR_SHORT_TIMEOUT) < 0) {
                dev_err(&port->dev, "%s FAILED to set "
                        "databits/stopbits/parity\n", __func__);
        }
@@ -2274,7 +2261,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
                                    0, priv->interface,
-                                   buf, 0, WDR_TIMEOUT) < 0) {
+                                   NULL, 0, WDR_TIMEOUT) < 0) {
                        dev_err(&port->dev,
                                "%s error from disable flowcontrol urb\n",
                                __func__);
@@ -2300,7 +2287,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
                                    0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
-                                   buf, 0, WDR_TIMEOUT) < 0) {
+                                   NULL, 0, WDR_TIMEOUT) < 0) {
                        dev_err(&port->dev,
                                "urb failed to set to rts/cts flow control\n");
                }
@@ -2332,7 +2319,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
                                            FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
                                            urb_value , (FTDI_SIO_XON_XOFF_HS
                                                         | priv->interface),
-                                           buf, 0, WDR_TIMEOUT) < 0) {
+                                           NULL, 0, WDR_TIMEOUT) < 0) {
                                dev_err(&port->dev, "urb failed to set to "
                                        "xon/xoff flow control\n");
                        }
@@ -2346,7 +2333,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
                                            FTDI_SIO_SET_FLOW_CTRL_REQUEST,
                                            FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
                                            0, priv->interface,
-                                           buf, 0, WDR_TIMEOUT) < 0) {
+                                           NULL, 0, WDR_TIMEOUT) < 0) {
                                dev_err(&port->dev,
                                        "urb failed to clear flow control\n");
                        }
@@ -2360,21 +2347,22 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)
 {
        struct usb_serial_port *port = tty->driver_data;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
-       unsigned char buf[2];
+       unsigned char *buf;
+       int len;
        int ret;
 
        dbg("%s TIOCMGET", __func__);
+
+       buf = kmalloc(2, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       /*
+        * The 8U232AM returns a two byte value (the SIO a 1 byte value) in
+        * the same format as the data returned from the in point.
+        */
        switch (priv->chip_type) {
        case SIO:
-               /* Request the status from the device */
-               ret = usb_control_msg(port->serial->dev,
-                          usb_rcvctrlpipe(port->serial->dev, 0),
-                          FTDI_SIO_GET_MODEM_STATUS_REQUEST,
-                          FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
-                          0, 0,
-                          buf, 1, WDR_TIMEOUT);
-               if (ret < 0)
-                       return ret;
+               len = 1;
                break;
        case FT8U232AM:
        case FT232BM:
@@ -2382,27 +2370,30 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)
        case FT232RL:
        case FT2232H:
        case FT4232H:
-               /* the 8U232AM returns a two byte value (the sio is a 1 byte
-                  value) - in the same format as the data returned from the in
-                  point */
-               ret = usb_control_msg(port->serial->dev,
-                                  usb_rcvctrlpipe(port->serial->dev, 0),
-                                  FTDI_SIO_GET_MODEM_STATUS_REQUEST,
-                                  FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
-                                  0, priv->interface,
-                                  buf, 2, WDR_TIMEOUT);
-               if (ret < 0)
-                       return ret;
+               len = 2;
                break;
        default:
-               return -EFAULT;
+               ret = -EFAULT;
+               goto out;
        }
 
-       return  (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
+       ret = usb_control_msg(port->serial->dev,
+                       usb_rcvctrlpipe(port->serial->dev, 0),
+                       FTDI_SIO_GET_MODEM_STATUS_REQUEST,
+                       FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
+                       0, priv->interface,
+                       buf, len, WDR_TIMEOUT);
+       if (ret < 0)
+               goto out;
+
+       ret = (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
                (buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
                (buf[0]  & FTDI_SIO_RI_MASK  ? TIOCM_RI  : 0) |
                (buf[0]  & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 0) |
                priv->last_dtr_rts;
+out:
+       kfree(buf);
+       return ret;
 }
 
 static int ftdi_tiocmset(struct tty_struct *tty, struct file *file,