diff options
Diffstat (limited to 'drivers/usb/serial/ftdi_sio.c')
-rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 195 |
1 files changed, 89 insertions, 106 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 7638828e7317..6af0dfa5f5ac 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -33,12 +33,12 @@ #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> @@ -88,10 +88,10 @@ struct ftdi_private { unsigned int latency; /* latency setting in use */ spinlock_t tx_lock; /* spinlock for transmit state */ - unsigned long tx_bytes; 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 +614,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 +738,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 +817,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 +833,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 +940,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 +948,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 +963,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 +1122,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 +1139,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 +1147,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 +1160,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 +1170,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 +1223,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 +1231,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 +1242,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 +1272,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 +1332,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 +1365,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 +1417,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 +1434,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 +1444,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,9 +1543,9 @@ 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); - /* This will push the characters through immediately rather - than queue a task to deliver them */ + priv->flags = ASYNC_LOW_LATENCY; if (quirk && quirk->port_probe) @@ -1585,7 +1577,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; } @@ -1630,8 +1623,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; @@ -1641,10 +1632,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; } @@ -1720,7 +1712,7 @@ static int ftdi_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) urb->transfer_buffer_length, ftdi_read_bulk_callback, port); result = usb_submit_urb(urb, mem_flags); - if (result) + if (result && result != -EPERM) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); @@ -1732,16 +1724,10 @@ 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__); - spin_lock_irqsave(&priv->tx_lock, flags); - priv->tx_bytes = 0; - spin_unlock_irqrestore(&priv->tx_lock, flags); - write_latency_timer(port); /* No error checking for this (will get errors later anyway) */ @@ -1749,7 +1735,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. @@ -1777,7 +1763,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) { @@ -1786,7 +1771,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"); } @@ -1847,7 +1832,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++; @@ -1927,7 +1912,6 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, } else { spin_lock_irqsave(&priv->tx_lock, flags); priv->tx_outstanding_bytes += count; - priv->tx_bytes += count; spin_unlock_irqrestore(&priv->tx_lock, flags); } @@ -2154,8 +2138,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 */ @@ -2171,7 +2154,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); } @@ -2195,7 +2178,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; @@ -2246,12 +2228,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"); } } @@ -2263,7 +2243,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__); } @@ -2275,7 +2255,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__); @@ -2301,7 +2281,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"); } @@ -2333,7 +2313,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"); } @@ -2347,7 +2327,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"); } @@ -2361,21 +2341,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: @@ -2383,27 +2364,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, @@ -2508,8 +2492,7 @@ void ftdi_unthrottle(struct tty_struct *tty) port->throttled = port->throttle_req = 0; spin_unlock_irqrestore(&port->lock, flags); - /* Resubmit urb if throttled and open. */ - if (was_throttled && test_bit(ASYNCB_INITIALIZED, &port->port.flags)) + if (was_throttled) ftdi_submit_read_urb(port, GFP_KERNEL); } |