From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751107AbWFGHVn (ORCPT ); Wed, 7 Jun 2006 03:21:43 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751110AbWFGHVm (ORCPT ); Wed, 7 Jun 2006 03:21:42 -0400 Received: from fly.promiranet.ru ([194.63.146.2]:15542 "EHLO promiranet.ru") by vger.kernel.org with ESMTP id S1751107AbWFGHVl (ORCPT ); Wed, 7 Jun 2006 03:21:41 -0400 Message-ID: <44867E6B.9010304@gmail.com> Date: Wed, 07 Jun 2006 11:21:15 +0400 From: Vitja Makarov User-Agent: Thunderbird 1.5.0.2 (X11/20060308) MIME-Version: 1.0 To: Greg KH CC: linux-usb-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org Subject: Re: [PATCH] ftdi_sio patch References: <1925ef8a0606041248i56b99f5s5fb93c6d92a6a9fc@mail.gmail.com> <20060604204210.GC23895@kroah.com> In-Reply-To: <20060604204210.GC23895@kroah.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Score: -2.8 (--) Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Sorry for my bad indentation and linewraps, I used circular buffer implementation from ti_usb_3410_5052 that uses kerenl's circ_buf structure. Signed-off-by: Vitja Makarov vitja. diff -uprN orig/drivers/usb/serial/ftdi_sio.c new/drivers/usb/serial/ftdi_sio.c --- orig/drivers/usb/serial/ftdi_sio.c 2006-03-28 01:05:57.000000000 +0400 +++ new/drivers/usb/serial/ftdi_sio.c 2006-06-07 10:48:26.000000000 +0400 @@ -258,6 +258,7 @@ #include #include #include +#include #include "usb-serial.h" #include "ftdi_sio.h" @@ -517,6 +518,8 @@ static const char *ftdi_chip_name[] = { /* Constants for read urb and write urb */ #define BUFSZ 512 #define PKTSZ 64 +/* size of write buffer */ +#define SERIAL_BUF_SIZE 1024 /* rx_flags */ #define THROTTLED 0x01 @@ -545,6 +548,11 @@ struct ftdi_private { int force_baud; /* if non-zero, force the baud rate to this value */ int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */ + + spinlock_t lock; /* private lock */ + + struct circ_buf *buf; /* write buffer */ + int write_urb_in_use; /* write urb in use indicator */ }; /* Used for TIOCMIWAIT */ @@ -562,6 +570,7 @@ static void ftdi_shutdown (struct usb_s static int ftdi_open (struct usb_serial_port *port, struct file *filp); static void ftdi_close (struct usb_serial_port *port, struct file *filp); static int ftdi_write (struct usb_serial_port *port, const unsigned char *buf, int count); +static void ftdi_send (struct usb_serial_port *port); static int ftdi_write_room (struct usb_serial_port *port); static int ftdi_chars_in_buffer (struct usb_serial_port *port); static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs); @@ -575,6 +584,14 @@ static void ftdi_break_ctl (struct usb_ static void ftdi_throttle (struct usb_serial_port *port); static void ftdi_unthrottle (struct usb_serial_port *port); +/* circular buffer */ +static struct circ_buf *serial_buf_alloc(void); +static void serial_buf_free(struct circ_buf *cb); +static void serial_buf_clear(struct circ_buf *cb); +static int serial_buf_data_avail(struct circ_buf *cb); +static int serial_buf_put(struct circ_buf *cb, const char *buf, int count); +static int serial_buf_get(struct circ_buf *cb, char *buf, int count); + static unsigned short int ftdi_232am_baud_base_to_divisor (int baud, int base); static unsigned short int ftdi_232am_baud_to_divisor (int baud); static __u32 ftdi_232bm_baud_base_to_divisor (int baud, int base); @@ -1138,6 +1155,7 @@ static int ftdi_sio_attach (struct usb_s struct usb_serial_port *port = serial->port[0]; struct ftdi_private *priv; struct ftdi_sio_quirk *quirk; + char *transfer_buffer; dbg("%s",__FUNCTION__); @@ -1147,6 +1165,7 @@ static int ftdi_sio_attach (struct usb_s return -ENOMEM; } memset(priv, 0, sizeof(*priv)); + spin_lock_init(&priv->lock); spin_lock_init(&priv->rx_lock); init_waitqueue_head(&priv->delta_msr_wait); @@ -1167,14 +1186,22 @@ static int ftdi_sio_attach (struct usb_s } INIT_WORK(&priv->rx_work, ftdi_process_read, port); + + /* Try to increase the size of write buffer */ + transfer_buffer = kmalloc(SERIAL_BUF_SIZE, GFP_KERNEL); + + if (transfer_buffer) { + port->write_urb->transfer_buffer = transfer_buffer; + port->write_urb->transfer_buffer_length = SERIAL_BUF_SIZE; + } /* Free port's existing write urb and transfer buffer. */ - if (port->write_urb) { - usb_free_urb (port->write_urb); - port->write_urb = NULL; + priv->buf = serial_buf_alloc(); + if (priv->buf == NULL) { + kfree(port->bulk_in_buffer); + kfree(priv); + return -ENOMEM; } - kfree(port->bulk_out_buffer); - port->bulk_out_buffer = NULL; usb_set_serial_port_data(serial->port[0], priv); @@ -1246,6 +1273,7 @@ static void ftdi_shutdown (struct usb_se */ if (priv) { + serial_buf_free(priv->buf); usb_set_serial_port_data(port, NULL); kfree(priv); } @@ -1258,7 +1286,7 @@ static int ftdi_open (struct usb_serial 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 */ @@ -1341,132 +1369,139 @@ static void ftdi_close (struct usb_seria /* cancel any scheduled reading */ cancel_delayed_work(&priv->rx_work); flush_scheduled_work(); - + /* shutdown our bulk read */ if (port->read_urb) usb_kill_urb(port->read_urb); + + /* shutdown our bulk write */ + if (port->write_urb) + usb_kill_urb(port->write_urb); } /* ftdi_close */ +static int ftdi_write(struct usb_serial_port *port, const unsigned char *buf, int count) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); - -/* The SIO requires the first byte to have: - * B0 1 - * B1 0 - * B2..7 length of message excluding byte 0 - * - * The new devices do not require this byte - */ -static int ftdi_write (struct usb_serial_port *port, - const unsigned char *buf, int count) -{ /* ftdi_write */ + if (!count) + return count; + + spin_lock_irqsave(&priv->lock, flags); + count = serial_buf_put(priv->buf, buf, count); + spin_unlock_irqrestore(&priv->lock, flags); + + ftdi_send(port); + + return count; +} /* ftdi_write */ + + +static void ftdi_send(struct usb_serial_port *port) +{ + int count, result; struct ftdi_private *priv = usb_get_serial_port_data(port); - struct urb *urb; - unsigned char *buffer; + unsigned long flags; int data_offset ; /* will be 1 for the SIO and 0 otherwise */ - int status; - int transfer_size; - dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count); + dbg("%s - port %d", __FUNCTION__, port->number); - if (count == 0) { - dbg("write request of 0 bytes"); - return 0; + spin_lock_irqsave(&priv->lock, flags); + + if (priv->write_urb_in_use) { + spin_unlock_irqrestore(&priv->lock, flags); + return; } - + data_offset = priv->write_offset; dbg("data_offset set to %d",data_offset); - /* Determine total transfer size */ - transfer_size = count; - if (data_offset > 0) { - /* Original sio needs control bytes too... */ - transfer_size += (data_offset * - ((count + (PKTSZ - 1 - data_offset)) / - (PKTSZ - data_offset))); - } - - buffer = kmalloc (transfer_size, GFP_ATOMIC); - if (!buffer) { - err("%s ran out of kernel memory for urb ...", __FUNCTION__); - return -ENOMEM; - } - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - err("%s - no more free urbs", __FUNCTION__); - kfree (buffer); - return -ENOMEM; + if (data_offset > 0) { + int len = port->bulk_out_size; + unsigned char *first_byte = port->write_urb->transfer_buffer; + + count = 0; + + while (serial_buf_data_avail(priv->buf) && len > 1) { + int toget = min(len - data_offset, PKTSZ - data_offset); + int cnt; + + cnt = serial_buf_get(priv->buf, first_byte + data_offset, + toget); + *first_byte |= 1 | (cnt << 2); + first_byte += cnt + data_offset; + len -= cnt + data_offset; + count += cnt + data_offset; + } + } else { + count = serial_buf_get(priv->buf, port->write_urb->transfer_buffer, + port->bulk_out_size); } - /* Copy data */ - if (data_offset > 0) { - /* Original sio requires control byte at start of each packet. */ - int user_pktsz = PKTSZ - data_offset; - int todo = count; - unsigned char *first_byte = buffer; - const unsigned char *current_position = buf; - - while (todo > 0) { - if (user_pktsz > todo) { - user_pktsz = todo; - } - /* Write the control byte at the front of the packet*/ - *first_byte = 1 | ((user_pktsz) << 2); - /* Copy data for packet */ - memcpy (first_byte + data_offset, - current_position, user_pktsz); - first_byte += user_pktsz + data_offset; - current_position += user_pktsz; - todo -= user_pktsz; - } - } else { - /* No control byte required. */ - /* Copy in the data to send */ - memcpy (buffer, buf, count); + if (count == 0) { + spin_unlock_irqrestore(&priv->lock, flags); + return; } - usb_serial_debug_data(debug, &port->dev, __FUNCTION__, transfer_size, buffer); + priv->write_urb_in_use = 1; + spin_unlock_irqrestore(&priv->lock, flags); - /* fill the buffer and send it */ - usb_fill_bulk_urb(urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), - buffer, transfer_size, - ftdi_write_bulk_callback, port); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer); - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - err("%s - failed submitting write urb, error %d", __FUNCTION__, status); - count = status; - kfree (buffer); + port->write_urb->transfer_buffer_length = count; + port->write_urb->dev = port->serial->dev; + result = usb_submit_urb (port->write_urb, GFP_ATOMIC); + if (result) { + dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); + priv->write_urb_in_use = 0; + // TODO: reschedule pl2303_send } - /* we are done with this urb, so let the host driver - * really free it when it is finished with it */ - usb_free_urb (urb); - - dbg("%s write returning: %d", __FUNCTION__, count); - return count; -} /* ftdi_write */ - + schedule_work(&port->work); +} -/* This function may get called when the device is closed */ static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { - struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - - /* free up the transfer buffer, as usb_free_urb() does not do this */ - kfree (urb->transfer_buffer); + struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct ftdi_private *priv = usb_get_serial_port_data(port); + int result; dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->status) { - dbg("nonzero write bulk status received: %d", urb->status); + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + priv->write_urb_in_use = 0; return; + default: + /* error in the urb, so we have to resubmit it */ + dbg("%s - Overflow in write", __FUNCTION__); + dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + port->write_urb->transfer_buffer_length = 1; + port->write_urb->dev = port->serial->dev; + result = usb_submit_urb (port->write_urb, GFP_ATOMIC); + if (result) + dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result); + else + return; } - schedule_work(&port->work); -} /* ftdi_write_bulk_callback */ + priv->write_urb_in_use = 0; + + /* send any buffered data */ + ftdi_send(port); +} + static int ftdi_write_room( struct usb_serial_port *port ) @@ -2078,6 +2113,129 @@ static void ftdi_unthrottle (struct usb_ schedule_work(&priv->rx_work); } +/* Circular Buffer Functions, code from ti_usb_3410_5052 used */ + +/* + * serial_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ + +static struct circ_buf *serial_buf_alloc(void) +{ + struct circ_buf *cb; + + cb = (struct circ_buf *)kmalloc(sizeof(struct circ_buf), GFP_KERNEL); + if (cb == NULL) + return NULL; + + cb->buf = kmalloc(SERIAL_BUF_SIZE, GFP_KERNEL); + if (cb->buf == NULL) { + kfree(cb); + return NULL; + } + + serial_buf_clear(cb); + + return cb; +} + + +/* + * serial_buf_free + * + * Free the buffer and all associated memory. + */ + +static void serial_buf_free(struct circ_buf *cb) +{ + kfree(cb->buf); + kfree(cb); +} + + +/* + * serial_buf_clear + * + * Clear out all data in the circular buffer. + */ + +static void serial_buf_clear(struct circ_buf *cb) +{ + cb->head = cb->tail = 0; +} + + +/* + * serial_buf_data_avail + * + * Return the number of bytes of data available in the circular + * buffer. + */ + +static int serial_buf_data_avail(struct circ_buf *cb) +{ + return CIRC_CNT(cb->head,cb->tail,SERIAL_BUF_SIZE); +} + +/* + * serial_buf_put + * + * Copy data data from a user buffer and put it into the circular buffer. + * Restrict to the amount of space available. + * + * Return the number of bytes copied. + */ + +static int serial_buf_put(struct circ_buf *cb, const char *buf, int count) +{ + int c, ret = 0; + + while (1) { + c = CIRC_SPACE_TO_END(cb->head, cb->tail, SERIAL_BUF_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(cb->buf + cb->head, buf, c); + cb->head = (cb->head + c) & (SERIAL_BUF_SIZE-1); + buf += c; + count -= c; + ret += c; + } + + return ret; +} + +/* + * serial_buf_get + * + * Get data from the circular buffer and copy to the given buffer. + * Restrict to the amount of data available. + * + * Return the number of bytes copied. + */ + +static int serial_buf_get(struct circ_buf *cb, char *buf, int count) +{ + int c, ret = 0; + + while (1) { + c = CIRC_CNT_TO_END(cb->head, cb->tail, SERIAL_BUF_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(buf, cb->buf + cb->tail, c); + cb->tail = (cb->tail + c) & (SERIAL_BUF_SIZE-1); + buf += c; + count -= c; + ret += c; + } + + return ret; +} + static int __init ftdi_init (void) { int retval;