X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fserial%2Fusb-serial.c;h=716f6806cc8902e055a7af4298e554e0512dcbc6;hb=refs%2Fheads%2Fvserver;hp=19c92e8182c773fb0aaa4f7c31f2e7284eb33437;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 19c92e818..716f6806c 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1,7 +1,7 @@ /* * USB Serial Converter driver * - * Copyright (C) 1999 - 2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 1999 - 2005 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2000 Peter Berger (pberger@brimson.com) * Copyright (C) 2000 Al Borchers (borchers@steinerpoint.com) * @@ -9,319 +9,13 @@ * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * - * This driver was originally based on the ACM driver by Armin Fuerst (which was + * This driver was originally based on the ACM driver by Armin Fuerst (which was * based on a driver by Brad Keryan) * * See Documentation/usb/usb-serial.txt for more information on using this driver * - * (12/10/2002) gkh - * Split the ports off into their own struct device, and added a - * usb-serial bus driver. - * - * (11/19/2002) gkh - * removed a few #ifdefs for the generic code and cleaned up the failure - * logic in initialization. - * - * (10/02/2002) gkh - * moved the console code to console.c and out of this file. - * - * (06/05/2002) gkh - * moved location of startup() call in serial_probe() until after all - * of the port information and endpoints are initialized. This makes - * things easier for some drivers. - * - * (04/10/2002) gkh - * added serial_read_proc function which creates a - * /proc/tty/driver/usb-serial file. - * - * (03/27/2002) gkh - * Got USB serial console code working properly and merged into the main - * version of the tree. Thanks to Randy Dunlap for the initial version - * of this code, and for pushing me to finish it up. - * The USB serial console works with any usb serial driver device. - * - * (03/21/2002) gkh - * Moved all manipulation of port->open_count into the core. Now the - * individual driver's open and close functions are called only when the - * first open() and last close() is called. Making the drivers a bit - * smaller and simpler. - * Fixed a bug if a driver didn't have the owner field set. - * - * (02/26/2002) gkh - * Moved all locking into the main serial_* functions, instead of having - * the individual drivers have to grab the port semaphore. This should - * reduce races. - * Reworked the MOD_INC logic a bit to always increment and decrement, even - * if the generic driver is being used. - * - * (10/10/2001) gkh - * usb_serial_disconnect() now sets the serial->dev pointer is to NULL to - * help prevent child drivers from accessing the device since it is now - * gone. - * - * (09/13/2001) gkh - * Moved generic driver initialize after we have registered with the USB - * core. Thanks to Randy Dunlap for pointing this problem out. - * - * (07/03/2001) gkh - * Fixed module paramater size. Thanks to John Brockmeyer for the pointer. - * Fixed vendor and product getting defined through the MODULE_PARM macro - * if the Generic driver wasn't compiled in. - * Fixed problem with generic_shutdown() not being called for drivers that - * don't have a shutdown() function. - * - * (06/06/2001) gkh - * added evil hack that is needed for the prolific pl2303 device due to the - * crazy way its endpoints are set up. - * - * (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. - * - * 2001_02_05 gkh - * Fixed buffer overflows bug with the generic serial driver. Thanks to - * Todd Squires for fixing this. - * - * (01/10/2001) gkh - * Fixed bug where the generic serial adaptor grabbed _any_ device that was - * offered to it. - * - * (12/12/2000) gkh - * Removed MOD_INC and MOD_DEC from poll and disconnect functions, and - * moved them to the serial_open and serial_close functions. - * Also fixed bug with there not being a MOD_DEC for the generic driver - * (thanks to Gary Brubaker for finding this.) - * - * (11/29/2000) gkh - * Small NULL pointer initialization cleanup which saves a bit of disk image - * - * (11/01/2000) Adam J. Richter - * instead of using idVendor/idProduct pairs, usb serial drivers - * now identify their hardware interest with usb_device_id tables, - * which they usually have anyhow for use with MODULE_DEVICE_TABLE. - * - * (10/05/2000) gkh - * Fixed bug with urb->dev not being set properly, now that the usb - * core needs it. - * - * (09/11/2000) gkh - * Removed DEBUG #ifdefs with call to usb_serial_debug_data - * - * (08/28/2000) gkh - * Added port_lock to port structure. - * Added locks for SMP safeness to generic driver - * Fixed the ability to open a generic device's port more than once. - * - * (07/23/2000) gkh - * Added bulk_out_endpointAddress to port structure. - * - * (07/19/2000) gkh, pberger, and borchers - * Modifications to allow usb-serial drivers to be modules. - * - * (07/03/2000) gkh - * Added more debugging to serial_ioctl call - * - * (06/25/2000) gkh - * Changed generic_write_bulk_callback to not call wake_up_interruptible - * directly, but to have port_softint do it at a safer time. - * - * (06/23/2000) gkh - * Cleaned up debugging statements in a quest to find UHCI timeout bug. - * - * (05/22/2000) gkh - * Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be - * removed from the individual device source files. - * - * (05/03/2000) gkh - * Added the Digi Acceleport driver from Al Borchers and Peter Berger. - * - * (05/02/2000) gkh - * Changed devfs and tty register code to work properly now. This was based on - * the ACM driver changes by Vojtech Pavlik. - * - * (04/27/2000) Ryan VanderBijl - * Put calls to *_paranoia_checks into one function. - * - * (04/23/2000) gkh - * Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports. - * Moved when the startup code printed out the devices that are supported. - * - * (04/19/2000) gkh - * Added driver for ZyXEL omni.net lcd plus ISDN TA - * Made startup info message specify which drivers were compiled in. - * - * (04/03/2000) gkh - * Changed the probe process to remove the module unload races. - * Changed where the tty layer gets initialized to have devfs work nicer. - * Added initial devfs support. - * - * (03/26/2000) gkh - * Split driver up into device specific pieces. - * - * (03/19/2000) gkh - * Fixed oops that could happen when device was removed while a program - * was talking to the device. - * Removed the static urbs and now all urbs are created and destroyed - * dynamically. - * Reworked the internal interface. Now everything is based on the - * usb_serial_port structure instead of the larger usb_serial structure. - * This fixes the bug that a multiport device could not have more than - * one port open at one time. - * - * (03/17/2000) gkh - * Added config option for debugging messages. - * Added patch for keyspan pda from Brian Warner. - * - * (03/06/2000) gkh - * Added the keyspan pda code from Brian Warner - * Moved a bunch of the port specific stuff into its own structure. This - * is in anticipation of the true multiport devices (there's a bug if you - * try to access more than one port of any multiport device right now) - * - * (02/21/2000) gkh - * Made it so that any serial devices only have to specify which functions - * they want to overload from the generic function calls (great, - * inheritance in C, in a driver, just what I wanted...) - * Added support for set_termios and ioctl function calls. No drivers take - * advantage of this yet. - * Removed the #ifdef MODULE, now there is no module specific code. - * Cleaned up a few comments in usb-serial.h that were wrong (thanks again - * to Miles Lott). - * Small fix to get_free_serial. - * - * (02/14/2000) gkh - * Removed the Belkin and Peracom functionality from the driver due to - * the lack of support from the vendor, and me not wanting people to - * accidenatly buy the device, expecting it to work with Linux. - * Added read_bulk_callback and write_bulk_callback to the type structure - * for the needs of the FTDI and WhiteHEAT driver. - * Changed all reverences to FTDI to FTDI_SIO at the request of Bill - * Ryder. - * Changed the output urb size back to the max endpoint size to make - * the ftdi_sio driver have it easier, and due to the fact that it didn't - * really increase the speed any. - * - * (02/11/2000) gkh - * Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a - * patch from Miles Lott (milos@insync.net). - * Fixed bug with not restoring the minor range that a device grabs, if - * the startup function fails (thanks Miles for finding this). - * - * (02/05/2000) gkh - * Added initial framework for the Keyspan PDA serial converter so that - * Brian Warner has a place to put his code. - * Made the ezusb specific functions generic enough that different - * devices can use them (whiteheat and keyspan_pda both need them). - * Split out a whole bunch of structure and other stuff to a separate - * usb-serial.h file. - * Made the Visor connection messages a little more understandable, now - * that Miles Lott (milos@insync.net) has gotten the Generic channel to - * work. Also made them always show up in the log file. - * - * (01/25/2000) gkh - * Added initial framework for FTDI serial converter so that Bill Ryder - * has a place to put his code. - * Added the vendor specific info from Handspring. Now we can print out - * informational debug messages as well as understand what is happening. - * - * (01/23/2000) gkh - * Fixed problem of crash when trying to open a port that didn't have a - * device assigned to it. Made the minor node finding a little smarter, - * now it looks to find a continuous space for the new device. - * - * (01/21/2000) gkh - * Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net) - * Fixed get_serial_by_minor which was all messed up for multi port - * devices. Fixed multi port problem for generic devices. Now the number - * of ports is determined by the number of bulk out endpoints for the - * generic device. - * - * (01/19/2000) gkh - * Removed lots of cruft that was around from the old (pre urb) driver - * interface. - * Made the serial_table dynamic. This should save lots of memory when - * the number of minor nodes goes up to 256. - * Added initial support for devices that have more than one port. - * Added more debugging comments for the Visor, and added a needed - * set_configuration call. - * - * (01/17/2000) gkh - * Fixed the WhiteHEAT firmware (my processing tool had a bug) - * and added new debug loader firmware for it. - * Removed the put_char function as it isn't really needed. - * Added visor startup commands as found by the Win98 dump. - * - * (01/13/2000) gkh - * Fixed the vendor id for the generic driver to the one I meant it to be. - * - * (01/12/2000) gkh - * Forget the version numbering...that's pretty useless... - * Made the driver able to be compiled so that the user can select which - * converter they want to use. This allows people who only want the Visor - * support to not pay the memory size price of the WhiteHEAT. - * Fixed bug where the generic driver (idVendor=0000 and idProduct=0000) - * grabbed the root hub. Not good. - * - * version 0.4.0 (01/10/2000) gkh - * Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT - * device. Added startup function to allow firmware to be downloaded to - * a device if it needs to be. - * Added firmware download logic to the WhiteHEAT device. - * Started to add #defines to split up the different drivers for potential - * configuration option. - * - * version 0.3.1 (12/30/99) gkh - * Fixed problems with urb for bulk out. - * Added initial support for multiple sets of endpoints. This enables - * the Handspring Visor to be attached successfully. Only the first - * bulk in / bulk out endpoint pair is being used right now. - * - * version 0.3.0 (12/27/99) gkh - * Added initial support for the Handspring Visor based on a patch from - * Miles Lott (milos@sneety.insync.net) - * Cleaned up the code a bunch and converted over to using urbs only. - * - * version 0.2.3 (12/21/99) gkh - * Added initial support for the Connect Tech WhiteHEAT converter. - * Incremented the number of ports in expectation of getting the - * WhiteHEAT to work properly (4 ports per connection). - * Added notification on insertion and removal of what port the - * device is/was connected to (and what kind of device it was). - * - * version 0.2.2 (12/16/99) gkh - * Changed major number to the new allocated number. We're legal now! - * - * version 0.2.1 (12/14/99) gkh - * Fixed bug that happens when device node is opened when there isn't a - * device attached to it. Thanks to marek@webdesign.no for noticing this. - * - * version 0.2.0 (11/10/99) gkh - * Split up internals to make it easier to add different types of serial - * converters to the code. - * Added a "generic" driver that gets it's vendor and product id - * from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net) - * for the idea and sample code (from the usb scanner driver.) - * Cleared up any licensing questions by releasing it under the GNU GPL. - * - * version 0.1.2 (10/25/99) gkh - * Fixed bug in detecting device. - * - * version 0.1.1 (10/05/99) gkh - * Changed the major number to not conflict with anything else. - * - * version 0.1 (09/28/99) gkh - * Can recognize the two different devices and start up a read from - * device when asked to. Writes also work. No control signals yet, this - * all is vendor specific data (i.e. no spec), also no control for - * different baud rates or other bit settings. - * Currently we are using the same devid as the acm driver. This needs - * to change. - * */ -#include #include #include #include @@ -332,48 +26,28 @@ #include #include #include +#include #include #include #include #include - - -#ifdef CONFIG_USB_SERIAL_DEBUG - static int debug = 1; -#else - static int debug; -#endif - -#include "usb-serial.h" +#include #include "pl2303.h" /* * Version Information */ -#define DRIVER_VERSION "v2.0" #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/" #define DRIVER_DESC "USB Serial Driver core" - -#ifdef CONFIG_USB_SERIAL_GENERIC -/* we want to look at all devices, as the vendor/product id can change - * depending on the command line argument */ -static struct usb_device_id generic_serial_ids[] = { - {.driver_info = 42}, - {} -}; - -#endif /* CONFIG_USB_SERIAL_GENERIC */ +static void port_free(struct usb_serial_port *port); /* Driver structure we register with the USB core */ static struct usb_driver usb_serial_driver = { - .owner = THIS_MODULE, .name = "usbserial", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, -#ifdef CONFIG_USB_SERIAL_GENERIC - .id_table = generic_serial_ids, -#endif + .no_dynamic_id = 1, }; /* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead @@ -383,10 +57,10 @@ static struct usb_driver usb_serial_driver = { drivers depend on it. */ -static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ +static int debug; +static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ static LIST_HEAD(usb_serial_driver_list); - struct usb_serial *usb_serial_get_by_index(unsigned index) { struct usb_serial *serial = serial_table[index]; @@ -410,7 +84,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po good_spot = 1; for (j = 1; j <= num_ports-1; ++j) - if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) { + if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) { good_spot = 0; i += j; break; @@ -427,7 +101,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po return NULL; } -static void return_serial (struct usb_serial *serial) +static void return_serial(struct usb_serial *serial) { int i; @@ -439,8 +113,54 @@ static void return_serial (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { serial_table[serial->minor + i] = NULL; } +} + +static void destroy_serial(struct kref *kref) +{ + struct usb_serial *serial; + struct usb_serial_port *port; + int i; + + serial = to_usb_serial(kref); - return; + dbg("%s - %s", __FUNCTION__, serial->type->description); + + serial->type->shutdown(serial); + + /* return the minor range that this device had */ + return_serial(serial); + + for (i = 0; i < serial->num_ports; ++i) + serial->port[i]->open_count = 0; + + /* the ports are cleaned up and released in port_release() */ + for (i = 0; i < serial->num_ports; ++i) + if (serial->port[i]->dev.parent != NULL) { + device_unregister(&serial->port[i]->dev); + serial->port[i] = NULL; + } + + /* If this is a "fake" port, we have to clean it up here, as it will + * not get cleaned up in port_release() as it was never registered with + * the driver core */ + if (serial->num_ports < serial->num_port_pointers) { + for (i = serial->num_ports; i < serial->num_port_pointers; ++i) { + port = serial->port[i]; + if (!port) + continue; + port_free(port); + } + } + + usb_put_dev(serial->dev); + + /* free up any memory that we allocated */ + kfree (serial); +} + +void usb_serial_put(struct usb_serial *serial) +{ + kref_put(&serial->kref, destroy_serial); } /***************************************************************************** @@ -451,81 +171,111 @@ static int serial_open (struct tty_struct *tty, struct file * filp) struct usb_serial *serial; struct usb_serial_port *port; unsigned int portNumber; - int retval = 0; + int retval; dbg("%s", __FUNCTION__); - /* initialize the pointer incase something fails */ - tty->driver_data = NULL; - /* get the serial object associated with this tty pointer */ serial = usb_serial_get_by_index(tty->index); if (!serial) { - retval = -ENODEV; - goto bailout; + tty->driver_data = NULL; + return -ENODEV; } - /* set up our port structure making the tty driver remember our port object, and us it */ portNumber = tty->index - serial->minor; port = serial->port[portNumber]; - tty->driver_data = port; - - port->tty = tty; - - /* lock this module before we call it, - this may, which means we must bail out, safe because we are called with BKL held */ - if (!try_module_get(serial->type->owner)) { + if (!port) { retval = -ENODEV; - goto bailout; + goto bailout_kref_put; } + if (mutex_lock_interruptible(&port->mutex)) { + retval = -ERESTARTSYS; + goto bailout_kref_put; + } + ++port->open_count; + + /* set up our port structure making the tty driver + * remember our port object, and us it */ + tty->driver_data = port; + port->tty = tty; + if (port->open_count == 1) { + + /* lock this module before we call it + * this may fail, which means we must bail out, + * safe because we are called with BKL held */ + if (!try_module_get(serial->type->driver.owner)) { + retval = -ENODEV; + goto bailout_mutex_unlock; + } + /* only call the device specific open if this * is the first time the port is opened */ retval = serial->type->open(port, filp); - if (retval) { - port->open_count = 0; - module_put(serial->type->owner); - kref_put(&serial->kref); - } + if (retval) + goto bailout_module_put; } -bailout: + + mutex_unlock(&port->mutex); + return 0; + +bailout_module_put: + module_put(serial->type->driver.owner); +bailout_mutex_unlock: + port->open_count = 0; + tty->driver_data = NULL; + port->tty = NULL; + mutex_unlock(&port->mutex); +bailout_kref_put: + usb_serial_put(serial); return retval; } static void serial_close(struct tty_struct *tty, struct file * filp) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; if (!port) return; dbg("%s - port %d", __FUNCTION__, port->number); + mutex_lock(&port->mutex); + + if (port->open_count == 0) { + mutex_unlock(&port->mutex); + return; + } + --port->open_count; - if (port->open_count <= 0) { + if (port->open_count == 0) { /* only call the device specific close if this * port is being closed by the last owner */ port->serial->type->close(port, filp); - port->open_count = 0; if (port->tty) { if (port->tty->driver_data) port->tty->driver_data = NULL; port->tty = NULL; } + + module_put(port->serial->type->driver.owner); } - module_put(port->serial->type->owner); - kref_put(&port->serial->kref); + mutex_unlock(&port->mutex); + usb_serial_put(port->serial); } -static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) +static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; int retval = -EINVAL; + if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED) + goto exit; + dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count); if (!port->open_count) { @@ -534,7 +284,7 @@ static int serial_write (struct tty_struct * tty, int from_user, const unsigned } /* pass on to the driver specific version of this function */ - retval = port->serial->type->write(port, from_user, buf, count); + retval = port->serial->type->write(port, buf, count); exit: return retval; @@ -542,8 +292,11 @@ exit: static int serial_write_room (struct tty_struct *tty) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - int retval = -EINVAL; + struct usb_serial_port *port = tty->driver_data; + int retval = -ENODEV; + + if (!port) + goto exit; dbg("%s - port %d", __FUNCTION__, port->number); @@ -561,8 +314,11 @@ exit: static int serial_chars_in_buffer (struct tty_struct *tty) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - int retval = -EINVAL; + struct usb_serial_port *port = tty->driver_data; + int retval = -ENODEV; + + if (!port) + goto exit; dbg("%s = port %d", __FUNCTION__, port->number); @@ -580,47 +336,50 @@ exit: static void serial_throttle (struct tty_struct * tty) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; + + if (!port) + return; dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { dbg ("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function */ if (port->serial->type->throttle) port->serial->type->throttle(port); - -exit: - ; } static void serial_unthrottle (struct tty_struct * tty) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; + + if (!port) + return; dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function */ if (port->serial->type->unthrottle) port->serial->type->unthrottle(port); - -exit: - ; } static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; int retval = -ENODEV; + if (!port) + goto exit; + dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd); if (!port->open_count) { @@ -638,49 +397,42 @@ exit: return retval; } -static void serial_set_termios (struct tty_struct *tty, struct termios * old) +static void serial_set_termios (struct tty_struct *tty, struct ktermios * old) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; + + if (!port) + return; dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->set_termios) port->serial->type->set_termios(port, old); - -exit: - ; } static void serial_break (struct tty_struct *tty, int break_state) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; + + if (!port) + return; dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->break_ctl) port->serial->type->break_ctl(port, break_state); - -exit: - ; -} - -static void serial_shutdown (struct usb_serial *serial) -{ - dbg ("%s", __FUNCTION__); - - serial->type->shutdown(serial); } static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) @@ -692,17 +444,19 @@ static int serial_read_proc (char *page, char **start, off_t off, int count, int char tmp[40]; dbg("%s", __FUNCTION__); - length += sprintf (page, "usbserinfo:1.0 driver:%s\n", DRIVER_VERSION); + length += sprintf (page, "usbserinfo:1.0 driver:2.0\n"); for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) { serial = usb_serial_get_by_index(i); if (serial == NULL) continue; length += sprintf (page+length, "%d:", i); - if (serial->type->owner) - length += sprintf (page+length, " module:%s", module_name(serial->type->owner)); - length += sprintf (page+length, " name:\"%s\"", serial->type->name); - length += sprintf (page+length, " vendor:%04x product:%04x", serial->vendor, serial->product); + if (serial->type->driver.owner) + length += sprintf (page+length, " module:%s", module_name(serial->type->driver.owner)); + length += sprintf (page+length, " name:\"%s\"", serial->type->description); + length += sprintf (page+length, " vendor:%04x product:%04x", + le16_to_cpu(serial->dev->descriptor.idVendor), + le16_to_cpu(serial->dev->descriptor.idProduct)); length += sprintf (page+length, " num_ports:%d", serial->num_ports); length += sprintf (page+length, " port:%d", i - serial->minor + 1); @@ -710,13 +464,15 @@ static int serial_read_proc (char *page, char **start, off_t off, int count, int length += sprintf (page+length, " path:%s", tmp); length += sprintf (page+length, "\n"); - if ((length + begin) > (off + count)) + if ((length + begin) > (off + count)) { + usb_serial_put(serial); goto done; + } if ((length + begin) < off) { begin += length; length = 0; } - kref_put(&serial->kref); + usb_serial_put(serial); } *eof = 1; done: @@ -728,44 +484,59 @@ done: static int serial_tiocmget (struct tty_struct *tty, struct file *file) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; + + if (!port) + return -ENODEV; dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return -ENODEV; } if (port->serial->type->tiocmget) return port->serial->type->tiocmget(port, file); -exit: return -EINVAL; } static int serial_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial_port *port = tty->driver_data; + + if (!port) + return -ENODEV; dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return -ENODEV; } if (port->serial->type->tiocmset) return port->serial->type->tiocmset(port, file, set, clear); -exit: return -EINVAL; } -void usb_serial_port_softint(void *private) +/* + * We would be calling tty_wakeup here, but unfortunately some line + * disciplines have an annoying habit of calling tty->write from + * the write wakeup callback (e.g. n_hdlc.c). + */ +void usb_serial_port_softint(struct usb_serial_port *port) +{ + schedule_work(&port->work); +} + +static void usb_serial_port_work(struct work_struct *work) { - struct usb_serial_port *port = (struct usb_serial_port *)private; + struct usb_serial_port *port = + container_of(work, struct usb_serial_port, work); struct tty_struct *tty; dbg("%s - port %d", __FUNCTION__, port->number); @@ -777,68 +548,7 @@ void usb_serial_port_softint(void *private) if (!tty) return; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { - dbg("%s - write wakeup call.", __FUNCTION__); - (tty->ldisc.write_wakeup)(tty); - } - - wake_up_interruptible(&tty->write_wait); -} - -static void destroy_serial(struct kref *kref) -{ - struct usb_serial *serial; - struct usb_serial_port *port; - int i; - - serial = to_usb_serial(kref); - - dbg ("%s - %s", __FUNCTION__, serial->type->name); - serial_shutdown (serial); - - /* return the minor range that this device had */ - return_serial(serial); - - for (i = 0; i < serial->num_ports; ++i) - serial->port[i]->open_count = 0; - - /* the ports are cleaned up and released in port_release() */ - for (i = 0; i < serial->num_ports; ++i) - if (serial->port[i]->dev.parent != NULL) { - device_unregister(&serial->port[i]->dev); - serial->port[i] = NULL; - } - - /* If this is a "fake" port, we have to clean it up here, as it will - * not get cleaned up in port_release() as it was never registered with - * the driver core */ - if (serial->num_ports < serial->num_port_pointers) { - for (i = serial->num_ports; i < serial->num_port_pointers; ++i) { - port = serial->port[i]; - if (!port) - continue; - if (port->read_urb) { - usb_unlink_urb(port->read_urb); - usb_free_urb(port->read_urb); - } - if (port->write_urb) { - usb_unlink_urb(port->write_urb); - usb_free_urb(port->write_urb); - } - if (port->interrupt_in_urb) { - usb_unlink_urb(port->interrupt_in_urb); - usb_free_urb(port->interrupt_in_urb); - } - kfree(port->bulk_in_buffer); - kfree(port->bulk_out_buffer); - kfree(port->interrupt_in_buffer); - } - } - - usb_put_dev(serial->dev); - - /* free up any memory that we allocated */ - kfree (serial); + tty_wakeup(tty); } static void port_release(struct device *dev) @@ -846,46 +556,65 @@ static void port_release(struct device *dev) struct usb_serial_port *port = to_usb_serial_port(dev); dbg ("%s - %s", __FUNCTION__, dev->bus_id); - if (port->read_urb) { - usb_unlink_urb(port->read_urb); - usb_free_urb(port->read_urb); - } - if (port->write_urb) { - usb_unlink_urb(port->write_urb); - usb_free_urb(port->write_urb); - } - if (port->interrupt_in_urb) { - usb_unlink_urb(port->interrupt_in_urb); - usb_free_urb(port->interrupt_in_urb); - } + port_free(port); +} + +static void port_free(struct usb_serial_port *port) +{ + usb_kill_urb(port->read_urb); + usb_free_urb(port->read_urb); + usb_kill_urb(port->write_urb); + usb_free_urb(port->write_urb); + usb_kill_urb(port->interrupt_in_urb); + usb_free_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_out_urb); + usb_free_urb(port->interrupt_out_urb); kfree(port->bulk_in_buffer); kfree(port->bulk_out_buffer); kfree(port->interrupt_in_buffer); + kfree(port->interrupt_out_buffer); + flush_scheduled_work(); /* port->work */ kfree(port); } static struct usb_serial * create_serial (struct usb_device *dev, struct usb_interface *interface, - struct usb_serial_device_type *type) + struct usb_serial_driver *driver) { struct usb_serial *serial; - serial = kmalloc (sizeof (*serial), GFP_KERNEL); + serial = kzalloc(sizeof(*serial), GFP_KERNEL); if (!serial) { dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); return NULL; } - memset (serial, 0, sizeof(*serial)); serial->dev = usb_get_dev(dev); - serial->type = type; + serial->type = driver; serial->interface = interface; - serial->vendor = dev->descriptor.idVendor; - serial->product = dev->descriptor.idProduct; - kref_init(&serial->kref, destroy_serial); + kref_init(&serial->kref); return serial; } +static struct usb_serial_driver *search_serial_device(struct usb_interface *iface) +{ + struct list_head *p; + const struct usb_device_id *id; + struct usb_serial_driver *t; + + /* Check if the usb id matches a known device */ + list_for_each(p, &usb_serial_driver_list) { + t = list_entry(p, struct usb_serial_driver, driver_list); + id = usb_match_id(iface, t->id_table); + if (id != NULL) { + dbg("descriptor matches"); + return t; + } + } + + return NULL; +} + int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -895,36 +624,23 @@ int usb_serial_probe(struct usb_interface *interface, struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS]; + struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS]; struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS]; struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS]; - struct usb_serial_device_type *type = NULL; - struct list_head *tmp; + struct usb_serial_driver *type = NULL; int retval; - int found; int minor; int buffer_size; int i; int num_interrupt_in = 0; + int num_interrupt_out = 0; int num_bulk_in = 0; int num_bulk_out = 0; int num_ports = 0; int max_endpoints; - const struct usb_device_id *id_pattern = NULL; - - /* loop through our list of known serial converters, and see if this - device matches. */ - found = 0; - list_for_each (tmp, &usb_serial_driver_list) { - type = list_entry(tmp, struct usb_serial_device_type, driver_list); - id_pattern = usb_match_id(interface, type->id_table); - if (id_pattern != NULL) { - dbg("descriptor matches"); - found = 1; - break; - } - } - if (!found) { - /* no match */ + + type = search_serial_device(interface); + if (!type) { dbg("none matched"); return -ENODEV; } @@ -932,18 +648,22 @@ int usb_serial_probe(struct usb_interface *interface, serial = create_serial (dev, interface, type); if (!serial) { dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); - return -ENODEV; + return -ENOMEM; } /* if this device type has a probe function, call it */ if (type->probe) { - if (!try_module_get(type->owner)) { + const struct usb_device_id *id; + + if (!try_module_get(type->driver.owner)) { dev_err(&interface->dev, "module get failed, exiting\n"); kfree (serial); return -EIO; } - retval = type->probe (serial, id_pattern); - module_put(type->owner); + + id = usb_match_id(interface, type->id_table); + retval = type->probe(serial, id); + module_put(type->driver.owner); if (retval) { dbg ("sub driver rejected device"); @@ -957,46 +677,51 @@ int usb_serial_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - - if ((endpoint->bEndpointAddress & 0x80) && - ((endpoint->bmAttributes & 3) == 0x02)) { + + if (usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - dbg("found bulk in"); + dbg("found bulk in on endpoint %d", i); bulk_in_endpoint[num_bulk_in] = endpoint; ++num_bulk_in; } - if (((endpoint->bEndpointAddress & 0x80) == 0x00) && - ((endpoint->bmAttributes & 3) == 0x02)) { + if (usb_endpoint_is_bulk_out(endpoint)) { /* we found a bulk out endpoint */ - dbg("found bulk out"); + dbg("found bulk out on endpoint %d", i); bulk_out_endpoint[num_bulk_out] = endpoint; ++num_bulk_out; } - - if ((endpoint->bEndpointAddress & 0x80) && - ((endpoint->bmAttributes & 3) == 0x03)) { + + if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ - dbg("found interrupt in"); + dbg("found interrupt in on endpoint %d", i); interrupt_in_endpoint[num_interrupt_in] = endpoint; ++num_interrupt_in; } + + if (usb_endpoint_is_int_out(endpoint)) { + /* we found an interrupt out endpoint */ + dbg("found interrupt out on endpoint %d", i); + interrupt_out_endpoint[num_interrupt_out] = endpoint; + ++num_interrupt_out; + } } #if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE) /* BEGIN HORRIBLE HACK FOR PL2303 */ /* this is needed due to the looney way its endpoints are set up */ - if (((dev->descriptor.idVendor == PL2303_VENDOR_ID) && - (dev->descriptor.idProduct == PL2303_PRODUCT_ID)) || - ((dev->descriptor.idVendor == ATEN_VENDOR_ID) && - (dev->descriptor.idProduct == ATEN_PRODUCT_ID))) { + if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) && + (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) || + ((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) && + (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) || + ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) && + (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID))) { if (interface != dev->actconfig->interface[0]) { /* check out the endpoints of the other interface*/ iface_desc = dev->actconfig->interface[0]->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if ((endpoint->bEndpointAddress & 0x80) && - ((endpoint->bmAttributes & 3) == 0x03)) { + if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dbg("found interrupt in for Prolific device on separate interface"); interrupt_in_endpoint[num_interrupt_in] = endpoint; @@ -1019,7 +744,7 @@ int usb_serial_probe(struct usb_interface *interface, #endif /* found all that we need */ - dev_info(&interface->dev, "%s converter detected\n", type->name); + dev_info(&interface->dev, "%s converter detected\n", type->description); #ifdef CONFIG_USB_SERIAL_GENERIC if (type == &usb_serial_generic_device) { @@ -1034,13 +759,13 @@ int usb_serial_probe(struct usb_interface *interface, if (!num_ports) { /* if this device type has a calc_num_ports function, call it */ if (type->calc_num_ports) { - if (!try_module_get(type->owner)) { + if (!try_module_get(type->driver.owner)) { dev_err(&interface->dev, "module get failed, exiting\n"); kfree (serial); return -EIO; } num_ports = type->calc_num_ports (serial); - module_put(type->owner); + module_put(type->driver.owner); } if (!num_ports) num_ports = type->num_ports; @@ -1057,22 +782,25 @@ int usb_serial_probe(struct usb_interface *interface, serial->num_bulk_in = num_bulk_in; serial->num_bulk_out = num_bulk_out; serial->num_interrupt_in = num_interrupt_in; + serial->num_interrupt_out = num_interrupt_out; /* create our ports, we need as many as the max endpoints */ /* we don't use num_ports here cauz some devices have more endpoint pairs than ports */ max_endpoints = max(num_bulk_in, num_bulk_out); max_endpoints = max(max_endpoints, num_interrupt_in); + max_endpoints = max(max_endpoints, num_interrupt_out); max_endpoints = max(max_endpoints, (int)serial->num_ports); serial->num_port_pointers = max_endpoints; dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints); for (i = 0; i < max_endpoints; ++i) { - port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL); + port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) goto probe_error; - memset(port, 0x00, sizeof(struct usb_serial_port)); port->number = i + serial->minor; port->serial = serial; - INIT_WORK(&port->work, usb_serial_port_softint, port); + spin_lock_init(&port->lock); + mutex_init(&port->mutex); + INIT_WORK(&port->work, usb_serial_port_work); serial->port[i] = port; } @@ -1085,7 +813,8 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = endpoint->wMaxPacketSize; + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); if (!port->bulk_in_buffer) { @@ -1108,7 +837,7 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = endpoint->wMaxPacketSize; + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL); @@ -1124,37 +853,69 @@ int usb_serial_probe(struct usb_interface *interface, port); } - for (i = 0; i < num_interrupt_in; ++i) { - endpoint = interrupt_in_endpoint[i]; - port = serial->port[i]; - port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!port->interrupt_in_urb) { - dev_err(&interface->dev, "No free urbs available\n"); - goto probe_error; + if (serial->type->read_int_callback) { + for (i = 0; i < num_interrupt_in; ++i) { + endpoint = interrupt_in_endpoint[i]; + port = serial->port[i]; + port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!port->interrupt_in_urb) { + dev_err(&interface->dev, "No free urbs available\n"); + goto probe_error; + } + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; + port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!port->interrupt_in_buffer) { + dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n"); + goto probe_error; + } + usb_fill_int_urb (port->interrupt_in_urb, dev, + usb_rcvintpipe (dev, + endpoint->bEndpointAddress), + port->interrupt_in_buffer, buffer_size, + serial->type->read_int_callback, port, + endpoint->bInterval); } - buffer_size = endpoint->wMaxPacketSize; - port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; - port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL); - if (!port->interrupt_in_buffer) { - dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n"); - goto probe_error; + } else if (num_interrupt_in) { + dbg("the device claims to support interrupt in transfers, but read_int_callback is not defined"); + } + + if (serial->type->write_int_callback) { + for (i = 0; i < num_interrupt_out; ++i) { + endpoint = interrupt_out_endpoint[i]; + port = serial->port[i]; + port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!port->interrupt_out_urb) { + dev_err(&interface->dev, "No free urbs available\n"); + goto probe_error; + } + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + port->interrupt_out_size = buffer_size; + port->interrupt_out_endpointAddress = endpoint->bEndpointAddress; + port->interrupt_out_buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!port->interrupt_out_buffer) { + dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n"); + goto probe_error; + } + usb_fill_int_urb (port->interrupt_out_urb, dev, + usb_sndintpipe (dev, + endpoint->bEndpointAddress), + port->interrupt_out_buffer, buffer_size, + serial->type->write_int_callback, port, + endpoint->bInterval); } - usb_fill_int_urb (port->interrupt_in_urb, dev, - usb_rcvintpipe (dev, - endpoint->bEndpointAddress), - port->interrupt_in_buffer, buffer_size, - serial->type->read_int_callback, port, - endpoint->bInterval); + } else if (num_interrupt_out) { + dbg("the device claims to support interrupt out transfers, but write_int_callback is not defined"); } - + /* if this device type has an attach function, call it */ if (type->attach) { - if (!try_module_get(type->owner)) { + if (!try_module_get(type->driver.owner)) { dev_err(&interface->dev, "module get failed, exiting\n"); goto probe_error; } retval = type->attach (serial); - module_put(type->owner); + module_put(type->driver.owner); if (retval < 0) goto probe_error; if (retval > 0) { @@ -1174,7 +935,10 @@ int usb_serial_probe(struct usb_interface *interface, snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number); dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id); - device_register (&port->dev); + retval = device_register(&port->dev); + if (retval) + dev_err(&port->dev, "Error registering port device, " + "continuing\n"); } usb_serial_console_init (debug, minor); @@ -1189,26 +953,30 @@ probe_error: port = serial->port[i]; if (!port) continue; - if (port->read_urb) - usb_free_urb (port->read_urb); + usb_free_urb(port->read_urb); kfree(port->bulk_in_buffer); } for (i = 0; i < num_bulk_out; ++i) { port = serial->port[i]; if (!port) continue; - if (port->write_urb) - usb_free_urb (port->write_urb); + usb_free_urb(port->write_urb); kfree(port->bulk_out_buffer); } for (i = 0; i < num_interrupt_in; ++i) { port = serial->port[i]; if (!port) continue; - if (port->interrupt_in_urb) - usb_free_urb (port->interrupt_in_urb); + usb_free_urb(port->interrupt_in_urb); kfree(port->interrupt_in_buffer); } + for (i = 0; i < num_interrupt_out; ++i) { + port = serial->port[i]; + if (!port) + continue; + usb_free_urb(port->interrupt_out_urb); + kfree(port->interrupt_out_buffer); + } /* return the minor range that this device had */ return_serial (serial); @@ -1222,21 +990,29 @@ probe_error: void usb_serial_disconnect(struct usb_interface *interface) { + int i; struct usb_serial *serial = usb_get_intfdata (interface); struct device *dev = &interface->dev; + struct usb_serial_port *port; + usb_serial_console_disconnect(serial); dbg ("%s", __FUNCTION__); usb_set_intfdata (interface, NULL); if (serial) { + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + if (port && port->tty) + tty_hangup(port->tty); + } /* let the last holder of this object * cause it to be cleaned up */ - kref_put(&serial->kref); + usb_serial_put(serial); } dev_info(dev, "device disconnected\n"); } -static struct tty_operations serial_ops = { +static const struct tty_operations serial_ops = { .open = serial_open, .close = serial_close, .write = serial_write, @@ -1257,7 +1033,7 @@ struct tty_driver *usb_serial_tty_driver; static int __init usb_serial_init(void) { int i; - int result = 0; + int result; usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); if (!usb_serial_tty_driver) @@ -1268,31 +1044,27 @@ static int __init usb_serial_init(void) serial_table[i] = NULL; } - bus_register(&usb_serial_bus_type); - - /* register the generic driver, if we should */ - result = usb_serial_generic_register(debug); - if (result < 0) { - err("%s - registering generic driver failed", __FUNCTION__); - goto exit; + result = bus_register(&usb_serial_bus_type); + if (result) { + err("%s - registering bus driver failed", __FUNCTION__); + goto exit_bus; } usb_serial_tty_driver->owner = THIS_MODULE; usb_serial_tty_driver->driver_name = "usbserial"; - usb_serial_tty_driver->devfs_name = "usb/tts/"; usb_serial_tty_driver->name = "ttyUSB"; usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; usb_serial_tty_driver->minor_start = 0; usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; - usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; usb_serial_tty_driver->init_termios = tty_std_termios; usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(usb_serial_tty_driver, &serial_ops); result = tty_register_driver(usb_serial_tty_driver); if (result) { err("%s - tty_register_driver failed", __FUNCTION__); - goto exit_generic; + goto exit_reg_driver; } /* register the USB driver */ @@ -1302,17 +1074,27 @@ static int __init usb_serial_init(void) goto exit_tty; } - info(DRIVER_DESC " " DRIVER_VERSION); + /* register the generic driver, if we should */ + result = usb_serial_generic_register(debug); + if (result < 0) { + err("%s - registering generic driver failed", __FUNCTION__); + goto exit_generic; + } + + info(DRIVER_DESC); return result; +exit_generic: + usb_deregister(&usb_serial_driver); + exit_tty: tty_unregister_driver(usb_serial_tty_driver); -exit_generic: - usb_serial_generic_deregister(); +exit_reg_driver: + bus_unregister(&usb_serial_bus_type); -exit: +exit_bus: err ("%s - returning with error %d", __FUNCTION__, result); put_tty_driver(usb_serial_tty_driver); return result; @@ -1344,7 +1126,7 @@ module_exit(usb_serial_exit); } \ } while (0) -static void fixup_generic(struct usb_serial_device_type *device) +static void fixup_generic(struct usb_serial_driver *device) { set_to_generic_if_null(device, open); set_to_generic_if_null(device, write); @@ -1356,60 +1138,46 @@ static void fixup_generic(struct usb_serial_device_type *device) set_to_generic_if_null(device, shutdown); } -int usb_serial_register(struct usb_serial_device_type *new_device) +int usb_serial_register(struct usb_serial_driver *driver) { int retval; - fixup_generic(new_device); - - /* Add this device to our list of devices */ - list_add(&new_device->driver_list, &usb_serial_driver_list); + fixup_generic(driver); - retval = usb_serial_bus_register (new_device); + if (!driver->description) + driver->description = driver->driver.name; - if (retval) - goto error; - - info("USB Serial support registered for %s", new_device->name); + /* Add this device to our list of devices */ + list_add(&driver->driver_list, &usb_serial_driver_list); - return retval; -error: - err("problem %d when registering driver %s", retval, new_device->name); - list_del(&new_device->driver_list); + retval = usb_serial_bus_register(driver); + if (retval) { + err("problem %d when registering driver %s", retval, driver->description); + list_del(&driver->driver_list); + } + else + info("USB Serial support registered for %s", driver->description); return retval; } -void usb_serial_deregister(struct usb_serial_device_type *device) +void usb_serial_deregister(struct usb_serial_driver *device) { - struct usb_serial *serial; - int i; - - info("USB Serial deregistering driver %s", device->name); - - /* clear out the serial_table if the device is attached to a port */ - for(i = 0; i < SERIAL_TTY_MINORS; ++i) { - serial = serial_table[i]; - if ((serial != NULL) && (serial->type == device)) { - usb_driver_release_interface (&usb_serial_driver, serial->interface); - usb_serial_disconnect (serial->interface); - } - } - + info("USB Serial deregistering driver %s", device->description); list_del(&device->driver_list); - usb_serial_bus_deregister (device); + usb_serial_bus_deregister(device); } /* If the usb-serial core is built into the core, the usb-serial drivers need these symbols to load properly as modules. */ -EXPORT_SYMBOL(usb_serial_register); -EXPORT_SYMBOL(usb_serial_deregister); -EXPORT_SYMBOL(usb_serial_probe); -EXPORT_SYMBOL(usb_serial_disconnect); -EXPORT_SYMBOL(usb_serial_port_softint); +EXPORT_SYMBOL_GPL(usb_serial_register); +EXPORT_SYMBOL_GPL(usb_serial_deregister); +EXPORT_SYMBOL_GPL(usb_serial_probe); +EXPORT_SYMBOL_GPL(usb_serial_disconnect); +EXPORT_SYMBOL_GPL(usb_serial_port_softint); /* Module information */