diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index cdd1dfc66..08d3e8e90 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -610,17 +610,9 @@ static int gserial_wakeup_host(struct gserial *gser) */ static int gs_open(struct tty_struct *tty, struct file *file) { - int port_num = tty->index; - struct gs_port *port; + struct gs_port *port = tty->driver_data; int status = 0; - mutex_lock(&ports[port_num].lock); - port = ports[port_num].port; - if (!port) { - status = -ENODEV; - goto out; - } - spin_lock_irq(&port->port_lock); /* allocate circular buffer on first open */ @@ -629,16 +621,17 @@ static int gs_open(struct tty_struct *tty, struct file *file) spin_unlock_irq(&port->port_lock); /* - * portmaster's mutex still protects from simultaneous open(), - * and close() can't happen, yet. + * The TTY core holds tty_lock across ->open(), so this is + * serialised against a simultaneous open() or close() of + * the same tty. */ status = kfifo_alloc(&port->port_write_buf, WRITE_BUF_SIZE, GFP_KERNEL); if (status) { pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n", - port_num, tty, file); - goto out; + port->port_num, tty, file); + return status; } spin_lock_irq(&port->port_lock); @@ -648,7 +641,6 @@ static int gs_open(struct tty_struct *tty, struct file *file) if (port->port.count++) goto exit_unlock_port; - tty->driver_data = port; port->port.tty = tty; /* if connected, start the I/O stream */ @@ -672,8 +664,6 @@ static int gs_open(struct tty_struct *tty, struct file *file) exit_unlock_port: spin_unlock_irq(&port->port_lock); -out: - mutex_unlock(&ports[port_num].lock); return status; } @@ -693,7 +683,7 @@ static int gs_close_flush_done(struct gs_port *p) static void gs_close(struct tty_struct *tty, struct file *file) { struct gs_port *port = tty->driver_data; - struct gserial *gser; + struct gserial *gser; spin_lock_irq(&port->port_lock); @@ -908,7 +898,40 @@ static int gs_get_icount(struct tty_struct *tty, return 0; } +static int gs_install(struct tty_driver *driver, struct tty_struct *tty) +{ + int port_num = tty->index; + struct gs_port *port; + int status; + + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; + if (!port) { + mutex_unlock(&ports[port_num].lock); + return -ENODEV; + } + tty_port_get(&port->port); + mutex_unlock(&ports[port_num].lock); + + status = tty_port_install(&port->port, driver, tty); + if (status) { + tty_port_put(&port->port); + return status; + } + + tty->driver_data = port; + + return 0; +} + +static void gs_cleanup(struct tty_struct *tty) +{ + tty_port_put(tty->port); +} + static const struct tty_operations gs_tty_ops = { + .install = gs_install, + .cleanup = gs_cleanup, .open = gs_open, .close = gs_close, .write = gs_write, @@ -1203,6 +1226,15 @@ static void gs_console_exit(struct gs_port *port) #endif +static void gs_destruct_port(struct tty_port *port) +{ + kfree(container_of(port, struct gs_port, port)); +} + +static const struct tty_port_operations gs_port_ops = { + .destruct = gs_destruct_port, +}; + static int gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) { @@ -1222,6 +1254,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) } tty_port_init(&port->port); + port->port.ops = &gs_port_ops; spin_lock_init(&port->port_lock); init_waitqueue_head(&port->drain_wait); init_waitqueue_head(&port->close_wait); @@ -1258,8 +1291,7 @@ static void gserial_free_port(struct gs_port *port) /* wait for old opens to finish */ wait_event(port->close_wait, gs_closed(port)); WARN_ON(port->port_usb != NULL); - tty_port_destroy(&port->port); - kfree(port); + tty_port_put(&port->port); } void gserial_free_line(unsigned char port_num) @@ -1276,8 +1308,8 @@ void gserial_free_line(unsigned char port_num) ports[port_num].port = NULL; mutex_unlock(&ports[port_num].lock); - gserial_free_port(port); tty_unregister_device(gs_tty_driver, port_num); + gserial_free_port(port); } EXPORT_SYMBOL_GPL(gserial_free_line);