usb_serial: API all change

USB serial likes to use port->tty back pointers for the real work it does and
to do so without any actual locking. Unfortunately when you consider hangup
events, hangup/parallel reopen or even worse hangup followed by parallel close
events the tty->port and port->tty pointers are not guaranteed to be the same
as port->tty is the active tty while tty->port is the port the tty may or
may not still be attached to.

So rework the entire API to pass the tty struct. For console cases we need
to pass both for now. This shows up multiple drivers that immediately crash
with USB console some of which have been fixed in the process.

Longer term we need a proper tty as console abstraction

Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index a58822a..7cf383a 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -243,9 +243,9 @@
 static void stop_read(struct edgeport_port *edge_port);
 static int restart_read(struct edgeport_port *edge_port);
 
-static void edge_set_termios(struct usb_serial_port *port,
-			     struct ktermios *old_termios);
-static void edge_send(struct usb_serial_port *port);
+static void edge_set_termios(struct tty_struct *tty,
+		struct usb_serial_port *port, struct ktermios *old_termios);
+static void edge_send(struct tty_struct *tty);
 
 /* sysfs attributes */
 static int edge_create_sysfs_attrs(struct usb_serial_port *port);
@@ -572,7 +572,7 @@
 								int flush)
 {
 	int baud_rate;
-	struct tty_struct *tty = port->port->tty;
+	struct tty_struct *tty = port->port->port.tty;
 	wait_queue_t wait;
 	unsigned long flags;
 
@@ -1554,7 +1554,7 @@
 	/* Save the new modem status */
 	edge_port->shadow_msr = msr & 0xf0;
 
-	tty = edge_port->port->tty;
+	tty = edge_port->port->port.tty;
 	/* handle CTS flow control */
 	if (tty && C_CRTSCTS(tty)) {
 		if (msr & EDGEPORT_MSR_CTS) {
@@ -1587,9 +1587,8 @@
 		new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK);
 
 	/* Place LSR data byte into Rx buffer */
-	if (lsr_data && edge_port->port->tty)
-		edge_tty_recv(&edge_port->port->dev, edge_port->port->tty,
-								&data, 1);
+	if (lsr_data && edge_port->port->port.tty)
+		edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1);
 
 	/* update input line counters */
 	icount = &edge_port->icount;
@@ -1750,7 +1749,7 @@
 		++data;
 	}
 
-	tty = edge_port->port->tty;
+	tty = edge_port->port->port.tty;
 	if (tty && urb->actual_length) {
 		usb_serial_debug_data(debug, &edge_port->port->dev,
 					__func__, urb->actual_length, data);
@@ -1819,10 +1818,11 @@
 	}
 
 	/* send any buffered data */
-	edge_send(port);
+	edge_send(port->port.tty);
 }
 
-static int edge_open(struct usb_serial_port *port, struct file *filp)
+static int edge_open(struct tty_struct *tty,
+			struct usb_serial_port *port, struct file *filp)
 {
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	struct edgeport_serial *edge_serial;
@@ -1838,7 +1838,8 @@
 	if (edge_port == NULL)
 		return -ENODEV;
 
-	port->tty->low_latency = low_latency;
+	if (tty)
+		tty->low_latency = low_latency;
 
 	port_number = port->number - port->serial->minor;
 	switch (port_number) {
@@ -1874,7 +1875,8 @@
 	}
 
 	/* set up the port settings */
-	edge_set_termios(port, port->tty->termios);
+	if (tty)
+		edge_set_termios(tty, port, port->port.tty->termios);
 
 	/* open up the port */
 
@@ -2000,7 +2002,8 @@
 	return status;
 }
 
-static void edge_close(struct usb_serial_port *port, struct file *filp)
+static void edge_close(struct tty_struct *tty,
+			struct usb_serial_port *port, struct file *filp)
 {
 	struct edgeport_serial *edge_serial;
 	struct edgeport_port *edge_port;
@@ -2048,8 +2051,8 @@
 	dbg("%s - exited", __func__);
 }
 
-static int edge_write(struct usb_serial_port *port, const unsigned char *data,
-								int count)
+static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
+				const unsigned char *data, int count)
 {
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	unsigned long flags;
@@ -2070,16 +2073,16 @@
 	count = edge_buf_put(edge_port->ep_out_buf, data, count);
 	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
 
-	edge_send(port);
+	edge_send(tty);
 
 	return count;
 }
 
-static void edge_send(struct usb_serial_port *port)
+static void edge_send(struct tty_struct *tty)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	int count, result;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
-	struct tty_struct *tty = port->tty;
 	unsigned long flags;
 
 
@@ -2133,8 +2136,9 @@
 		tty_wakeup(tty);
 }
 
-static int edge_write_room(struct usb_serial_port *port)
+static int edge_write_room(struct tty_struct *tty)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	int room = 0;
 	unsigned long flags;
@@ -2154,8 +2158,9 @@
 	return room;
 }
 
-static int edge_chars_in_buffer(struct usb_serial_port *port)
+static int edge_chars_in_buffer(struct tty_struct *tty)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	int chars = 0;
 	unsigned long flags;
@@ -2175,10 +2180,10 @@
 	return chars;
 }
 
-static void edge_throttle(struct usb_serial_port *port)
+static void edge_throttle(struct tty_struct *tty)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
-	struct tty_struct *tty = port->tty;
 	int status;
 
 	dbg("%s - port %d", __func__, port->number);
@@ -2189,11 +2194,10 @@
 	/* if we are implementing XON/XOFF, send the stop character */
 	if (I_IXOFF(tty)) {
 		unsigned char stop_char = STOP_CHAR(tty);
-		status = edge_write(port, &stop_char, 1);
-		if (status <= 0)
-			dev_err(&port->dev,
-				"%s - failed to write stop character, %d\n",
-							__func__, status);
+		status = edge_write(tty, port, &stop_char, 1);
+		if (status <= 0) {
+			dev_err(&port->dev, "%s - failed to write stop character, %d\n", __func__, status);
+		}
 	}
 
 	/* if we are implementing RTS/CTS, stop reads */
@@ -2203,10 +2207,10 @@
 
 }
 
-static void edge_unthrottle(struct usb_serial_port *port)
+static void edge_unthrottle(struct tty_struct *tty)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
-	struct tty_struct *tty = port->tty;
 	int status;
 
 	dbg("%s - port %d", __func__, port->number);
@@ -2217,11 +2221,10 @@
 	/* if we are implementing XON/XOFF, send the start character */
 	if (I_IXOFF(tty)) {
 		unsigned char start_char = START_CHAR(tty);
-		status = edge_write(port, &start_char, 1);
-		if (status <= 0)
-			dev_err(&port->dev,
-				"%s - failed to write start character, %d\n",
-							__func__, status);
+		status = edge_write(tty, port, &start_char, 1);
+		if (status <= 0) {
+			dev_err(&port->dev, "%s - failed to write start character, %d\n", __func__, status);
+		}
 	}
 	/* if we are implementing RTS/CTS, restart reads */
 	/* are the Edgeport will assert the RTS line */
@@ -2271,11 +2274,10 @@
 	return status;
 }
 
-static void change_port_settings(struct edgeport_port *edge_port,
-						struct ktermios *old_termios)
+static void change_port_settings(struct tty_struct *tty,
+		struct edgeport_port *edge_port, struct ktermios *old_termios)
 {
 	struct ump_uart_config *config;
-	struct tty_struct *tty;
 	int baud;
 	unsigned cflag;
 	int status;
@@ -2284,9 +2286,7 @@
 
 	dbg("%s - port %d", __func__, edge_port->port->number);
 
-	tty = edge_port->port->tty;
-
-	config = kmalloc(sizeof(*config), GFP_KERNEL);
+	config = kmalloc (sizeof (*config), GFP_KERNEL);
 	if (!config) {
 		*tty->termios = *old_termios;
 		dev_err(&edge_port->port->dev, "%s - out of memory\n",
@@ -2419,11 +2419,13 @@
 	return;
 }
 
-static void edge_set_termios(struct usb_serial_port *port,
-					struct ktermios *old_termios)
+static void edge_set_termios(struct tty_struct *tty, 
+		struct usb_serial_port *port, struct ktermios *old_termios)
 {
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
-	struct tty_struct *tty = port->tty;
+	unsigned int cflag;
+
+	cflag = tty->termios->c_cflag;
 
 	dbg("%s - clfag %08x iflag %08x", __func__,
 	    tty->termios->c_cflag, tty->termios->c_iflag);
@@ -2434,12 +2436,14 @@
 	if (edge_port == NULL)
 		return;
 	/* change the port settings to the new ones specified */
-	change_port_settings(edge_port, old_termios);
+	change_port_settings(tty, edge_port, old_termios);
+	return;
 }
 
-static int edge_tiocmset(struct usb_serial_port *port, struct file *file,
+static int edge_tiocmset(struct tty_struct *tty, struct file *file,
 					unsigned int set, unsigned int clear)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	unsigned int mcr;
 	unsigned long flags;
@@ -2469,8 +2473,9 @@
 	return 0;
 }
 
-static int edge_tiocmget(struct usb_serial_port *port, struct file *file)
+static int edge_tiocmget(struct tty_struct *tty, struct file *file)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	unsigned int result = 0;
 	unsigned int msr;
@@ -2522,9 +2527,10 @@
 	return 0;
 }
 
-static int edge_ioctl(struct usb_serial_port *port, struct file *file,
+static int edge_ioctl(struct tty_struct *tty, struct file *file,
 					unsigned int cmd, unsigned long arg)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	struct async_icount cnow;
 	struct async_icount cprev;
@@ -2569,18 +2575,19 @@
 	return -ENOIOCTLCMD;
 }
 
-static void edge_break(struct usb_serial_port *port, int on)
+static void edge_break(struct tty_struct *tty, int break_state)
 {
+	struct usb_serial_port *port = tty->driver_data;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 	int status;
 	int bv = 0;	/* Off */
 
-	dbg("%s - state = %d", __func__, on);
+	dbg("%s - state = %d", __func__, break_state);
 
 	/* chase the port close */
 	chase_port(edge_port, 0, 0);
 
-	if (on == -1)
+	if (break_state == -1)
 		bv = 1;	/* On */
 	status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv);
 	if (status)