From: Greg KH To: Alan Cox Cc: linux-usb-devel@lists.sourceforge.net Subject: [PATCH 3 of 3] USB Whiteheat driver fixes Hi, Here's a patch against 2.2.21-rc1 for the USB Whiteheat driver that adds locking to enable it to work properly on SMP machines. thanks, greg k-h diff -Nru a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c --- a/drivers/usb/serial/whiteheat.c Tue Mar 12 09:53:09 2002 +++ b/drivers/usb/serial/whiteheat.c Tue Mar 12 09:53:09 2002 @@ -11,6 +11,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (05/30/2001) gkh + * switched from using spinlock to a semaphore, which fixes lots of problems. + * * (04/08/2001) gb * Identify version on module load. * @@ -82,7 +85,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.0.0" +#define DRIVER_VERSION "v1.1" #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB ConnectTech WhiteHEAT driver" @@ -104,7 +107,7 @@ static __u16 connecttech_vendor_id = CONNECT_TECH_VENDOR_ID; static __u16 connecttech_whiteheat_fake_product_id = CONNECT_TECH_FAKE_WHITE_HEAT_ID; static __u16 connecttech_whiteheat_product_id = CONNECT_TECH_WHITE_HEAT_ID; -struct usb_serial_device_type whiteheat_fake_device = { +static struct usb_serial_device_type whiteheat_fake_device = { name: "Connect Tech - WhiteHEAT - (prerenumeration)", idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */ idProduct: &connecttech_whiteheat_fake_product_id, /* the White Heat initial product id */ @@ -117,7 +120,7 @@ num_ports: 1, startup: whiteheat_startup }; -struct usb_serial_device_type whiteheat_device = { +static struct usb_serial_device_type whiteheat_device = { name: "Connect Tech - WhiteHEAT", idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */ idProduct: &connecttech_whiteheat_product_id, /* the White Heat real product id */ @@ -228,6 +231,7 @@ struct usb_serial_port *port; int timeout; __u8 *transfer_buffer; + int retval = 0; dbg(__FUNCTION__" - command %d", command); @@ -240,9 +244,10 @@ memcpy (&transfer_buffer[1], data, datasize); port->write_urb->transfer_buffer_length = datasize + 1; port->write_urb->dev = serial->dev; - if (usb_submit_urb (port->write_urb)) { + retval = usb_submit_urb (port->write_urb); + if (retval) { dbg (__FUNCTION__" - submit urb failed"); - return -1; + goto exit; } /* wait for the command to complete */ @@ -253,20 +258,21 @@ if (info->command_finished == FALSE) { dbg (__FUNCTION__ " - command timed out."); - return -1; + retval = -ETIMEDOUT; + goto exit; } if (info->command_finished == WHITEHEAT_CMD_FAILURE) { dbg (__FUNCTION__ " - command failed."); - return -1; + retval = -EIO; + goto exit; } - if (info->command_finished == WHITEHEAT_CMD_COMPLETE) { + if (info->command_finished == WHITEHEAT_CMD_COMPLETE) dbg (__FUNCTION__ " - command completed."); - return 0; - } - return 0; +exit: + return retval; } @@ -275,10 +281,12 @@ struct whiteheat_min_set open_command; struct usb_serial_port *command_port; struct whiteheat_private *info; - int result; + int retval = 0; dbg(__FUNCTION__" - port %d", port->number); + down (&port->sem); + ++port->open_count; MOD_INC_USE_COUNT; @@ -291,7 +299,8 @@ info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL); if (info == NULL) { err(__FUNCTION__ " - out of memory"); - return -ENOMEM; + retval = -ENOMEM; + goto error_exit; } init_waitqueue_head(&info->wait_command); @@ -300,27 +309,45 @@ command_port->read_urb->complete = command_port_read_callback; command_port->read_urb->dev = port->serial->dev; command_port->tty = port->tty; /* need this to "fake" our our sanity check macros */ - usb_submit_urb (command_port->read_urb); + retval = usb_submit_urb (command_port->read_urb); + if (retval) { + err(__FUNCTION__ " - failed submitting read urb, error %d", retval); + goto error_exit; + } } /* Start reading from the device */ port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb); - if (result) - err(__FUNCTION__ " - failed submitting read urb, error %d", result); + retval = usb_submit_urb(port->read_urb); + if (retval) { + err(__FUNCTION__ " - failed submitting read urb, error %d", retval); + goto error_exit; + } /* send an open port command */ /* firmware uses 1 based port numbering */ open_command.port = port->number - port->serial->minor + 1; - whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); + retval = whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); + if (retval) + goto error_exit; /* Need to do device specific setup here (control lines, baud rate, etc.) */ /* FIXME!!! */ } dbg(__FUNCTION__ " - exit"); + up (&port->sem); + + return retval; + +error_exit: + --port->open_count; + MOD_DEC_USE_COUNT; + + dbg(__FUNCTION__ " - error_exit"); + up (&port->sem); - return 0; + return retval; } @@ -330,6 +357,7 @@ dbg(__FUNCTION__ " - port %d", port->number); + down (&port->sem); --port->open_count; if (port->open_count <= 0) { @@ -347,6 +375,7 @@ port->active = 0; } MOD_DEC_USE_COUNT; + up (&port->sem); } @@ -360,25 +389,28 @@ static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios) { - unsigned int cflag = port->tty->termios->c_cflag; + unsigned int cflag; struct whiteheat_port_settings port_settings; dbg(__FUNCTION__ " -port %d", port->number); + down (&port->sem); + + if ((!port->tty) || (!port->tty->termios)) { + dbg(__FUNCTION__" - no tty structures"); + goto exit; + } + + cflag = port->tty->termios->c_cflag; /* check that they really want us to change something */ if (old_termios) { if ((cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { dbg(__FUNCTION__ " - nothing to change..."); - return; + goto exit; } } - if ((!port->tty) || (!port->tty->termios)) { - dbg(__FUNCTION__" - no tty structures"); - return; - } - /* set the port number */ /* firmware uses 1 based port numbering */ port_settings.port = port->number + 1; @@ -443,6 +475,8 @@ /* now send the message to the device */ whiteheat_send_cmd (port->serial, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings)); +exit: + up (&port->sem); return; }