From: Vitja Makarov <vitja.makarov@gmail.com>
To: Greg KH <greg@kroah.com>
Cc: linux-usb-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] ftdi_sio patch
Date: Wed, 07 Jun 2006 11:21:15 +0400 [thread overview]
Message-ID: <44867E6B.9010304@gmail.com> (raw)
In-Reply-To: <20060604204210.GC23895@kroah.com>
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.makarov@gmail.com>
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 <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/serial.h>
+#include <linux/circ_buf.h>
#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;
prev parent reply other threads:[~2006-06-07 7:21 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-06-04 19:48 [PATCH] ftdi_sio patch Vitja Makarov
2006-06-04 20:42 ` Greg KH
2006-06-07 7:21 ` Vitja Makarov [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=44867E6B.9010304@gmail.com \
--to=vitja.makarov@gmail.com \
--cc=greg@kroah.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb-devel@lists.sourceforge.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.