Skip to content

Commit

Permalink
usb-serial: acquire references when a new tty is installed
Browse files Browse the repository at this point in the history
This patch (as1287) makes serial_install() be reponsible for acquiring
references to the usb_serial structure and the driver module when a
tty is first used.  This is more sensible than having serial_open() do
it, because a tty can be opened many times whereas it is installed
only once, when it is created.  (Not to mention that these actions are
reversed when the tty is released, not when it is closed.)  Finally,
it is at install time that the TTY core takes its own reference to the
usb_serial module, so it is only fitting that we should act the same
way in regard to the lower-level serial driver.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Alan Stern authored and Live-CD User committed Sep 19, 2009
1 parent 8bc2c1b commit cc56cd0
Showing 1 changed file with 47 additions and 64 deletions.
111 changes: 47 additions & 64 deletions drivers/usb/serial/usb-serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,100 +187,92 @@ void usb_serial_put(struct usb_serial *serial)
* Create the termios objects for this tty. We use the default
* USB serial settings but permit them to be overridden by
* serial->type->init_termios.
*
* This is the first place a new tty gets used. Hence this is where we
* acquire references to the usb_serial structure and the driver module,
* where we store a pointer to the port, and where we do an autoresume.
* All these actions are reversed in serial_do_free().
*/
static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
{
int idx = tty->index;
struct usb_serial *serial;
int retval;
struct usb_serial_port *port;
int retval = -ENODEV;

serial = usb_serial_get_by_index(idx);
if (!serial)
return retval;

port = serial->port[idx - serial->minor];
if (!port)
goto error_no_port;
if (!try_module_get(serial->type->driver.owner))
goto error_module_get;

retval = usb_autopm_get_interface(serial->interface);
if (retval)
goto error_get_interface;

/* If the termios setup has yet to be done */
if (tty->driver->termios[idx] == NULL) {
/* perform the standard setup */
retval = tty_init_termios(tty);
if (retval)
return retval;
goto error_init_termios;
/* allow the driver to update it */
serial = usb_serial_get_by_index(tty->index);
if (serial) {
if (serial->type->init_termios)
serial->type->init_termios(tty);
usb_serial_put(serial);
mutex_unlock(&serial->disc_mutex);
}
if (serial->type->init_termios)
serial->type->init_termios(tty);
}
mutex_unlock(&serial->disc_mutex);

tty->driver_data = port;

/* Final install (we use the default method) */
tty_driver_kref_get(driver);
tty->count++;
driver->ttys[idx] = tty;
return 0;
return retval;

error_init_termios:
usb_autopm_put_interface(serial->interface);
error_get_interface:
module_put(serial->type->driver.owner);
error_module_get:
error_no_port:
usb_serial_put(serial);
mutex_unlock(&serial->disc_mutex);
return retval;
}

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 first = 0;

dbg("%s", __func__);

/* get the serial object associated with this tty pointer */
serial = usb_serial_get_by_index(tty->index);
if (!serial) {
tty->driver_data = NULL;
return -ENODEV;
}

portNumber = tty->index - serial->minor;
port = serial->port[portNumber];
if (!port || serial->disconnected)
retval = -ENODEV;
/*
* Note: Our locking order requirement does not allow port->mutex
* to be acquired while serial->disc_mutex is held.
*/
mutex_unlock(&serial->disc_mutex);
if (retval)
goto bailout_serial_put;
port = tty->driver_data;
serial = port->serial;

if (mutex_lock_interruptible(&port->mutex)) {
retval = -ERESTARTSYS;
goto bailout_serial_put;
}
if (mutex_lock_interruptible(&port->mutex))
return -ERESTARTSYS;

++port->port.count;

/* set up our port structure making the tty driver
* remember our port object, and us it */
tty->driver_data = port;
tty_port_tty_set(&port->port, tty);

/* If the console is attached, the device is already open */
if (port->port.count == 1 && !port->console) {
first = 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;
}

mutex_lock(&serial->disc_mutex);
if (serial->disconnected)
retval = -ENODEV;
else
retval = usb_autopm_get_interface(serial->interface);
if (retval)
goto bailout_module_put;

/* only call the device specific open if this
* is the first time the port is opened */
retval = serial->type->open(tty, port);
if (retval)
goto bailout_interface_put;
goto bailout_module_put;
mutex_unlock(&serial->disc_mutex);
set_bit(ASYNCB_INITIALIZED, &port->port.flags);
}
Expand All @@ -297,18 +289,11 @@ static int serial_open (struct tty_struct *tty, struct file *filp)
goto bailout_mutex_unlock;
/* Undo the initial port actions */
mutex_lock(&serial->disc_mutex);
bailout_interface_put:
usb_autopm_put_interface(serial->interface);
bailout_module_put:
mutex_unlock(&serial->disc_mutex);
module_put(serial->type->driver.owner);
bailout_mutex_unlock:
port->port.count = 0;
tty->driver_data = NULL;
tty_port_tty_set(&port->port, NULL);
mutex_unlock(&port->mutex);
bailout_serial_put:
usb_serial_put(serial);
return retval;
}

Expand Down Expand Up @@ -355,17 +340,13 @@ static void serial_close(struct tty_struct *tty, struct file *filp)
{
struct usb_serial_port *port = tty->driver_data;

if (!port)
return;

dbg("%s - port %d", __func__, port->number);

if (tty_port_close_start(&port->port, tty, filp) == 0)
return;
serial_do_down(port);
tty_port_close_end(&port->port, tty);
tty_port_tty_set(&port->port, NULL);

}

/**
Expand All @@ -386,9 +367,11 @@ static void serial_do_free(struct tty_struct *tty)
/* The console is magical. Do not hang up the console hardware
* or there will be tears.
*/
if (port == NULL || port->console)
if (port->console)
return;

tty->driver_data = NULL;

serial = port->serial;
owner = serial->type->driver.owner;

Expand Down

0 comments on commit cc56cd0

Please sign in to comment.