ChangeSet 1.1119.3.3, 2003/09/04 08:52:26-07:00, greg@kroah.com [PATCH] USB: backport some pl2303 B0 fixes. Also add proper locking for the port structure. This comes from the 2.6 kernel tree too. drivers/usb/serial/pl2303.c | 111 ++++++++++++++++++++++++++++++++------------ 1 files changed, 81 insertions(+), 30 deletions(-) diff -Nru a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c --- a/drivers/usb/serial/pl2303.c Fri Sep 5 17:10:55 2003 +++ b/drivers/usb/serial/pl2303.c Fri Sep 5 17:10:55 2003 @@ -153,7 +153,8 @@ .shutdown = pl2303_shutdown, }; -struct pl2303_private { +struct pl2303_private { + spinlock_t lock; u8 line_control; u8 line_status; u8 termios_initialized; @@ -170,7 +171,8 @@ if (!priv) return -ENOMEM; memset (priv, 0x00, sizeof (struct pl2303_private)); - serial->port[i].private = priv; + spin_lock_init(&priv->lock); + usb_set_serial_port_data(&serial->port[i], priv); } return 0; } @@ -223,25 +225,30 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) { struct usb_serial *serial = port->serial; - struct pl2303_private *priv; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; unsigned int cflag; unsigned char *buf; int baud; int i; + u8 control; dbg("%s - port %d, initialized = %d", __FUNCTION__, port->number, - ((struct pl2303_private *) port->private)->termios_initialized); + priv->termios_initialized); if ((!port->tty) || (!port->tty->termios)) { dbg("%s - no tty structures", __FUNCTION__); return; } - if (!(((struct pl2303_private *) port->private)->termios_initialized)) { + spin_lock_irqsave(&priv->lock, flags); + if (!priv->termios_initialized) { *(port->tty->termios) = tty_std_termios; port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - ((struct pl2303_private *) port->private)->termios_initialized = 1; + priv->termios_initialized = 1; } + spin_unlock_irqrestore(&priv->lock, flags); + cflag = port->tty->termios->c_cflag; /* check that they really want us to change something */ if (old_termios) { @@ -341,13 +348,19 @@ 0, 0, buf, 7, 100); dbg ("0x21:0x20:0:0 %d", i); - if (cflag && CBAUD) { - priv = port->private; - if ((cflag && CBAUD) == B0) - priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); - else - priv->line_control |= (CONTROL_DTR | CONTROL_RTS); - set_control_lines (serial->dev, priv->line_control); + /* change control lines if we are switching to or from B0 */ + spin_lock_irqsave(&priv->lock, flags); + control = priv->line_control; + if ((cflag & CBAUD) == B0) + priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); + else + priv->line_control |= (CONTROL_DTR | CONTROL_RTS); + if (control != priv->line_control) { + control = priv->line_control; + spin_unlock_irqrestore(&priv->lock, flags); + set_control_lines(serial->dev, control); + } else { + spin_unlock_irqrestore(&priv->lock, flags); } buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0; @@ -435,6 +448,7 @@ { struct usb_serial *serial; struct pl2303_private *priv; + unsigned long flags; unsigned int c_cflag; int result; @@ -451,10 +465,11 @@ c_cflag = port->tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ - priv = port->private; + priv = usb_get_serial_port_data(port); + spin_lock_irqsave(&priv->lock, flags); priv->line_control = 0; - set_control_lines (port->serial->dev, - priv->line_control); + spin_unlock_irqrestore (&priv->lock, flags); + set_control_lines (port->serial->dev, 0); } } @@ -482,12 +497,15 @@ static int set_modem_info (struct usb_serial_port *port, unsigned int cmd, unsigned int *value) { - struct pl2303_private *priv = port->private; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; unsigned int arg; + u8 control; if (copy_from_user(&arg, value, sizeof(int))) return -EFAULT; + spin_lock_irqsave (&priv->lock, flags); switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) @@ -511,17 +529,25 @@ priv->line_control |= ((arg & TIOCM_DTR) ? CONTROL_DTR : 0); break; } + control = priv->line_control; + spin_unlock_irqrestore (&priv->lock, flags); - return set_control_lines (port->serial->dev, priv->line_control); + return set_control_lines (port->serial->dev, control); } static int get_modem_info (struct usb_serial_port *port, unsigned int *value) { - struct pl2303_private *priv = port->private; - unsigned int mcr = priv->line_control; - unsigned int status = priv->line_status; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int mcr; + unsigned int status; unsigned int result; + spin_lock_irqsave (&priv->lock, flags); + mcr = priv->line_control; + status = priv->line_status; + spin_unlock_irqrestore (&priv->lock, flags); + result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0) | ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0) | ((status & UART_CTS) ? TIOCM_CTS : 0) @@ -560,7 +586,6 @@ return -ENOIOCTLCMD; } - static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) { struct usb_serial *serial = port->serial; @@ -589,8 +614,10 @@ dbg("%s", __FUNCTION__); - for (i = 0; i < serial->num_ports; ++i) - kfree (serial->port[i].private); + 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); + } } @@ -598,17 +625,30 @@ { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - struct pl2303_private *priv = port->private; + struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; + unsigned long flags; + + dbg("%s (%d)", __FUNCTION__, port->number); /* ints auto restart... */ - if (!serial) { + 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); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); return; } - if (urb->status) { - urb->status = 0; + if (!serial) { return; } @@ -618,7 +658,9 @@ return; /* Save off the uart status for others to look at */ + spin_lock_irqsave(&priv->lock, flags); priv->line_status = data[UART_STATE]; + spin_unlock_irqrestore(&priv->lock, flags); return; } @@ -628,9 +670,10 @@ { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - struct pl2303_private *priv = port->private; + struct pl2303_private *priv = usb_get_serial_port_data(port); struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; + unsigned long flags; int i; int result; u8 status; @@ -670,7 +713,10 @@ /* get tty_flag from status */ tty_flag = TTY_NORMAL; + + spin_lock_irqsave(&priv->lock, flags); status = priv->line_status; + spin_unlock_irqrestore(&priv->lock, flags); /* break takes precedence over parity, */ /* which takes precedence over framing errors */ @@ -745,9 +791,14 @@ static int __init pl2303_init (void) { - usb_serial_register (&pl2303_device); + int retval; + retval = usb_serial_register(&pl2303_device); + if (retval) + goto failed_usb_serial_register; info(DRIVER_DESC " " DRIVER_VERSION); return 0; +failed_usb_serial_register: + return retval; }