X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fserial%2Fpl2303.c;h=3368d2b0412d2ef74cdc4b9784b4f877f741522d;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=c61e56829a960c1d7011ba7c7b75e9cdb21b6c77;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index c61e56829..3368d2b04 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -55,18 +55,35 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.11" +#define DRIVER_VERSION "v0.12" #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" static int debug; +#define PL2303_CLOSING_WAIT (30*HZ) + +#define PL2303_BUF_SIZE 1024 +#define PL2303_TMP_BUF_SIZE 1024 + +static DECLARE_MUTEX(pl2303_tmp_buf_sem); + +struct pl2303_buf { + unsigned int buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; + static struct usb_device_id id_table [] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, + { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, @@ -76,6 +93,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, { } /* Terminating entry */ }; @@ -132,14 +150,26 @@ static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs); static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs); static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs); -static int pl2303_write (struct usb_serial_port *port, int from_user, +static int pl2303_write (struct usb_serial_port *port, const unsigned char *buf, int count); +static void pl2303_send (struct usb_serial_port *port); +static int pl2303_write_room(struct usb_serial_port *port); +static int pl2303_chars_in_buffer(struct usb_serial_port *port); static void pl2303_break_ctl(struct usb_serial_port *port,int break_state); static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file); static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); static int pl2303_startup (struct usb_serial *serial); static void pl2303_shutdown (struct usb_serial *serial); +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size); +static void pl2303_buf_free(struct pl2303_buf *pb); +static void pl2303_buf_clear(struct pl2303_buf *pb); +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb); +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb); +static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, + unsigned int count); +static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count); /* All of the device info needed for the PL2303 SIO serial converter */ @@ -162,6 +192,8 @@ static struct usb_serial_device_type pl2303_device = { .read_bulk_callback = pl2303_read_bulk_callback, .read_int_callback = pl2303_read_int_callback, .write_bulk_callback = pl2303_write_bulk_callback, + .write_room = pl2303_write_room, + .chars_in_buffer = pl2303_chars_in_buffer, .attach = pl2303_startup, .shutdown = pl2303_shutdown, }; @@ -174,6 +206,8 @@ enum pl2303_type { struct pl2303_private { spinlock_t lock; + struct pl2303_buf *buf; + int write_urb_in_use; wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; @@ -201,14 +235,28 @@ static int pl2303_startup (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL); if (!priv) - return -ENOMEM; + goto cleanup; memset (priv, 0x00, sizeof (struct pl2303_private)); spin_lock_init(&priv->lock); + priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); + if (priv->buf == NULL) { + kfree(priv); + goto cleanup; + } init_waitqueue_head(&priv->delta_msr_wait); priv->type = type; usb_set_serial_port_data(serial->port[i], priv); } return 0; + +cleanup: + for (--i; i>=0; --i) { + priv = usb_get_serial_port_data(serial->port[i]); + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(serial->port[i], NULL); + } + return -ENOMEM; } static int set_control_lines (struct usb_device *dev, u8 value) @@ -222,42 +270,97 @@ static int set_control_lines (struct usb_device *dev, u8 value) return retval; } -static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) +static int pl2303_write (struct usb_serial_port *port, const unsigned char *buf, int count) { - int result; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); if (!count) return count; - if (port->write_urb->status == -EINPROGRESS) { - dbg("%s - already writing", __FUNCTION__); - return 0; + spin_lock_irqsave(&priv->lock, flags); + count = pl2303_buf_put(priv->buf, buf, count); + spin_unlock_irqrestore(&priv->lock, flags); + + pl2303_send(port); + + return count; +} + +static void pl2303_send(struct usb_serial_port *port) +{ + int count, result; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->write_urb_in_use) { + spin_unlock_irqrestore(&priv->lock, flags); + return; } - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; - if (from_user) { - if (copy_from_user (port->write_urb->transfer_buffer, buf, count)) - return -EFAULT; - } else { - memcpy (port->write_urb->transfer_buffer, buf, count); + count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, + port->bulk_out_size); + + if (count == 0) { + spin_unlock_irqrestore(&priv->lock, flags); + return; } - + + priv->write_urb_in_use = 1; + + spin_unlock_irqrestore(&priv->lock, flags); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_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) + if (result) { dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); - else - result = count; + priv->write_urb_in_use = 0; + // TODO: reschedule pl2303_send + } - return result; + schedule_work(&port->work); +} + +static int pl2303_write_room(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + int room = 0; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + room = pl2303_buf_space_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s - returns %d", __FUNCTION__, room); + return room; } +static int pl2303_chars_in_buffer(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + int chars = 0; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + spin_lock_irqsave(&priv->lock, flags); + chars = pl2303_buf_data_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s - returns %d", __FUNCTION__, chars); + return chars; +} static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) { @@ -422,7 +525,7 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol } kfree (buf); -} +} static int pl2303_open (struct usb_serial_port *port, struct file *filp) { @@ -461,7 +564,7 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0); - + if (priv->type == HX) { /* HX chip */ SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44); @@ -504,45 +607,67 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) static void pl2303_close (struct usb_serial_port *port, struct file *filp) { - struct pl2303_private *priv; + struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int c_cflag; - int result; + int bps; + long timeout; + wait_queue_t wait; \ dbg("%s - port %d", __FUNCTION__, port->number); - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __FUNCTION__); - result = usb_unlink_urb (port->write_urb); - if (result) - dbg("%s - usb_unlink_urb (write_urb)" - " failed with reason: %d", __FUNCTION__, - result); + /* wait for data to drain from the buffer */ + spin_lock_irqsave(&priv->lock, flags); + timeout = PL2303_CLOSING_WAIT; + init_waitqueue_entry(&wait, current); + add_wait_queue(&port->tty->write_wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (pl2303_buf_data_avail(priv->buf) == 0 + || timeout == 0 || signal_pending(current) + || !usb_get_intfdata(port->serial->interface)) /* disconnect */ + break; + spin_unlock_irqrestore(&priv->lock, flags); + timeout = schedule_timeout(timeout); + spin_lock_irqsave(&priv->lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->tty->write_wait, &wait); + /* clear out any remaining data in the buffer */ + pl2303_buf_clear(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); - result = usb_unlink_urb (port->read_urb); - if (result) - dbg("%s - usb_unlink_urb (read_urb) " - "failed with reason: %d", __FUNCTION__, - result); + /* wait for characters to drain from the device */ + /* (this is long enough for the entire 256 byte */ + /* pl2303 hardware buffer to drain with no flow */ + /* control for data rates of 1200 bps or more, */ + /* for lower rates we should really know how much */ + /* data is in the buffer to compute a delay */ + /* that is not unnecessarily long) */ + bps = tty_get_baud_rate(port->tty); + if (bps > 1200) + timeout = max((HZ*2560)/bps,HZ/10); + else + timeout = 2*HZ; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(timeout); - result = usb_unlink_urb (port->interrupt_in_urb); - if (result) - dbg("%s - usb_unlink_urb (interrupt_in_urb)" - " failed with reason: %d", __FUNCTION__, - result); + /* shutdown our urbs */ + dbg("%s - shutting down urbs", __FUNCTION__); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); if (port->tty) { c_cflag = port->tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ - priv = usb_get_serial_port_data(port); spin_lock_irqsave(&priv->lock, flags); priv->line_control = 0; spin_unlock_irqrestore (&priv->lock, flags); set_control_lines (port->serial->dev, 0); } } - } static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, @@ -672,12 +797,17 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) static void pl2303_shutdown (struct usb_serial *serial) { int i; + struct pl2303_private *priv; dbg("%s", __FUNCTION__); for (i = 0; i < serial->num_ports; ++i) { - kfree (usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); + priv = usb_get_serial_port_data(serial->port[i]); + if (priv) { + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(serial->port[i], NULL); + } } } @@ -815,11 +945,23 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct pl2303_private *priv = usb_get_serial_port_data(port); int result; dbg("%s - port %d", __FUNCTION__, port->number); - - if (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); @@ -828,14 +970,199 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) 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; + } - return; + priv->write_urb_in_use = 0; + + /* send any buffered data */ + pl2303_send(port); +} + + +/* + * pl2303_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ + +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) +{ + + struct pl2303_buf *pb; + + + if (size == 0) + return NULL; + + pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); + if (pb == NULL) + return NULL; + + pb->buf_buf = kmalloc(size, GFP_KERNEL); + if (pb->buf_buf == NULL) { + kfree(pb); + return NULL; } - schedule_work(&port->work); + pb->buf_size = size; + pb->buf_get = pb->buf_put = pb->buf_buf; + + return pb; + +} + + +/* + * pl2303_buf_free + * + * Free the buffer and all associated memory. + */ + +static void pl2303_buf_free(struct pl2303_buf *pb) +{ + if (pb != NULL) { + if (pb->buf_buf != NULL) + kfree(pb->buf_buf); + kfree(pb); + } +} + + +/* + * pl2303_buf_clear + * + * Clear out all data in the circular buffer. + */ + +static void pl2303_buf_clear(struct pl2303_buf *pb) +{ + if (pb != NULL) + pb->buf_get = pb->buf_put; + /* equivalent to a get of all data available */ +} + + +/* + * pl2303_buf_data_avail + * + * Return the number of bytes of data available in the circular + * buffer. + */ + +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) +{ + if (pb != NULL) + return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); + else + return 0; +} + + +/* + * pl2303_buf_space_avail + * + * Return the number of bytes of space available in the circular + * buffer. + */ + +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) +{ + if (pb != NULL) + return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); + else + return 0; +} + + +/* + * pl2303_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 unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, + unsigned int count) +{ + + unsigned int len; + + + if (pb == NULL) + return 0; + + len = pl2303_buf_space_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_put; + if (count > len) { + memcpy(pb->buf_put, buf, len); + memcpy(pb->buf_buf, buf+len, count - len); + pb->buf_put = pb->buf_buf + count - len; + } else { + memcpy(pb->buf_put, buf, count); + if (count < len) + pb->buf_put += count; + else /* count == len */ + pb->buf_put = pb->buf_buf; + } + + return count; + } +/* + * pl2303_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 unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count) +{ + + unsigned int len; + + + if (pb == NULL) + return 0; + + len = pl2303_buf_data_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_get; + if (count > len) { + memcpy(buf, pb->buf_get, len); + memcpy(buf+len, pb->buf_buf, count - len); + pb->buf_get = pb->buf_buf + count - len; + } else { + memcpy(buf, pb->buf_get, count); + if (count < len) + pb->buf_get += count; + else /* count == len */ + pb->buf_get = pb->buf_buf; + } + + return count; + +} + static int __init pl2303_init (void) { int retval; @@ -865,6 +1192,7 @@ module_init(pl2303_init); module_exit(pl2303_exit); MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR);