From: Greg KH To: Alan Cox Cc: linux-usb-devel@lists.sourceforge.net Subject: [PATCH 06 of 15] USB Serial ftdi_sio driver updated This patch updates the ftdi_sio driver. diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c Thu Jan 3 21:41:54 2002 +++ b/drivers/usb/serial/ftdi_sio.c Thu Jan 3 21:41:54 2002 @@ -1,7 +1,7 @@ /* * USB FTDI SIO driver * - * Copyright (C) 1999, 2000 + * Copyright (C) 1999 - 2001 * Greg Kroah-Hartman (greg@kroah.com) * Bill Ryder (bryder@sgi.com) * @@ -12,9 +12,21 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * - * See http://reality.sgi.com/bryder_wellington/ftdi_sio for upto date testing info + * See http://ftdi-usb-sio.sourceforge.net for upto date testing info * and extra documentation * + * (04/Nov/2001) Bill Ryder + * Fixed bug in read_bulk_callback where incorrect urb buffer was used. + * cleaned up write offset calculation + * added write_room since default values can be incorrect for sio + * changed write_bulk_callback to use same queue_task as other drivers + * (the previous version caused panics) + * Removed port iteration code since the device only has one I/O port and it + * was wrong anyway. + * + * (31/May/2001) gkh + * switched from using spinlock to a semaphore, which fixes lots of problems. + * * (23/May/2001) Bill Ryder * Added runtime debug patch (thanx Tyson D Sawyer). * Cleaned up comments for 8U232 @@ -91,7 +103,6 @@ #include #include #include - #ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1; #else @@ -105,7 +116,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.1.0" +#define DRIVER_VERSION "v1.2.0" #define DRIVER_AUTHOR "Greg Kroah-Hartman , Bill Ryder " #define DRIVER_DESC "USB FTDI RS232 Converters Driver" @@ -114,6 +125,7 @@ struct ftdi_private { ftdi_type_t ftdi_type; __u16 last_set_data_urb_value ; /* the last data state set - needed for doing a break */ + int write_offset; }; /* function prototypes for a FTDI serial converter */ static int ftdi_sio_startup (struct usb_serial *serial); @@ -122,6 +134,7 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp); static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp); static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static int ftdi_sio_write_room (struct usb_serial_port *port); static void ftdi_sio_write_bulk_callback (struct urb *urb); static void ftdi_sio_read_bulk_callback (struct urb *urb); static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old); @@ -149,6 +162,7 @@ open: ftdi_sio_open, close: ftdi_sio_close, write: ftdi_sio_write, + write_room: ftdi_sio_write_room, read_bulk_callback: ftdi_sio_read_bulk_callback, write_bulk_callback: ftdi_sio_write_bulk_callback, ioctl: ftdi_sio_ioctl, @@ -158,7 +172,7 @@ shutdown: ftdi_sio_shutdown, }; -struct usb_serial_device_type ftdi_8U232AM_device = { +static struct usb_serial_device_type ftdi_8U232AM_device = { name: "FTDI 8U232AM", idVendor: &ftdi_vendor_id, /* the FTDI vendor ID */ idProduct: &ftdi_8U232AM_product_id, @@ -172,6 +186,7 @@ open: ftdi_sio_open, close: ftdi_sio_close, write: ftdi_sio_write, + write_room: ftdi_sio_write_room, read_bulk_callback: ftdi_sio_read_bulk_callback, write_bulk_callback: ftdi_sio_write_bulk_callback, ioctl: ftdi_sio_ioctl, @@ -226,15 +241,15 @@ { struct ftdi_private *priv; - init_waitqueue_head(&serial->port[0].write_wait); priv = serial->port->private = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); if (!priv){ - err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct ftdi_private)); + err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct ftdi_private)); return -ENOMEM; } priv->ftdi_type = sio; + priv->write_offset = 1; return (0); } @@ -244,15 +259,15 @@ { struct ftdi_private *priv; - init_waitqueue_head(&serial->port[0].write_wait); priv = serial->port->private = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); if (!priv){ - err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct ftdi_private)); + err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct ftdi_private)); return -ENOMEM; } priv->ftdi_type = F8U232AM; + priv->write_offset = 0; return (0); } @@ -262,13 +277,14 @@ dbg (__FUNCTION__); - /* Close ports if they are open */ + + /* stop reads and writes on all ports */ while (serial->port[0].open_count > 0) { ftdi_sio_close (&serial->port[0], NULL); } - if (serial->port->private){ - kfree(serial->port->private); - serial->port->private = NULL; + if (serial->port[0].private){ + kfree(serial->port[0].private); + serial->port[0].private = NULL; } } @@ -278,13 +294,12 @@ { /* ftdi_sio_open */ struct termios tmp_termios; struct usb_serial *serial = port->serial; - unsigned long flags; /* Used for spinlock */ - int result; + int result = 0; char buf[1]; /* Needed for the usb_control_msg I think */ dbg(__FUNCTION__); - spin_lock_irqsave (&port->port_lock, flags); + down (&port->sem); MOD_INC_USE_COUNT; ++port->open_count; @@ -292,8 +307,6 @@ if (!port->active){ port->active = 1; - spin_unlock_irqrestore (&port->port_lock, flags); - /* This will push the characters through immediately rather than queue a task to deliver them */ port->tty->low_latency = 1; @@ -329,63 +342,61 @@ result = usb_submit_urb(port->read_urb); if (result) err(__FUNCTION__ " - failed submitting read urb, error %d", result); - } else { /* the port was already active - so no initialisation is done */ - spin_unlock_irqrestore (&port->port_lock, flags); } - return (0); + up (&port->sem); + return result; } /* ftdi_sio_open */ static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp) { /* ftdi_sio_close */ - struct usb_serial *serial = port->serial; + struct usb_serial *serial = port->serial; /* Checked in usbserial.c */ unsigned int c_cflag = port->tty->termios->c_cflag; char buf[1]; - unsigned long flags; dbg( __FUNCTION__); - spin_lock_irqsave (&port->port_lock, flags); + down (&port->sem); --port->open_count; if (port->open_count <= 0) { - spin_unlock_irqrestore (&port->port_lock, flags); - if (c_cflag & HUPCL){ - /* Disable flow control */ - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0, 0, - buf, 0, WDR_TIMEOUT) < 0) { - err("error from flowcontrol urb"); - } - - /* drop DTR */ - if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){ - err("Error from DTR LOW urb"); - } - /* drop RTS */ - if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0) { - err("Error from RTS LOW urb"); - } - } /* Note change no line is hupcl is off */ - - /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); + if (serial->dev) { + if (c_cflag & HUPCL){ + /* Disable flow control */ + if (usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, 0, buf, 0, WDR_TIMEOUT) < 0) { + err("error from flowcontrol urb"); + } + + /* drop DTR */ + if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){ + err("Error from DTR LOW urb"); + } + /* drop RTS */ + if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0) { + err("Error from RTS LOW urb"); + } + } /* Note change no line is hupcl is off */ + + /* shutdown our bulk reads and writes */ + /* ***CHECK*** behaviour when there is nothing queued */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + } port->active = 0; port->open_count = 0; } else { - spin_unlock_irqrestore (&port->port_lock, flags); - /* Send a HUP if necessary */ if (!(port->tty->termios->c_cflag & CLOCAL)){ tty_hangup(port->tty); } - } + up (&port->sem); MOD_DEC_USE_COUNT; } /* ftdi_sio_close */ @@ -402,10 +413,9 @@ { /* ftdi_sio_write */ struct usb_serial *serial = port->serial; struct ftdi_private *priv = (struct ftdi_private *)port->private; + unsigned char *first_byte = port->write_urb->transfer_buffer; int data_offset ; - int rc; int result; - DECLARE_WAITQUEUE(wait, current); dbg(__FUNCTION__ " port %d, %d bytes", port->number, count); @@ -414,87 +424,63 @@ return 0; } - if (priv->ftdi_type == sio){ - data_offset = 1; - } else { - data_offset = 0; - } + data_offset = priv->write_offset; dbg("data_offset set to %d",data_offset); - /* only do something if we have a bulk out endpoint */ - if (serial->num_bulk_out) { - unsigned char *first_byte = port->write_urb->transfer_buffer; - - /* Was seeing a race here, got a read callback, then write callback before - hitting interuptible_sleep_on - so wrapping in a wait_queue */ - - add_wait_queue(&port->write_wait, &wait); - set_current_state (TASK_INTERRUPTIBLE); - while (port->write_urb->status == -EINPROGRESS) { - dbg(__FUNCTION__ " write in progress - retrying"); - if (signal_pending(current)) { - current->state = TASK_RUNNING; - remove_wait_queue(&port->write_wait, &wait); - rc = -ERESTARTSYS; - goto err; - } - schedule(); - } - remove_wait_queue(&port->write_wait, &wait); - set_current_state(TASK_RUNNING); + if (port->write_urb->status == -EINPROGRESS) { + dbg (__FUNCTION__ " - already writing"); + return (0); + } - count += data_offset; - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; - if (count == 0) { - return 0; - } + down(&port->sem); + + count += data_offset; + count = (count > port->bulk_out_size) ? port->bulk_out_size : count; /* Copy in the data to send */ - if (from_user) { - copy_from_user(port->write_urb->transfer_buffer + data_offset , - buf, count - data_offset ); - } - else { - memcpy(port->write_urb->transfer_buffer + data_offset, - buf, count - data_offset ); - } - - first_byte = port->write_urb->transfer_buffer; - if (data_offset > 0){ - /* Write the control byte at the front of the packet*/ - *first_byte = 1 | ((count-data_offset) << 2) ; + if (from_user) { + if (copy_from_user(port->write_urb->transfer_buffer + data_offset, + buf, count - data_offset )){ + up (&port->sem); + return -EFAULT; } + } else { + memcpy(port->write_urb->transfer_buffer + data_offset, + buf, count - data_offset ); + } + + first_byte = port->write_urb->transfer_buffer; + if (data_offset > 0){ + /* Write the control byte at the front of the packet*/ + *first_byte = 1 | ((count-data_offset) << 2) ; + } - dbg(__FUNCTION__ " Bytes: %d, First Byte: 0o%03o",count, first_byte[0]); - usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte); + dbg(__FUNCTION__ " Bytes: %d, First Byte: 0x%02x",count, first_byte[0]); + usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte); - /* send the data out the bulk port */ - FILL_BULK_URB(port->write_urb, serial->dev, - usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, count, - ftdi_sio_write_bulk_callback, port); + /* send the data out the bulk port */ + FILL_BULK_URB(port->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, count, + ftdi_sio_write_bulk_callback, port); - result = usb_submit_urb(port->write_urb); - if (result) { - err(__FUNCTION__ " - failed submitting write urb, error %d", result); - return 0; - } - - dbg(__FUNCTION__ " write returning: %d", count - data_offset); - return (count - data_offset); + result = usb_submit_urb(port->write_urb); + if (result) { + err(__FUNCTION__ " - failed submitting write urb, error %d", result); + up (&port->sem); + return 0; } - - /* no bulk out, so return 0 bytes written */ - return 0; - err: /* error exit */ - return(rc); + up (&port->sem); + + dbg(__FUNCTION__ " write returning: %d", count - data_offset); + return (count - data_offset); + } /* ftdi_sio_write */ static void ftdi_sio_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial; - struct tty_struct *tty = port->tty; dbg("ftdi_sio_write_bulk_callback"); @@ -511,16 +497,29 @@ dbg("nonzero write bulk status received: %d", urb->status); return; } + queue_task(&port->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); - wake_up_interruptible(&port->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - - wake_up_interruptible(&tty->write_wait); - return; } /* ftdi_sio_write_bulk_callback */ + +static int ftdi_sio_write_room( struct usb_serial_port *port ) +{ + struct ftdi_private *priv = (struct ftdi_private *)port->private; + int room; + if ( port->write_urb->status == -EINPROGRESS) { + /* There is a race here with the _write routines but it won't hurt */ + room = 0; + } else { + room = port->bulk_out_size - priv->write_offset; + } + return(room); + + +} /* ftdi_sio_write_room */ + + static void ftdi_sio_read_bulk_callback (struct urb *urb) { /* ftdi_sio_serial_buld_callback */ struct usb_serial_port *port = (struct usb_serial_port *)urb->context; @@ -533,7 +532,7 @@ int i; int result; - dbg(__FUNCTION__); + dbg(__FUNCTION__ " - port %d", port->number); if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) { return; @@ -625,12 +624,12 @@ #endif /* Continue trying to always read */ - FILL_BULK_URB(urb, serial->dev, + FILL_BULK_URB(port->read_urb, serial->dev, usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - urb->transfer_buffer, urb->transfer_buffer_length, + port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, ftdi_sio_read_bulk_callback, port); - result = usb_submit_urb(urb); + result = usb_submit_urb(port->read_urb); if (result) err(__FUNCTION__ " - failed resubmitting read urb, error %d", result); @@ -638,7 +637,7 @@ } /* ftdi_sio_serial_read_bulk_callback */ -__u16 translate_baudrate_to_ftdi(unsigned int cflag, ftdi_type_t ftdi_type) +static __u16 translate_baudrate_to_ftdi(unsigned int cflag, ftdi_type_t ftdi_type) { /* translate_baudrate_to_ftdi */ __u16 urb_value = ftdi_sio_b9600; @@ -883,7 +882,8 @@ case TIOCMSET: /* Turns on and off the lines as specified by the mask */ dbg(__FUNCTION__ " TIOCMSET"); - if ((ret = get_user(mask, (unsigned long *) arg))) return ret; + if (get_user(mask, (unsigned long *) arg)) + return -EFAULT; urb_value = ((mask & TIOCM_DTR) ? HIGH : LOW); if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),urb_value) < 0){ err("Error from DTR set urb (TIOCMSET)"); @@ -896,7 +896,8 @@ case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ dbg(__FUNCTION__ " TIOCMBIS"); - if ((ret = get_user(mask, (unsigned long *) arg))) return ret; + if (get_user(mask, (unsigned long *) arg)) + return -EFAULT; if (mask & TIOCM_DTR){ if ((ret = set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), @@ -917,7 +918,8 @@ case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */ dbg(__FUNCTION__ " TIOCMBIC"); - if ((ret = get_user(mask, (unsigned long *) arg))) return ret; + if (get_user(mask, (unsigned long *) arg)) + return -EFAULT; if (mask & TIOCM_DTR){ if ((ret = set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),