From 03ad032bb78b2732b607ed198e951240e1d21e59 Mon Sep 17 00:00:00 2001 From: Peter Holik Date: Sat, 18 Apr 2009 07:24:17 +0000 Subject: export usbnet_get_ethernet_addr from usbnet and fixed cdc_ether.c because of using the same function get_ethernet_addr as cdc_ether.c i export usbnet_get_ethernet_addr from usbnet and fixed cdc_ether (suggested by Oliver Neukum). Signed-off-by: Peter Holik Signed-off-by: David S. Miller --- include/linux/usb/usbnet.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 36fabb95c7d..5d44059f6d6 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -183,6 +183,7 @@ extern void usbnet_tx_timeout (struct net_device *net); extern int usbnet_change_mtu (struct net_device *net, int new_mtu); extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *); +extern int usbnet_get_ethernet_addr(struct usbnet *, int); extern void usbnet_defer_kevent (struct usbnet *, int); extern void usbnet_skb_return (struct usbnet *, struct sk_buff *); extern void usbnet_unlink_rx_urbs(struct usbnet *); -- cgit v1.2.3-70-g09d2 From 335f8514f200e63d689113d29cb7253a5c282967 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 11 Jun 2009 12:26:29 +0100 Subject: tty: Bring the usb tty port structure into more use This allows us to clean stuff up, but is probably also going to cause some app breakage with buggy apps as we now implement proper POSIX behaviour for USB ports matching all the other ports. This does also mean other apps that break on USB will now work properly. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/usb/serial/belkin_sa.c | 6 +- drivers/usb/serial/ch341.c | 46 ++++++----- drivers/usb/serial/console.c | 6 +- drivers/usb/serial/cp210x.c | 6 +- drivers/usb/serial/cyberjack.c | 6 +- drivers/usb/serial/cypress_m8.c | 81 ++++++------------- drivers/usb/serial/digi_acceleport.c | 75 +++++------------- drivers/usb/serial/empeg.c | 6 +- drivers/usb/serial/ftdi_sio.c | 50 ++++++------ drivers/usb/serial/garmin_gps.c | 3 +- drivers/usb/serial/generic.c | 3 +- drivers/usb/serial/io_edgeport.c | 10 +-- drivers/usb/serial/io_ti.c | 3 +- drivers/usb/serial/ipaq.c | 6 +- drivers/usb/serial/ipw.c | 18 ++--- drivers/usb/serial/ir-usb.c | 6 +- drivers/usb/serial/iuu_phoenix.c | 25 +----- drivers/usb/serial/keyspan.c | 13 ++- drivers/usb/serial/keyspan.h | 8 +- drivers/usb/serial/keyspan_pda.c | 48 ++++++++---- drivers/usb/serial/kl5kusb105.c | 6 +- drivers/usb/serial/kobil_sct.c | 9 +-- drivers/usb/serial/mct_u232.c | 37 ++++----- drivers/usb/serial/mos7720.c | 3 +- drivers/usb/serial/mos7840.c | 48 +----------- drivers/usb/serial/navman.c | 3 +- drivers/usb/serial/omninet.c | 6 +- drivers/usb/serial/opticon.c | 3 +- drivers/usb/serial/option.c | 68 ++++++++-------- drivers/usb/serial/oti6858.c | 57 ++------------ drivers/usb/serial/pl2303.c | 79 ++++++++----------- drivers/usb/serial/sierra.c | 97 ++++++++++------------- drivers/usb/serial/spcp8x5.c | 85 ++++++++------------ drivers/usb/serial/symbolserial.c | 3 +- drivers/usb/serial/ti_usb_3410_5052.c | 6 +- drivers/usb/serial/usb-serial.c | 144 +++++++++++++++++++++++----------- drivers/usb/serial/visor.c | 6 +- drivers/usb/serial/whiteheat.c | 33 +------- include/linux/usb/serial.h | 10 ++- 39 files changed, 467 insertions(+), 661 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index b7eacad4d48..2bfd6dd85b5 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -93,8 +93,7 @@ static int belkin_sa_startup(struct usb_serial *serial); static void belkin_sa_shutdown(struct usb_serial *serial); static int belkin_sa_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void belkin_sa_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void belkin_sa_close(struct usb_serial_port *port); static void belkin_sa_read_int_callback(struct urb *urb); static void belkin_sa_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); @@ -244,8 +243,7 @@ exit: } /* belkin_sa_open */ -static void belkin_sa_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void belkin_sa_close(struct usb_serial_port *port) { dbg("%s port %d", __func__, port->number); diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index ab4cc277aa6..2830766f5b3 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -262,13 +262,33 @@ error: kfree(priv); return r; } -static void ch341_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int ch341_carrier_raised(struct usb_serial_port *port) +{ + struct ch341_private *priv = usb_get_serial_port_data(port); + if (priv->line_status & CH341_BIT_DCD) + return 1; + return 0; +} + +static void ch341_dtr_rts(struct usb_serial_port *port, int on) { struct ch341_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int c_cflag; + dbg("%s - port %d", __func__, port->number); + /* drop DTR and RTS */ + spin_lock_irqsave(&priv->lock, flags); + if (on) + priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR; + else + priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR); + spin_unlock_irqrestore(&priv->lock, flags); + ch341_set_handshake(port->serial->dev, priv->line_control); + wake_up_interruptible(&priv->delta_msr_wait); +} + +static void ch341_close(struct usb_serial_port *port) +{ dbg("%s - port %d", __func__, port->number); /* shutdown our urbs */ @@ -276,18 +296,6 @@ static void ch341_close(struct tty_struct *tty, struct usb_serial_port *port, usb_kill_urb(port->write_urb); usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); - - if (tty) { - c_cflag = tty->termios->c_cflag; - if (c_cflag & HUPCL) { - /* drop DTR and RTS */ - spin_lock_irqsave(&priv->lock, flags); - priv->line_control = 0; - spin_unlock_irqrestore(&priv->lock, flags); - ch341_set_handshake(port->serial->dev, 0); - } - } - wake_up_interruptible(&priv->delta_msr_wait); } @@ -302,7 +310,6 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, dbg("ch341_open()"); priv->baud_rate = DEFAULT_BAUD_RATE; - priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; r = ch341_configure(serial->dev, priv); if (r) @@ -322,7 +329,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, if (r) { dev_err(&port->dev, "%s - failed submitting interrupt urb," " error %d\n", __func__, r); - ch341_close(tty, port, NULL); + ch341_close(port); return -EPROTO; } @@ -343,9 +350,6 @@ static void ch341_set_termios(struct tty_struct *tty, dbg("ch341_set_termios()"); - if (!tty || !tty->termios) - return; - baud_rate = tty_get_baud_rate(tty); priv->baud_rate = baud_rate; @@ -568,6 +572,8 @@ static struct usb_serial_driver ch341_device = { .usb_driver = &ch341_driver, .num_ports = 1, .open = ch341_open, + .dtr_rts = ch341_dtr_rts, + .carrier_raised = ch341_carrier_raised, .close = ch341_close, .ioctl = ch341_ioctl, .set_termios = ch341_set_termios, diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 19e24045b13..247b61bfb7f 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -169,7 +169,9 @@ static int usb_console_setup(struct console *co, char *options) kfree(tty); } } - + /* So we know not to kill the hardware on a hangup on this + port. We have also bumped the use count by one so it won't go + idle */ port->console = 1; retval = 0; @@ -182,7 +184,7 @@ free_tty: kfree(tty); reset_open_count: port->port.count = 0; -goto out; + goto out; } static void usb_console_write(struct console *co, diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index e8d5133ce9c..d9f586dc6ec 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -36,8 +36,7 @@ static int cp2101_open(struct tty_struct *, struct usb_serial_port *, struct file *); static void cp2101_cleanup(struct usb_serial_port *); -static void cp2101_close(struct tty_struct *, struct usb_serial_port *, - struct file*); +static void cp2101_close(struct usb_serial_port *); static void cp2101_get_termios(struct tty_struct *, struct usb_serial_port *port); static void cp2101_get_termios_port(struct usb_serial_port *port, @@ -398,8 +397,7 @@ static void cp2101_cleanup(struct usb_serial_port *port) } } -static void cp2101_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static void cp2101_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index dd501bb63ed..933ba913e66 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -61,8 +61,7 @@ static int cyberjack_startup(struct usb_serial *serial); static void cyberjack_shutdown(struct usb_serial *serial); static int cyberjack_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void cyberjack_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void cyberjack_close(struct usb_serial_port *port); static int cyberjack_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int cyberjack_write_room(struct tty_struct *tty); @@ -185,8 +184,7 @@ static int cyberjack_open(struct tty_struct *tty, return result; } -static void cyberjack_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void cyberjack_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index e568710b263..669f9384853 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -174,8 +174,8 @@ static int cypress_ca42v2_startup(struct usb_serial *serial); static void cypress_shutdown(struct usb_serial *serial); static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void cypress_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void cypress_close(struct usb_serial_port *port); +static void cypress_dtr_rts(struct usb_serial_port *port, int on); static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static void cypress_send(struct usb_serial_port *port); @@ -218,6 +218,7 @@ static struct usb_serial_driver cypress_earthmate_device = { .shutdown = cypress_shutdown, .open = cypress_open, .close = cypress_close, + .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, .ioctl = cypress_ioctl, @@ -244,6 +245,7 @@ static struct usb_serial_driver cypress_hidcom_device = { .shutdown = cypress_shutdown, .open = cypress_open, .close = cypress_close, + .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, .ioctl = cypress_ioctl, @@ -270,6 +272,7 @@ static struct usb_serial_driver cypress_ca42v2_device = { .shutdown = cypress_shutdown, .open = cypress_open, .close = cypress_close, + .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, .ioctl = cypress_ioctl, @@ -656,11 +659,7 @@ static int cypress_open(struct tty_struct *tty, priv->rx_flags = 0; spin_unlock_irqrestore(&priv->lock, flags); - /* raise both lines and set termios */ - spin_lock_irqsave(&priv->lock, flags); - priv->line_control = CONTROL_DTR | CONTROL_RTS; - priv->cmd_ctrl = 1; - spin_unlock_irqrestore(&priv->lock, flags); + /* Set termios */ result = cypress_write(tty, port, NULL, 0); if (result) { @@ -694,76 +693,42 @@ static int cypress_open(struct tty_struct *tty, __func__, result); cypress_set_dead(port); } - + port->port.drain_delay = 256; return result; } /* cypress_open */ +static void cypress_dtr_rts(struct usb_serial_port *port, int on) +{ + struct cypress_private *priv = usb_get_serial_port_data(port); + /* drop dtr and rts */ + priv = usb_get_serial_port_data(port); + spin_lock_irq(&priv->lock); + if (on == 0) + priv->line_control = 0; + else + priv->line_control = CONTROL_DTR | CONTROL_RTS; + priv->cmd_ctrl = 1; + spin_unlock_irq(&priv->lock); + cypress_write(NULL, port, NULL, 0); +} -static void cypress_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void cypress_close(struct usb_serial_port *port) { struct cypress_private *priv = usb_get_serial_port_data(port); - unsigned int c_cflag; - int bps; - long timeout; - wait_queue_t wait; dbg("%s - port %d", __func__, port->number); - /* wait for data to drain from buffer */ - spin_lock_irq(&priv->lock); - timeout = CYPRESS_CLOSING_WAIT; - init_waitqueue_entry(&wait, current); - add_wait_queue(&tty->write_wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (cypress_buf_data_avail(priv->buf) == 0 - || timeout == 0 || signal_pending(current) - /* without mutex, allowed due to harmless failure mode */ - || port->serial->disconnected) - break; - spin_unlock_irq(&priv->lock); - timeout = schedule_timeout(timeout); - spin_lock_irq(&priv->lock); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&tty->write_wait, &wait); - /* clear out any remaining data in the buffer */ - cypress_buf_clear(priv->buf); - spin_unlock_irq(&priv->lock); - /* writing is potentially harmful, lock must be taken */ mutex_lock(&port->serial->disc_mutex); if (port->serial->disconnected) { mutex_unlock(&port->serial->disc_mutex); return; } - /* wait for characters to drain from device */ - if (tty) { - bps = tty_get_baud_rate(tty); - if (bps > 1200) - timeout = max((HZ * 2560) / bps, HZ / 10); - else - timeout = 2 * HZ; - schedule_timeout_interruptible(timeout); - } - + cypress_buf_clear(priv->buf); dbg("%s - stopping urbs", __func__); usb_kill_urb(port->interrupt_in_urb); usb_kill_urb(port->interrupt_out_urb); - if (tty) { - c_cflag = tty->termios->c_cflag; - if (c_cflag & HUPCL) { - /* drop dtr and rts */ - priv = usb_get_serial_port_data(port); - spin_lock_irq(&priv->lock); - priv->line_control = 0; - priv->cmd_ctrl = 1; - spin_unlock_irq(&priv->lock); - cypress_write(tty, port, NULL, 0); - } - } if (stats) dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 38ba4ea8b6b..30f5140eff0 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -422,7 +422,6 @@ struct digi_port { int dp_throttled; int dp_throttle_restart; wait_queue_head_t dp_flush_wait; - int dp_in_close; /* close in progress */ wait_queue_head_t dp_close_wait; /* wait queue for close */ struct work_struct dp_wakeup_work; struct usb_serial_port *dp_port; @@ -456,8 +455,9 @@ static int digi_write_room(struct tty_struct *tty); static int digi_chars_in_buffer(struct tty_struct *tty); static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static void digi_close(struct usb_serial_port *port); +static int digi_carrier_raised(struct usb_serial_port *port); +static void digi_dtr_rts(struct usb_serial_port *port, int on); static int digi_startup_device(struct usb_serial *serial); static int digi_startup(struct usb_serial *serial); static void digi_shutdown(struct usb_serial *serial); @@ -510,6 +510,8 @@ static struct usb_serial_driver digi_acceleport_2_device = { .num_ports = 3, .open = digi_open, .close = digi_close, + .dtr_rts = digi_dtr_rts, + .carrier_raised = digi_carrier_raised, .write = digi_write, .write_room = digi_write_room, .write_bulk_callback = digi_write_bulk_callback, @@ -1328,6 +1330,19 @@ static int digi_chars_in_buffer(struct tty_struct *tty) } +static void digi_dtr_rts(struct usb_serial_port *port, int on) +{ + /* Adjust DTR and RTS */ + digi_set_modem_signals(port, on * (TIOCM_DTR|TIOCM_RTS), 1); +} + +static int digi_carrier_raised(struct usb_serial_port *port) +{ + struct digi_port *priv = usb_get_serial_port_data(port); + if (priv->dp_modem_signals & TIOCM_CD) + return 1; + return 0; +} static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) @@ -1336,7 +1351,6 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, unsigned char buf[32]; struct digi_port *priv = usb_get_serial_port_data(port); struct ktermios not_termios; - unsigned long flags = 0; dbg("digi_open: TOP: port=%d, open_count=%d", priv->dp_port_num, port->port.count); @@ -1345,26 +1359,6 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, if (digi_startup_device(port->serial) != 0) return -ENXIO; - spin_lock_irqsave(&priv->dp_port_lock, flags); - - /* don't wait on a close in progress for non-blocking opens */ - if (priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0) { - spin_unlock_irqrestore(&priv->dp_port_lock, flags); - return -EAGAIN; - } - - /* wait for a close in progress to finish */ - while (priv->dp_in_close) { - cond_wait_interruptible_timeout_irqrestore( - &priv->dp_close_wait, DIGI_RETRY_TIMEOUT, - &priv->dp_port_lock, flags); - if (signal_pending(current)) - return -EINTR; - spin_lock_irqsave(&priv->dp_port_lock, flags); - } - - spin_unlock_irqrestore(&priv->dp_port_lock, flags); - /* read modem signals automatically whenever they change */ buf[0] = DIGI_CMD_READ_INPUT_SIGNALS; buf[1] = priv->dp_port_num; @@ -1387,16 +1381,11 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, not_termios.c_iflag = ~tty->termios->c_iflag; digi_set_termios(tty, port, ¬_termios); } - - /* set DTR and RTS */ - digi_set_modem_signals(port, TIOCM_DTR|TIOCM_RTS, 1); - return 0; } -static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static void digi_close(struct usb_serial_port *port) { DEFINE_WAIT(wait); int ret; @@ -1411,28 +1400,9 @@ static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, if (port->serial->disconnected) goto exit; - /* do cleanup only after final close on this port */ - spin_lock_irq(&priv->dp_port_lock); - priv->dp_in_close = 1; - spin_unlock_irq(&priv->dp_port_lock); - - /* tell line discipline to process only XON/XOFF */ - tty->closing = 1; - - /* wait for output to drain */ - if ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0) - tty_wait_until_sent(tty, DIGI_CLOSE_TIMEOUT); - - /* flush driver and line discipline buffers */ - tty_driver_flush_buffer(tty); - tty_ldisc_flush(tty); - if (port->serial->dev) { - /* wait for transmit idle */ - if ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0) - digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT); - /* drop DTR and RTS */ - digi_set_modem_signals(port, 0, 0); + /* FIXME: Transmit idle belongs in the wait_unti_sent path */ + digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT); /* disable input flow control */ buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; @@ -1477,11 +1447,9 @@ static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, /* shutdown any outstanding bulk writes */ usb_kill_urb(port->write_urb); } - tty->closing = 0; exit: spin_lock_irq(&priv->dp_port_lock); priv->dp_write_urb_in_use = 0; - priv->dp_in_close = 0; wake_up_interruptible(&priv->dp_close_wait); spin_unlock_irq(&priv->dp_port_lock); mutex_unlock(&port->serial->disc_mutex); @@ -1560,7 +1528,6 @@ static int digi_startup(struct usb_serial *serial) priv->dp_throttled = 0; priv->dp_throttle_restart = 0; init_waitqueue_head(&priv->dp_flush_wait); - priv->dp_in_close = 0; init_waitqueue_head(&priv->dp_close_wait); INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock); priv->dp_port = serial->port[i]; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index c709ec474a8..2b141ccb0cd 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -81,8 +81,7 @@ static int debug; /* function prototypes for an empeg-car player */ static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static void empeg_close(struct usb_serial_port *port); static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -181,8 +180,7 @@ static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, } -static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static void empeg_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index d9fcdaedf38..d9d87111f9a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -719,8 +719,8 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port); static int ftdi_sio_port_remove(struct usb_serial_port *port); static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void ftdi_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void ftdi_close(struct usb_serial_port *port); +static void ftdi_dtr_rts(struct usb_serial_port *port, int on); static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int ftdi_write_room(struct tty_struct *tty); @@ -758,6 +758,7 @@ static struct usb_serial_driver ftdi_sio_device = { .port_remove = ftdi_sio_port_remove, .open = ftdi_open, .close = ftdi_close, + .dtr_rts = ftdi_dtr_rts, .throttle = ftdi_throttle, .unthrottle = ftdi_unthrottle, .write = ftdi_write, @@ -1558,6 +1559,30 @@ static int ftdi_open(struct tty_struct *tty, } /* ftdi_open */ +static void ftdi_dtr_rts(struct usb_serial_port *port, int on) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + char buf[1]; + + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) { + /* Disable flow control */ + if (!on && usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, priv->interface, buf, 0, + WDR_TIMEOUT) < 0) { + dev_err(&port->dev, "error from flowcontrol urb\n"); + } + /* drop RTS and DTR */ + if (on) + set_mctrl(port, TIOCM_DTR | TIOCM_RTS); + else + clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } + mutex_unlock(&port->serial->disc_mutex); +} /* * usbserial:__serial_close only calls ftdi_close if the point is open @@ -1567,31 +1592,12 @@ static int ftdi_open(struct tty_struct *tty, * */ -static void ftdi_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void ftdi_close(struct usb_serial_port *port) { /* ftdi_close */ - unsigned int c_cflag = tty->termios->c_cflag; struct ftdi_private *priv = usb_get_serial_port_data(port); - char buf[1]; dbg("%s", __func__); - mutex_lock(&port->serial->disc_mutex); - if (c_cflag & HUPCL && !port->serial->disconnected) { - /* Disable flow control */ - if (usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0, priv->interface, buf, 0, - WDR_TIMEOUT) < 0) { - dev_err(&port->dev, "error from flowcontrol urb\n"); - } - - /* drop RTS and DTR */ - clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); - } /* Note change no line if hupcl is off */ - mutex_unlock(&port->serial->disc_mutex); /* cancel any scheduled reading */ cancel_delayed_work_sync(&priv->rx_work); diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 586d30ff450..ee25a3fe3b0 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -993,8 +993,7 @@ static int garmin_open(struct tty_struct *tty, } -static void garmin_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void garmin_close(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 4cec9906ccf..be82ea95672 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -184,8 +184,7 @@ int usb_serial_generic_resume(struct usb_serial *serial) } EXPORT_SYMBOL_GPL(usb_serial_generic_resume); -void usb_serial_generic_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +void usb_serial_generic_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); generic_cleanup(port); diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index fb4a73d090f..53ef5996e33 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -207,8 +207,7 @@ static void edge_bulk_out_cmd_callback(struct urb *urb); /* function prototypes for the usbserial callbacks */ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void edge_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static void edge_close(struct usb_serial_port *port); static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int edge_write_room(struct tty_struct *tty); @@ -965,7 +964,7 @@ static int edge_open(struct tty_struct *tty, if (!edge_port->txfifo.fifo) { dbg("%s - no memory", __func__); - edge_close(tty, port, filp); + edge_close(port); return -ENOMEM; } @@ -975,7 +974,7 @@ static int edge_open(struct tty_struct *tty, if (!edge_port->write_urb) { dbg("%s - no memory", __func__); - edge_close(tty, port, filp); + edge_close(port); return -ENOMEM; } @@ -1099,8 +1098,7 @@ static void block_until_tx_empty(struct edgeport_port *edge_port) * edge_close * this function is called by the tty driver when a port is closed *****************************************************************************/ -static void edge_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void edge_close(struct usb_serial_port *port) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 513b25e044c..eabf20eeb37 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2009,8 +2009,7 @@ release_es_lock: return status; } -static void edge_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void edge_close(struct usb_serial_port *port) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index cd62825a9ac..c610a99fa47 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -76,8 +76,7 @@ static int initial_wait; /* Function prototypes for an ipaq */ static int ipaq_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void ipaq_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void ipaq_close(struct usb_serial_port *port); static int ipaq_calc_num_ports(struct usb_serial *serial); static int ipaq_startup(struct usb_serial *serial); static void ipaq_shutdown(struct usb_serial *serial); @@ -714,8 +713,7 @@ error: } -static void ipaq_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void ipaq_close(struct usb_serial_port *port) { struct ipaq_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index da2a2b46644..29ad038b9c8 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -302,23 +302,17 @@ static int ipw_open(struct tty_struct *tty, return 0; } -static void ipw_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void ipw_dtr_rts(struct usb_serial_port *port, int on) { struct usb_device *dev = port->serial->dev; int result; - if (tty_hung_up_p(filp)) { - dbg("%s: tty_hung_up_p ...", __func__); - return; - } - /*--1: drop the dtr */ dbg("%s:dropping dtr", __func__); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_PIN_CLRDTR, + on ? IPW_PIN_SETDTR : IPW_PIN_CLRDTR, 0, NULL, 0, @@ -332,7 +326,7 @@ static void ipw_close(struct tty_struct *tty, result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_PIN_CLRRTS, + on ? IPW_PIN_SETRTS : IPW_PIN_CLRRTS, 0, NULL, 0, @@ -340,7 +334,12 @@ static void ipw_close(struct tty_struct *tty, if (result < 0) dev_err(&port->dev, "dropping rts failed (error = %d)\n", result); +} +static void ipw_close(struct usb_serial_port *port) +{ + struct usb_device *dev = port->serial->dev; + int result; /*--3: purge */ dbg("%s:sending purge", __func__); @@ -461,6 +460,7 @@ static struct usb_serial_driver ipw_device = { .num_ports = 1, .open = ipw_open, .close = ipw_close, + .dtr_rts = ipw_dtr_rts, .port_probe = ipw_probe, .port_remove = ipw_disconnect, .write = ipw_write, diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 4e2cda93da5..66009b6b763 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -88,8 +88,7 @@ static int xbof = -1; static int ir_startup (struct usb_serial *serial); static int ir_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filep); -static void ir_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filep); +static void ir_close(struct usb_serial_port *port); static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static void ir_write_bulk_callback (struct urb *urb); @@ -346,8 +345,7 @@ static int ir_open(struct tty_struct *tty, return result; } -static void ir_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file * filp) +static void ir_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 4473d442b2a..bb572cee6ec 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -70,7 +70,6 @@ static void read_rxcmd_callback(struct urb *urb); struct iuu_private { spinlock_t lock; /* store irq state */ wait_queue_head_t delta_msr_wait; - u8 line_control; u8 line_status; u8 termios_initialized; int tiostatus; /* store IUART SIGNAL for tiocmget call */ @@ -946,19 +945,10 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud, return status; } -static int set_control_lines(struct usb_device *dev, u8 value) -{ - return 0; -} - -static void iuu_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void iuu_close(struct usb_serial_port *port) { /* iuu_led (port,255,0,0,0); */ struct usb_serial *serial; - struct iuu_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - unsigned int c_cflag; serial = port->serial; if (!serial) @@ -968,17 +958,6 @@ static void iuu_close(struct tty_struct *tty, iuu_uart_off(port); if (serial->dev) { - if (tty) { - c_cflag = tty->termios->c_cflag; - if (c_cflag & HUPCL) { - /* drop DTR and RTS */ - priv = usb_get_serial_port_data(port); - spin_lock_irqsave(&priv->lock, flags); - priv->line_control = 0; - spin_unlock_irqrestore(&priv->lock, flags); - set_control_lines(port->serial->dev, 0); - } - } /* free writebuf */ /* shutdown our urbs */ dbg("%s - shutting down urbs", __func__); @@ -1154,7 +1133,7 @@ static int iuu_open(struct tty_struct *tty, if (result) { dev_err(&port->dev, "%s - failed submitting read urb," " error %d\n", __func__, result); - iuu_close(tty, port, NULL); + iuu_close(port); return -EPROTO; } else { dbg("%s - rxcmd OK", __func__); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 00daa8f7759..f1195a98f31 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1298,8 +1298,16 @@ static inline void stop_urb(struct urb *urb) usb_kill_urb(urb); } -static void keyspan_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void keyspan_dtr_rts(struct usb_serial_port *port, int on) +{ + struct keyspan_port_private *p_priv = usb_get_serial_port_data(port); + + p_priv->rts_state = on; + p_priv->dtr_state = on; + keyspan_send_setup(port, 0); +} + +static void keyspan_close(struct usb_serial_port *port) { int i; struct usb_serial *serial = port->serial; @@ -1336,7 +1344,6 @@ static void keyspan_close(struct tty_struct *tty, stop_urb(p_priv->out_urbs[i]); } } - tty_port_tty_set(&port->port, NULL); } /* download the firmware to a pre-renumeration device */ diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 38b4582e073..0d4569b6076 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -38,9 +38,8 @@ static int keyspan_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void keyspan_close (struct tty_struct *tty, - struct usb_serial_port *port, - struct file *filp); +static void keyspan_close (struct usb_serial_port *port); +static void keyspan_dtr_rts (struct usb_serial_port *port, int on); static int keyspan_startup (struct usb_serial *serial); static void keyspan_shutdown (struct usb_serial *serial); static int keyspan_write_room (struct tty_struct *tty); @@ -562,6 +561,7 @@ static struct usb_serial_driver keyspan_1port_device = { .num_ports = 1, .open = keyspan_open, .close = keyspan_close, + .dtr_rts = keyspan_dtr_rts, .write = keyspan_write, .write_room = keyspan_write_room, .set_termios = keyspan_set_termios, @@ -582,6 +582,7 @@ static struct usb_serial_driver keyspan_2port_device = { .num_ports = 2, .open = keyspan_open, .close = keyspan_close, + .dtr_rts = keyspan_dtr_rts, .write = keyspan_write, .write_room = keyspan_write_room, .set_termios = keyspan_set_termios, @@ -602,6 +603,7 @@ static struct usb_serial_driver keyspan_4port_device = { .num_ports = 4, .open = keyspan_open, .close = keyspan_close, + .dtr_rts = keyspan_dtr_rts, .write = keyspan_write, .write_room = keyspan_write_room, .set_termios = keyspan_set_termios, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index bf1ae247da6..ab769dbea1b 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -651,6 +651,35 @@ static int keyspan_pda_chars_in_buffer(struct tty_struct *tty) } +static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on) +{ + struct usb_serial *serial = port->serial; + + if (serial->dev) { + if (on) + keyspan_pda_set_modem_info(serial, (1<<7) | (1<< 2)); + else + keyspan_pda_set_modem_info(serial, 0); + } +} + +static int keyspan_pda_carrier_raised(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + unsigned char modembits; + + /* If we can read the modem status and the DCD is low then + carrier is not raised yet */ + if (keyspan_pda_get_modem_info(serial, &modembits) >= 0) { + if (!(modembits & (1>>6))) + return 0; + } + /* Carrier raised, or we failed (eg disconnected) so + progress accordingly */ + return 1; +} + + static int keyspan_pda_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) { @@ -682,13 +711,6 @@ static int keyspan_pda_open(struct tty_struct *tty, priv->tx_room = room; priv->tx_throttled = room ? 0 : 1; - /* the normal serial device seems to always turn on DTR and RTS here, - so do the same */ - if (tty && (tty->termios->c_cflag & CBAUD)) - keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2)); - else - keyspan_pda_set_modem_info(serial, 0); - /*Start reading from the device*/ port->interrupt_in_urb->dev = serial->dev; rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); @@ -700,19 +722,11 @@ static int keyspan_pda_open(struct tty_struct *tty, error: return rc; } - - -static void keyspan_pda_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void keyspan_pda_close(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; if (serial->dev) { - /* the normal serial device seems to always shut - off DTR and RTS now */ - if (tty->termios->c_cflag & HUPCL) - keyspan_pda_set_modem_info(serial, 0); - /* shutdown our bulk reads and writes */ usb_kill_urb(port->write_urb); usb_kill_urb(port->interrupt_in_urb); @@ -839,6 +853,8 @@ static struct usb_serial_driver keyspan_pda_device = { .usb_driver = &keyspan_pda_driver, .id_table = id_table_std, .num_ports = 1, + .dtr_rts = keyspan_pda_dtr_rts, + .carrier_raised = keyspan_pda_carrier_raised, .open = keyspan_pda_open, .close = keyspan_pda_close, .write = keyspan_pda_write, diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index fcd9082f3e7..fa817c66b3e 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -76,8 +76,7 @@ static int klsi_105_startup(struct usb_serial *serial); static void klsi_105_shutdown(struct usb_serial *serial); static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void klsi_105_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void klsi_105_close(struct usb_serial_port *port); static int klsi_105_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static void klsi_105_write_bulk_callback(struct urb *urb); @@ -447,8 +446,7 @@ exit: } /* klsi_105_open */ -static void klsi_105_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void klsi_105_close(struct usb_serial_port *port) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int rc; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index c148544953b..6b570498287 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -72,8 +72,7 @@ static int kobil_startup(struct usb_serial *serial); static void kobil_shutdown(struct usb_serial *serial); static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void kobil_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static void kobil_close(struct usb_serial_port *port); static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int kobil_write_room(struct tty_struct *tty); @@ -209,7 +208,7 @@ static void kobil_shutdown(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { while (serial->port[i]->port.count > 0) - kobil_close(NULL, serial->port[i], NULL); + kobil_close(serial->port[i]); kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); } @@ -346,11 +345,11 @@ static int kobil_open(struct tty_struct *tty, } -static void kobil_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void kobil_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); + /* FIXME: Add rts/dtr methods */ if (port->write_urb) { usb_kill_urb(port->write_urb); usb_free_urb(port->write_urb); diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 82930a7d509..873795548fc 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -95,8 +95,8 @@ static int mct_u232_startup(struct usb_serial *serial); static void mct_u232_shutdown(struct usb_serial *serial); static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void mct_u232_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void mct_u232_close(struct usb_serial_port *port); +static void mct_u232_dtr_rts(struct usb_serial_port *port, int on); static void mct_u232_read_int_callback(struct urb *urb); static void mct_u232_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); @@ -140,6 +140,7 @@ static struct usb_serial_driver mct_u232_device = { .num_ports = 1, .open = mct_u232_open, .close = mct_u232_close, + .dtr_rts = mct_u232_dtr_rts, .throttle = mct_u232_throttle, .unthrottle = mct_u232_unthrottle, .read_int_callback = mct_u232_read_int_callback, @@ -496,29 +497,29 @@ error: return retval; } /* mct_u232_open */ - -static void mct_u232_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void mct_u232_dtr_rts(struct usb_serial_port *port, int on) { - unsigned int c_cflag; unsigned int control_state; struct mct_u232_private *priv = usb_get_serial_port_data(port); - dbg("%s port %d", __func__, port->number); - if (tty) { - c_cflag = tty->termios->c_cflag; - mutex_lock(&port->serial->disc_mutex); - if (c_cflag & HUPCL && !port->serial->disconnected) { - /* drop DTR and RTS */ - spin_lock_irq(&priv->lock); + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) { + /* drop DTR and RTS */ + spin_lock_irq(&priv->lock); + if (on) + priv->control_state |= TIOCM_DTR | TIOCM_RTS; + else priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - control_state = priv->control_state; - spin_unlock_irq(&priv->lock); - mct_u232_set_modem_ctrl(port->serial, control_state); - } - mutex_unlock(&port->serial->disc_mutex); + control_state = priv->control_state; + spin_unlock_irq(&priv->lock); + mct_u232_set_modem_ctrl(port->serial, control_state); } + mutex_unlock(&port->serial->disc_mutex); +} +static void mct_u232_close(struct usb_serial_port *port) +{ + dbg("%s port %d", __func__, port->number); if (port->serial->dev) { /* shutdown our urbs */ diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 24e3b5d4b4d..9e1a013ee7f 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -533,8 +533,7 @@ static int mos7720_chars_in_buffer(struct tty_struct *tty) return chars; } -static void mos7720_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void mos7720_close(struct usb_serial_port *port) { struct usb_serial *serial; struct moschip_port *mos7720_port; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 84fb1dcd30d..10b78a37214 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1135,54 +1135,12 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty) } -/************************************************************************ - * - * mos7840_block_until_tx_empty - * - * This function will block the close until one of the following: - * 1. TX count are 0 - * 2. The mos7840 has stopped - * 3. A timeout of 3 seconds without activity has expired - * - ************************************************************************/ -static void mos7840_block_until_tx_empty(struct tty_struct *tty, - struct moschip_port *mos7840_port) -{ - int timeout = HZ / 10; - int wait = 30; - int count; - - while (1) { - - count = mos7840_chars_in_buffer(tty); - - /* Check for Buffer status */ - if (count <= 0) - return; - - /* Block the thread for a while */ - interruptible_sleep_on_timeout(&mos7840_port->wait_chase, - timeout); - - /* No activity.. count down section */ - wait--; - if (wait == 0) { - dbg("%s - TIMEOUT", __func__); - return; - } else { - /* Reset timeout value back to seconds */ - wait = 30; - } - } -} - /***************************************************************************** * mos7840_close * this function is called by the tty driver when a port is closed *****************************************************************************/ -static void mos7840_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void mos7840_close(struct usb_serial_port *port) { struct usb_serial *serial; struct moschip_port *mos7840_port; @@ -1223,10 +1181,6 @@ static void mos7840_close(struct tty_struct *tty, } } - if (serial->dev) - /* flush and block until tx is empty */ - mos7840_block_until_tx_empty(tty, mos7840_port); - /* While closing port, shutdown all bulk read, write * * and interrupt read if they exists */ if (serial->dev) { diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index bcdcbb82270..f5f3751a888 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -98,8 +98,7 @@ static int navman_open(struct tty_struct *tty, return result; } -static void navman_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void navman_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index df653971272..1104617334f 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -66,8 +66,7 @@ static int debug; /* function prototypes */ static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void omninet_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static void omninet_close(struct usb_serial_port *port); static void omninet_read_bulk_callback(struct urb *urb); static void omninet_write_bulk_callback(struct urb *urb); static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -189,8 +188,7 @@ static int omninet_open(struct tty_struct *tty, return result; } -static void omninet_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void omninet_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index b500ad10b75..c20480aa975 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -173,8 +173,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port, return result; } -static void opticon_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static void opticon_close(struct usb_serial_port *port) { struct opticon_private *priv = usb_get_serial_data(port->serial); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 7817b82889c..a16d69fadba 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -45,8 +45,9 @@ /* Function prototypes */ static int option_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void option_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static void option_close(struct usb_serial_port *port); +static void option_dtr_rts(struct usb_serial_port *port, int on); + static int option_startup(struct usb_serial *serial); static void option_shutdown(struct usb_serial *serial); static int option_write_room(struct tty_struct *tty); @@ -61,7 +62,7 @@ static void option_set_termios(struct tty_struct *tty, static int option_tiocmget(struct tty_struct *tty, struct file *file); static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port); +static int option_send_setup(struct usb_serial_port *port); static int option_suspend(struct usb_serial *serial, pm_message_t message); static int option_resume(struct usb_serial *serial); @@ -551,6 +552,7 @@ static struct usb_serial_driver option_1port_device = { .num_ports = 1, .open = option_open, .close = option_close, + .dtr_rts = option_dtr_rts, .write = option_write, .write_room = option_write_room, .chars_in_buffer = option_chars_in_buffer, @@ -630,7 +632,7 @@ static void option_set_termios(struct tty_struct *tty, dbg("%s", __func__); /* Doesn't support option setting */ tty_termios_copy_hw(tty->termios, old_termios); - option_send_setup(tty, port); + option_send_setup(port); } static int option_tiocmget(struct tty_struct *tty, struct file *file) @@ -669,7 +671,7 @@ static int option_tiocmset(struct tty_struct *tty, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return option_send_setup(tty, port); + return option_send_setup(port); } /* Write */ @@ -897,10 +899,6 @@ static int option_open(struct tty_struct *tty, dbg("%s", __func__); - /* Set some sane defaults */ - portdata->rts_state = 1; - portdata->dtr_state = 1; - /* Reset low level data toggle and start reading from endpoints */ for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; @@ -936,37 +934,43 @@ static int option_open(struct tty_struct *tty, usb_pipeout(urb->pipe), 0); */ } - option_send_setup(tty, port); + option_send_setup(port); return 0; } -static void option_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void option_dtr_rts(struct usb_serial_port *port, int on) { - int i; struct usb_serial *serial = port->serial; struct option_port_private *portdata; dbg("%s", __func__); portdata = usb_get_serial_port_data(port); + mutex_lock(&serial->disc_mutex); + portdata->rts_state = on; + portdata->dtr_state = on; + if (serial->dev) + option_send_setup(port); + mutex_unlock(&serial->disc_mutex); +} - portdata->rts_state = 0; - portdata->dtr_state = 0; - if (serial->dev) { - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) - option_send_setup(tty, port); - mutex_unlock(&serial->disc_mutex); +static void option_close(struct usb_serial_port *port) +{ + int i; + struct usb_serial *serial = port->serial; + struct option_port_private *portdata; + + dbg("%s", __func__); + portdata = usb_get_serial_port_data(port); + if (serial->dev) { /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) usb_kill_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); } - tty_port_tty_set(&port->port, NULL); } /* Helper functions used by option_setup_urbs */ @@ -1032,28 +1036,24 @@ static void option_setup_urbs(struct usb_serial *serial) * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN * CDC. */ -static int option_send_setup(struct tty_struct *tty, - struct usb_serial_port *port) +static int option_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct option_port_private *portdata; int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber; + int val = 0; dbg("%s", __func__); portdata = usb_get_serial_port_data(port); - if (tty) { - int val = 0; - if (portdata->dtr_state) - val |= 0x01; - if (portdata->rts_state) - val |= 0x02; + if (portdata->dtr_state) + val |= 0x01; + if (portdata->rts_state) + val |= 0x02; - return usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT); - } - return 0; + return usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT); } static int option_startup(struct usb_serial *serial) diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index ba551f00f16..7de54781fe6 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -143,8 +143,7 @@ struct oti6858_control_pkt { /* function prototypes */ static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void oti6858_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void oti6858_close(struct usb_serial_port *port); static void oti6858_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int oti6858_ioctl(struct tty_struct *tty, struct file *file, @@ -622,67 +621,30 @@ static int oti6858_open(struct tty_struct *tty, if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); - oti6858_close(tty, port, NULL); + oti6858_close(port); return -EPROTO; } /* setup termios */ if (tty) oti6858_set_termios(tty, port, &tmp_termios); - + port->port.drain_delay = 256; /* FIXME: check the FIFO length */ return 0; } -static void oti6858_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void oti6858_close(struct usb_serial_port *port) { struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; - long timeout; - wait_queue_t wait; dbg("%s(port = %d)", __func__, port->number); - /* wait for data to drain from the buffer */ spin_lock_irqsave(&priv->lock, flags); - timeout = 30 * HZ; /* PL2303_CLOSING_WAIT */ - init_waitqueue_entry(&wait, current); - add_wait_queue(&tty->write_wait, &wait); - dbg("%s(): entering wait loop", __func__); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (oti6858_buf_data_avail(priv->buf) == 0 - || timeout == 0 || signal_pending(current) - || port->serial->disconnected) - break; - spin_unlock_irqrestore(&priv->lock, flags); - timeout = schedule_timeout(timeout); - spin_lock_irqsave(&priv->lock, flags); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&tty->write_wait, &wait); - dbg("%s(): after wait loop", __func__); - /* clear out any remaining data in the buffer */ oti6858_buf_clear(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); - /* wait for characters to drain from the device */ - /* (this is long enough for the entire 256 byte */ - /* pl2303 hardware buffer to drain with no flow */ - /* control for data rates of 1200 bps or more, */ - /* for lower rates we should really know how much */ - /* data is in the buffer to compute a delay */ - /* that is not unnecessarily long) */ - /* FIXME - bps = tty_get_baud_rate(tty); - if (bps > 1200) - timeout = max((HZ*2560)/bps,HZ/10); - else - */ - timeout = 2*HZ; - schedule_timeout_interruptible(timeout); - dbg("%s(): after schedule_timeout_interruptible()", __func__); + dbg("%s(): after buf_clear()", __func__); /* cancel scheduled setup */ cancel_delayed_work(&priv->delayed_setup_work); @@ -694,15 +656,6 @@ static void oti6858_close(struct tty_struct *tty, usb_kill_urb(port->write_urb); usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); - - /* - if (tty && (tty->termios->c_cflag) & HUPCL) { - // drop DTR and RTS - spin_lock_irqsave(&priv->lock, flags); - priv->pending_setup.control &= ~CONTROL_MASK; - spin_unlock_irqrestore(&priv->lock, flags); - } - */ } static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 751a533a434..e02dc3d643c 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -652,69 +652,41 @@ static void pl2303_set_termios(struct tty_struct *tty, kfree(buf); } -static void pl2303_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void pl2303_dtr_rts(struct usb_serial_port *port, int on) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + u8 control; + + spin_lock_irqsave(&priv->lock, flags); + /* Change DTR and RTS */ + if (on) + priv->line_control |= (CONTROL_DTR | CONTROL_RTS); + else + priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); + control = priv->line_control; + spin_unlock_irqrestore(&priv->lock, flags); + set_control_lines(port->serial->dev, control); +} + +static void pl2303_close(struct usb_serial_port *port) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int c_cflag; - int bps; - long timeout; - wait_queue_t wait; dbg("%s - port %d", __func__, port->number); - /* wait for data to drain from the buffer */ spin_lock_irqsave(&priv->lock, flags); - timeout = PL2303_CLOSING_WAIT; - init_waitqueue_entry(&wait, current); - add_wait_queue(&tty->write_wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (pl2303_buf_data_avail(priv->buf) == 0 || - timeout == 0 || signal_pending(current) || - port->serial->disconnected) - break; - spin_unlock_irqrestore(&priv->lock, flags); - timeout = schedule_timeout(timeout); - spin_lock_irqsave(&priv->lock, flags); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ pl2303_buf_clear(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); - /* wait for characters to drain from the device */ - /* (this is long enough for the entire 256 byte */ - /* pl2303 hardware buffer to drain with no flow */ - /* control for data rates of 1200 bps or more, */ - /* for lower rates we should really know how much */ - /* data is in the buffer to compute a delay */ - /* that is not unnecessarily long) */ - bps = tty_get_baud_rate(tty); - if (bps > 1200) - timeout = max((HZ*2560)/bps, HZ/10); - else - timeout = 2*HZ; - schedule_timeout_interruptible(timeout); - /* shutdown our urbs */ dbg("%s - shutting down urbs", __func__); usb_kill_urb(port->write_urb); usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); - if (tty) { - c_cflag = tty->termios->c_cflag; - if (c_cflag & HUPCL) { - /* drop DTR and RTS */ - spin_lock_irqsave(&priv->lock, flags); - priv->line_control = 0; - spin_unlock_irqrestore(&priv->lock, flags); - set_control_lines(port->serial->dev, 0); - } - } } static int pl2303_open(struct tty_struct *tty, @@ -748,7 +720,7 @@ static int pl2303_open(struct tty_struct *tty, if (result) { dev_err(&port->dev, "%s - failed submitting read urb," " error %d\n", __func__, result); - pl2303_close(tty, port, NULL); + pl2303_close(port); return -EPROTO; } @@ -758,9 +730,10 @@ static int pl2303_open(struct tty_struct *tty, if (result) { dev_err(&port->dev, "%s - failed submitting interrupt urb," " error %d\n", __func__, result); - pl2303_close(tty, port, NULL); + pl2303_close(port); return -EPROTO; } + port->port.drain_delay = 256; return 0; } @@ -821,6 +794,14 @@ static int pl2303_tiocmget(struct tty_struct *tty, struct file *file) return result; } +static int pl2303_carrier_raised(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + if (priv->line_status & UART_DCD) + return 1; + return 0; +} + static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) { struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -1125,6 +1106,8 @@ static struct usb_serial_driver pl2303_device = { .num_ports = 1, .open = pl2303_open, .close = pl2303_close, + .dtr_rts = pl2303_dtr_rts, + .carrier_raised = pl2303_carrier_raised, .write = pl2303_write, .ioctl = pl2303_ioctl, .break_ctl = pl2303_break_ctl, diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 913225c6161..1319b8968d8 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -240,57 +240,39 @@ struct sierra_port_private { int ri_state; }; -static int sierra_send_setup(struct tty_struct *tty, - struct usb_serial_port *port) +static int sierra_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; __u16 interface = 0; + int val = 0; dev_dbg(&port->dev, "%s", __func__); portdata = usb_get_serial_port_data(port); - if (tty) { - int val = 0; - if (portdata->dtr_state) - val |= 0x01; - if (portdata->rts_state) - val |= 0x02; - - /* If composite device then properly report interface */ - if (serial->num_ports == 1) { - interface = sierra_calc_interface(serial); - - /* Control message is sent only to interfaces with - * interrupt_in endpoints - */ - if (port->interrupt_in_urb) { - /* send control message */ - return usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x22, 0x21, val, interface, - NULL, 0, USB_CTRL_SET_TIMEOUT); - } - } - - /* Otherwise the need to do non-composite mapping */ - else { - if (port->bulk_out_endpointAddress == 2) - interface = 0; - else if (port->bulk_out_endpointAddress == 4) - interface = 1; - else if (port->bulk_out_endpointAddress == 5) - interface = 2; - - return usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x22, 0x21, val, interface, - NULL, 0, USB_CTRL_SET_TIMEOUT); - - } + if (portdata->dtr_state) + val |= 0x01; + if (portdata->rts_state) + val |= 0x02; + + /* If composite device then properly report interface */ + if (serial->num_ports == 1) + interface = sierra_calc_interface(serial); + + /* Otherwise the need to do non-composite mapping */ + else { + if (port->bulk_out_endpointAddress == 2) + interface = 0; + else if (port->bulk_out_endpointAddress == 4) + interface = 1; + else if (port->bulk_out_endpointAddress == 5) + interface = 2; } - + return usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 0x22, 0x21, val, interface, + NULL, 0, USB_CTRL_SET_TIMEOUT); return 0; } @@ -299,7 +281,7 @@ static void sierra_set_termios(struct tty_struct *tty, { dev_dbg(&port->dev, "%s", __func__); tty_termios_copy_hw(tty->termios, old_termios); - sierra_send_setup(tty, port); + sierra_send_setup(port); } static int sierra_tiocmget(struct tty_struct *tty, struct file *file) @@ -338,7 +320,7 @@ static int sierra_tiocmset(struct tty_struct *tty, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return sierra_send_setup(tty, port); + return sierra_send_setup(port); } static void sierra_outdat_callback(struct urb *urb) @@ -598,7 +580,7 @@ static int sierra_open(struct tty_struct *tty, } } - sierra_send_setup(tty, port); + sierra_send_setup(port); /* start up the interrupt endpoint if we have one */ if (port->interrupt_in_urb) { @@ -610,32 +592,38 @@ static int sierra_open(struct tty_struct *tty, return 0; } -static void sierra_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void sierra_dtr_rts(struct usb_serial_port *port, int on) { - int i; struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; - dev_dbg(&port->dev, "%s", __func__); portdata = usb_get_serial_port_data(port); - - portdata->rts_state = 0; - portdata->dtr_state = 0; + portdata->rts_state = on; + portdata->dtr_state = on; if (serial->dev) { mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - sierra_send_setup(tty, port); + sierra_send_setup(port); mutex_unlock(&serial->disc_mutex); + } +} + +static void sierra_close(struct usb_serial_port *port) +{ + int i; + struct usb_serial *serial = port->serial; + struct sierra_port_private *portdata; + dev_dbg(&port->dev, "%s", __func__); + portdata = usb_get_serial_port_data(port); + + if (serial->dev) { /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) usb_kill_urb(portdata->in_urbs[i]); } - usb_kill_urb(port->interrupt_in_urb); - tty_port_tty_set(&port->port, NULL); } static int sierra_startup(struct usb_serial *serial) @@ -737,6 +725,7 @@ static struct usb_serial_driver sierra_device = { .probe = sierra_probe, .open = sierra_open, .close = sierra_close, + .dtr_rts = sierra_dtr_rts, .write = sierra_write, .write_room = sierra_write_room, .set_termios = sierra_set_termios, diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 5e7528cc81a..8f7ed8f1399 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -446,66 +446,47 @@ static void spcp8x5_set_workMode(struct usb_device *dev, u16 value, "RTSCTS usb_control_msg(enable flowctrl) = %d\n", ret); } +static int spcp8x5_carrier_raised(struct usb_serial_port *port) +{ + struct spcp8x5_private *priv = usb_get_serial_port_data(port); + if (priv->line_status & MSR_STATUS_LINE_DCD) + return 1; + return 0; +} + +static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) +{ + struct spcp8x5_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + u8 control; + + spin_lock_irqsave(&priv->lock, flags); + if (on) + priv->line_control = MCR_CONTROL_LINE_DTR + | MCR_CONTROL_LINE_RTS; + else + priv->line_control &= ~ (MCR_CONTROL_LINE_DTR + | MCR_CONTROL_LINE_RTS); + control = priv->line_control; + spin_unlock_irqrestore(&priv->lock, flags); + spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); +} + /* close the serial port. We should wait for data sending to device 1st and * then kill all urb. */ -static void spcp8x5_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void spcp8x5_close(struct usb_serial_port *port) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int c_cflag; - int bps; - long timeout; - wait_queue_t wait; int result; dbg("%s - port %d", __func__, port->number); - /* wait for data to drain from the buffer */ spin_lock_irqsave(&priv->lock, flags); - timeout = SPCP8x5_CLOSING_WAIT; - init_waitqueue_entry(&wait, current); - add_wait_queue(&tty->write_wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (ringbuf_avail_data(priv->buf) == 0 || - timeout == 0 || signal_pending(current)) - break; - spin_unlock_irqrestore(&priv->lock, flags); - timeout = schedule_timeout(timeout); - spin_lock_irqsave(&priv->lock, flags); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&tty->write_wait, &wait); - /* clear out any remaining data in the buffer */ clear_ringbuf(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); - /* wait for characters to drain from the device (this is long enough - * for the entire all byte spcp8x5 hardware buffer to drain with no - * flow control for data rates of 1200 bps or more, for lower rates we - * should really know how much data is in the buffer to compute a delay - * that is not unnecessarily long) */ - bps = tty_get_baud_rate(tty); - if (bps > 1200) - timeout = max((HZ*2560) / bps, HZ/10); - else - timeout = 2*HZ; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(timeout); - - /* clear control lines */ - if (tty) { - c_cflag = tty->termios->c_cflag; - if (c_cflag & HUPCL) { - spin_lock_irqsave(&priv->lock, flags); - priv->line_control = 0; - spin_unlock_irqrestore(&priv->lock, flags); - spcp8x5_set_ctrlLine(port->serial->dev, 0 , priv->type); - } - } - /* kill urb */ if (port->write_urb != NULL) { result = usb_unlink_urb(port->write_urb); @@ -665,13 +646,6 @@ static int spcp8x5_open(struct tty_struct *tty, if (ret) return ret; - spin_lock_irqsave(&priv->lock, flags); - if (tty && (tty->termios->c_cflag & CBAUD)) - priv->line_control = MCR_DTR | MCR_RTS; - else - priv->line_control = 0; - spin_unlock_irqrestore(&priv->lock, flags); - spcp8x5_set_ctrlLine(serial->dev, priv->line_control , priv->type); /* Setup termios */ @@ -691,9 +665,10 @@ static int spcp8x5_open(struct tty_struct *tty, port->read_urb->dev = serial->dev; ret = usb_submit_urb(port->read_urb, GFP_KERNEL); if (ret) { - spcp8x5_close(tty, port, NULL); + spcp8x5_close(port); return -EPROTO; } + port->port.drain_delay = 256; return 0; } @@ -1033,6 +1008,8 @@ static struct usb_serial_driver spcp8x5_device = { .num_ports = 1, .open = spcp8x5_open, .close = spcp8x5_close, + .dtr_rts = spcp8x5_dtr_rts, + .carrier_raised = spcp8x5_carrier_raised, .write = spcp8x5_write, .set_termios = spcp8x5_set_termios, .ioctl = spcp8x5_ioctl, diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 69879e43794..8b07ebc6bae 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -152,8 +152,7 @@ static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port, return result; } -static void symbol_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static void symbol_close(struct usb_serial_port *port) { struct symbol_private *priv = usb_get_serial_data(port->serial); diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 0a64bac306e..42cb04c403b 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -100,8 +100,7 @@ static int ti_startup(struct usb_serial *serial); static void ti_shutdown(struct usb_serial *serial); static int ti_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *file); -static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *file); +static void ti_close(struct usb_serial_port *port); static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count); static int ti_write_room(struct tty_struct *tty); @@ -647,8 +646,7 @@ release_lock: } -static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *file) +static void ti_close(struct usb_serial_port *port) { struct ti_device *tdev; struct ti_port *tport; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index f331e2bde88..1967a7edc10 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -238,9 +238,11 @@ static int serial_open (struct tty_struct *tty, struct file *filp) goto bailout_interface_put; mutex_unlock(&serial->disc_mutex); } - mutex_unlock(&port->mutex); - return 0; + /* Now do the correct tty layer semantics */ + retval = tty_port_block_til_ready(&port->port, tty, filp); + if (retval == 0) + return 0; bailout_interface_put: usb_autopm_put_interface(serial->interface); @@ -259,64 +261,89 @@ bailout_serial_put: return retval; } -static void serial_close(struct tty_struct *tty, struct file *filp) +/** + * serial_do_down - shut down hardware + * @port: port to shut down + * + * Shut down a USB port unless it is the console. We never shut down the + * console hardware as it will always be in use. + * + * Don't free any resources at this point + */ +static void serial_do_down(struct usb_serial_port *port) { - struct usb_serial_port *port = tty->driver_data; + struct usb_serial_driver *drv = port->serial->type; struct usb_serial *serial; struct module *owner; - int count; - if (!port) + /* The console is magical, do not hang up the console hardware + or there will be tears */ + if (port->console) return; - dbg("%s - port %d", __func__, port->number); - mutex_lock(&port->mutex); serial = port->serial; owner = serial->type->driver.owner; - if (port->port.count == 0) { - mutex_unlock(&port->mutex); - return; - } - - if (port->port.count == 1) - /* only call the device specific close if this - * port is being closed by the last owner. Ensure we do - * this before we drop the port count. The call is protected - * by the port mutex - */ - serial->type->close(tty, port, filp); - - if (port->port.count == (port->console ? 2 : 1)) { - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty) { - /* We must do this before we drop the port count to - zero. */ - if (tty->driver_data) - tty->driver_data = NULL; - tty_port_tty_set(&port->port, NULL); - tty_kref_put(tty); - } - } + if (drv->close) + drv->close(port); - --port->port.count; - count = port->port.count; mutex_unlock(&port->mutex); - put_device(&port->dev); +} + +/** + * serial_do_free - free resources post close/hangup + * @port: port to free up + * + * Do the resource freeing and refcount dropping for the port. We must + * be careful about ordering and we must avoid freeing up the console. + */ +static void serial_do_free(struct usb_serial_port *port) +{ + struct usb_serial *serial; + struct module *owner; + + /* The console is magical, do not hang up the console hardware + or there will be tears */ + if (port->console) + return; + + serial = port->serial; + owner = serial->type->driver.owner; + put_device(&port->dev); /* Mustn't dereference port any more */ - if (count == 0) { - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) - usb_autopm_put_interface(serial->interface); - mutex_unlock(&serial->disc_mutex); - } + mutex_lock(&serial->disc_mutex); + if (!serial->disconnected) + usb_autopm_put_interface(serial->interface); + mutex_unlock(&serial->disc_mutex); usb_serial_put(serial); - /* Mustn't dereference serial any more */ - if (count == 0) - module_put(owner); + module_put(owner); +} + +static void serial_close(struct tty_struct *tty, struct file *filp) +{ + struct usb_serial_port *port = tty->driver_data; + + 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); + serial_do_free(port); +} + +static void serial_hangup(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + serial_do_down(port); + tty_port_hangup(&port->port); + serial_do_free(port); } static int serial_write(struct tty_struct *tty, const unsigned char *buf, @@ -648,6 +675,29 @@ static struct usb_serial_driver *search_serial_device( return NULL; } +static int serial_carrier_raised(struct tty_port *port) +{ + struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); + struct usb_serial_driver *drv = p->serial->type; + if (drv->carrier_raised) + return drv->carrier_raised(p); + /* No carrier control - don't block */ + return 1; +} + +static void serial_dtr_rts(struct tty_port *port, int on) +{ + struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); + struct usb_serial_driver *drv = p->serial->type; + if (drv->dtr_rts) + drv->dtr_rts(p, on); +} + +static const struct tty_port_operations serial_port_ops = { + .carrier_raised = serial_carrier_raised, + .dtr_rts = serial_dtr_rts, +}; + int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -841,6 +891,7 @@ int usb_serial_probe(struct usb_interface *interface, if (!port) goto probe_error; tty_port_init(&port->port); + port->port.ops = &serial_port_ops; port->serial = serial; spin_lock_init(&port->lock); mutex_init(&port->mutex); @@ -1071,6 +1122,9 @@ void usb_serial_disconnect(struct usb_interface *interface) if (port) { struct tty_struct *tty = tty_port_tty_get(&port->port); if (tty) { + /* The hangup will occur asynchronously but + the object refcounts will sort out all the + cleanup */ tty_hangup(tty); tty_kref_put(tty); } @@ -1135,6 +1189,7 @@ static const struct tty_operations serial_ops = { .open = serial_open, .close = serial_close, .write = serial_write, + .hangup = serial_hangup, .write_room = serial_write_room, .ioctl = serial_ioctl, .set_termios = serial_set_termios, @@ -1147,6 +1202,7 @@ static const struct tty_operations serial_ops = { .proc_fops = &serial_proc_fops, }; + struct tty_driver *usb_serial_tty_driver; static int __init usb_serial_init(void) diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 5ac414bda71..b15f1c0e1d4 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -38,8 +38,7 @@ /* function prototypes for a handspring visor */ static int visor_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void visor_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static void visor_close(struct usb_serial_port *port); static int visor_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int visor_write_room(struct tty_struct *tty); @@ -324,8 +323,7 @@ exit: } -static void visor_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void visor_close(struct usb_serial_port *port) { struct visor_private *priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 5335d3211c0..7c7295d09f3 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -147,8 +147,7 @@ static int whiteheat_attach(struct usb_serial *serial); static void whiteheat_shutdown(struct usb_serial *serial); static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); -static void whiteheat_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static void whiteheat_close(struct usb_serial_port *port); static int whiteheat_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -712,8 +711,7 @@ exit: } -static void whiteheat_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void whiteheat_close(struct usb_serial_port *port) { struct whiteheat_private *info = usb_get_serial_port_data(port); struct whiteheat_urb_wrap *wrap; @@ -723,31 +721,7 @@ static void whiteheat_close(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); - mutex_lock(&port->serial->disc_mutex); - /* filp is NULL when called from usb_serial_disconnect */ - if ((filp && (tty_hung_up_p(filp))) || port->serial->disconnected) { - mutex_unlock(&port->serial->disc_mutex); - return; - } - mutex_unlock(&port->serial->disc_mutex); - - tty->closing = 1; - -/* - * Not currently in use; tty_wait_until_sent() calls - * serial_chars_in_buffer() which deadlocks on the second semaphore - * acquisition. This should be fixed at some point. Greg's been - * notified. - if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) { - tty_wait_until_sent(tty, CLOSING_DELAY); - } -*/ - - tty_driver_flush_buffer(tty); - tty_ldisc_flush(tty); - firm_report_tx_done(port); - firm_close(port); /* shutdown our bulk reads and writes */ @@ -775,10 +749,7 @@ static void whiteheat_close(struct tty_struct *tty, } spin_unlock_irq(&info->lock); mutex_unlock(&info->deathwarrant); - stop_command_port(port->serial); - - tty->closing = 0; } diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 625e9e4639c..8cdfed738fe 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -224,8 +224,7 @@ struct usb_serial_driver { /* Called by console with tty = NULL and by tty */ int (*open)(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); - void (*close)(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); + void (*close)(struct usb_serial_port *port); int (*write)(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); /* Called only by the tty layer */ @@ -241,6 +240,10 @@ struct usb_serial_driver { int (*tiocmget)(struct tty_struct *tty, struct file *file); int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); + /* Called by the tty layer for port level work. There may or may not + be an attached tty at this point */ + void (*dtr_rts)(struct usb_serial_port *port, int on); + int (*carrier_raised)(struct usb_serial_port *port); /* USB events */ void (*read_int_callback)(struct urb *urb); void (*write_int_callback)(struct urb *urb); @@ -283,8 +286,7 @@ extern int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); extern int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -extern void usb_serial_generic_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +extern void usb_serial_generic_close(struct usb_serial_port *port); extern int usb_serial_generic_resume(struct usb_serial *serial); extern int usb_serial_generic_write_room(struct tty_struct *tty); extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty); -- cgit v1.2.3-70-g09d2 From cc835e321a9f3fa5e083436872e198095f4805b9 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 31 Mar 2009 12:28:31 -0700 Subject: USB: nop-usb-xceiv: behave when linked as a module The NOP OTG transceiver driver needs to be usable from modules. Make sure its symbols are always accessible at both compile and link time, and make sure the device instance is allocated from the heap so that device lifetime rules are obeyed. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/nop-usb-xceiv.c | 25 ++++++++++--------------- include/linux/usb/otg.h | 4 ++-- 2 files changed, 12 insertions(+), 17 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index c567168f89a..9ed5ea56867 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -22,8 +22,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Current status: - * this is to add "nop" transceiver for all those phy which is - * autonomous such as isp1504 etc. + * This provides a "nop" transceiver for PHYs which are + * autonomous such as isp1504, isp1707, etc. */ #include @@ -36,30 +36,25 @@ struct nop_usb_xceiv { struct device *dev; }; -static u64 nop_xceiv_dmamask = DMA_BIT_MASK(32); - -static struct platform_device nop_xceiv_device = { - .name = "nop_usb_xceiv", - .id = -1, - .dev = { - .dma_mask = &nop_xceiv_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = NULL, - }, -}; +static struct platform_device *pd; void usb_nop_xceiv_register(void) { - if (platform_device_register(&nop_xceiv_device) < 0) { + if (pd) + return; + pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); + if (!pd) { printk(KERN_ERR "Unable to register usb nop transceiver\n"); return; } } +EXPORT_SYMBOL(usb_nop_xceiv_register); void usb_nop_xceiv_unregister(void) { - platform_device_unregister(&nop_xceiv_device); + platform_device_unregister(pd); } +EXPORT_SYMBOL(usb_nop_xceiv_unregister); static inline struct nop_usb_xceiv *xceiv_to_nop(struct otg_transceiver *x) { diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 1aaa826396a..2443c0e7a80 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -80,10 +80,10 @@ struct otg_transceiver { /* for board-specific init logic */ extern int otg_set_transceiver(struct otg_transceiver *); -#ifdef CONFIG_NOP_USB_XCEIV + +/* sometimes transceivers are accessed only through e.g. ULPI */ extern void usb_nop_xceiv_register(void); extern void usb_nop_xceiv_unregister(void); -#endif /* for usb host and peripheral controller drivers */ -- cgit v1.2.3-70-g09d2 From cac85a8b4e8e7c51bc0ce2980bba0e35cfec5c2e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 29 Apr 2009 21:04:19 -0700 Subject: USB: composite.h: mark private struct members as private: Mark internal struct members as /* private: */ so that kernel-doc won't produce warnings about missing descriptions for them. Signed-off-by: Randy Dunlap Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/composite.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index acd7b0f06c8..4f6bb3d2160 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -124,6 +124,7 @@ struct usb_function { void (*suspend)(struct usb_function *); void (*resume)(struct usb_function *); + /* private: */ /* internals */ struct list_head list; }; @@ -219,6 +220,7 @@ struct usb_configuration { struct usb_composite_dev *cdev; + /* private: */ /* internals */ struct list_head list; struct list_head functions; @@ -321,6 +323,7 @@ struct usb_composite_dev { struct usb_configuration *config; + /* private: */ /* internals */ struct usb_device_descriptor desc; struct list_head configs; -- cgit v1.2.3-70-g09d2 From 715b1dc01fe44537e8fce9566e4bb48d6821d84b Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Mon, 11 May 2009 15:24:07 -0500 Subject: USB: usb_debug, usb_generic_serial: implement multi urb write The usb_debug driver, when used as the console, will always fail to insert the carriage return and new line sequence as well as randomly drop console output. This is a result of only having the single write_urb and that the tty layer will have a lock that prevents the processing of the back to back urb requests. The solution is to allow more than one urb to be outstanding and have a slightly deeper transmit queue. The idea and some code is borrowed from the ftdi_sio usb driver. The generic usb serial driver was modified so as to allow the classic method of 1 write urb, or a multi write urb scheme with N allowed outstanding urbs where N is controlled by max_in_flight_urbs. When max_in_flight_urbs in a "struct usb_serial_driver" is non zero the multi write urb scheme will be used. The size of 4000 was selected for the usb_debug driver so that the driver lowers possibility of losing the queued console messages during the kernel startup. Signed-off-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 121 ++++++++++++++++++++++++++++++++++++++--- drivers/usb/serial/usb_debug.c | 2 + include/linux/usb/serial.h | 4 ++ 3 files changed, 120 insertions(+), 7 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index be82ea95672..c919686521f 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -190,6 +190,88 @@ void usb_serial_generic_close(struct usb_serial_port *port) generic_cleanup(port); } +static int usb_serial_multi_urb_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) +{ + unsigned long flags; + struct urb *urb; + unsigned char *buffer; + int status; + int towrite; + int bwrite = 0; + + dbg("%s - port %d", __func__, port->number); + + if (count == 0) + dbg("%s - write request of 0 bytes", __func__); + + while (count > 0) { + towrite = (count > port->bulk_out_size) ? + port->bulk_out_size : count; + spin_lock_irqsave(&port->lock, flags); + if (port->urbs_in_flight > + port->serial->type->max_in_flight_urbs) { + spin_unlock_irqrestore(&port->lock, flags); + dbg("%s - write limit hit\n", __func__); + return bwrite; + } + port->tx_bytes_flight += towrite; + port->urbs_in_flight++; + spin_unlock_irqrestore(&port->lock, flags); + + buffer = kmalloc(towrite, GFP_ATOMIC); + if (!buffer) { + dev_err(&port->dev, + "%s ran out of kernel memory for urb ...\n", __func__); + goto error_no_buffer; + } + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(&port->dev, "%s - no more free urbs\n", + __func__); + goto error_no_urb; + } + + /* Copy data */ + memcpy(buffer, buf + bwrite, towrite); + usb_serial_debug_data(debug, &port->dev, __func__, + towrite, buffer); + /* fill the buffer and send it */ + usb_fill_bulk_urb(urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + buffer, towrite, + usb_serial_generic_write_bulk_callback, port); + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, status); + goto error; + } + + /* This urb is the responsibility of the host driver now */ + usb_free_urb(urb); + dbg("%s write: %d", __func__, towrite); + count -= towrite; + bwrite += towrite; + } + return bwrite; + +error: + usb_free_urb(urb); +error_no_urb: + kfree(buffer); +error_no_buffer: + spin_lock_irqsave(&port->lock, flags); + port->urbs_in_flight--; + port->tx_bytes_flight -= towrite; + spin_unlock_irqrestore(&port->lock, flags); + return bwrite; +} + int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { @@ -207,6 +289,11 @@ int usb_serial_generic_write(struct tty_struct *tty, /* only do something if we have a bulk out endpoint */ if (serial->num_bulk_out) { unsigned long flags; + + if (serial->type->max_in_flight_urbs) + return usb_serial_multi_urb_write(tty, port, + buf, count); + spin_lock_irqsave(&port->lock, flags); if (port->write_urb_busy) { spin_unlock_irqrestore(&port->lock, flags); @@ -257,15 +344,18 @@ int usb_serial_generic_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; + unsigned long flags; int room = 0; dbg("%s - port %d", __func__, port->number); - - /* FIXME: Locking */ - if (serial->num_bulk_out) { - if (!(port->write_urb_busy)) + spin_lock_irqsave(&port->lock, flags); + if (serial->type->max_in_flight_urbs) { + if (port->urbs_in_flight < serial->type->max_in_flight_urbs) room = port->bulk_out_size; + } else if (serial->num_bulk_out && !(port->write_urb_busy)) { + room = port->bulk_out_size; } + spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, room); return room; @@ -276,11 +366,16 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int chars = 0; + unsigned long flags; dbg("%s - port %d", __func__, port->number); - /* FIXME: Locking */ - if (serial->num_bulk_out) { + if (serial->type->max_in_flight_urbs) { + spin_lock_irqsave(&port->lock, flags); + chars = port->tx_bytes_flight; + spin_unlock_irqrestore(&port->lock, flags); + } else if (serial->num_bulk_out) { + /* FIXME: Locking */ if (port->write_urb_busy) chars = port->write_urb->transfer_buffer_length; } @@ -363,12 +458,24 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); void usb_serial_generic_write_bulk_callback(struct urb *urb) { + unsigned long flags; struct usb_serial_port *port = urb->context; int status = urb->status; dbg("%s - port %d", __func__, port->number); - port->write_urb_busy = 0; + if (port->serial->type->max_in_flight_urbs) { + spin_lock_irqsave(&port->lock, flags); + --port->urbs_in_flight; + port->tx_bytes_flight -= urb->transfer_buffer_length; + if (port->urbs_in_flight < 0) + port->urbs_in_flight = 0; + spin_unlock_irqrestore(&port->lock, flags); + } else { + /* Handle the case for single urb mode */ + port->write_urb_busy = 0; + } + if (status) { dbg("%s - nonzero write bulk status received: %d", __func__, status); diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 6c9cbb59552..a9427a8b867 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -15,6 +15,7 @@ #include #include +#define URB_DEBUG_MAX_IN_FLIGHT_URBS 4000 #define USB_DEBUG_MAX_PACKET_SIZE 8 static struct usb_device_id id_table [] = { @@ -46,6 +47,7 @@ static struct usb_serial_driver debug_device = { .id_table = id_table, .num_ports = 1, .open = usb_debug_open, + .max_in_flight_urbs = URB_DEBUG_MAX_IN_FLIGHT_URBS, }; static int __init debug_init(void) diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 8cdfed738fe..e2938fd179e 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -91,6 +91,9 @@ struct usb_serial_port { int write_urb_busy; __u8 bulk_out_endpointAddress; + int tx_bytes_flight; + int urbs_in_flight; + wait_queue_head_t write_wait; struct work_struct work; char throttled; @@ -207,6 +210,7 @@ struct usb_serial_driver { struct device_driver driver; struct usb_driver *usb_driver; struct usb_dynids dynids; + int max_in_flight_urbs; int (*probe)(struct usb_serial *serial, const struct usb_device_id *id); int (*attach)(struct usb_serial *serial); -- cgit v1.2.3-70-g09d2 From 98fcb5f78165b8a3d93870ad7afd4d9ebbb8b43a Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Mon, 11 May 2009 15:24:09 -0500 Subject: USB: serial: usb_debug,usb_generic_serial: implement sysrq and serial break The usb_debug driver was modified to implement serial break handling by using a "magic" data packet comprised of the sequence: 0x00 0xff 0x01 0xfe 0x00 0xfe 0x01 0xff When the tty layer requests a serial break the usb_debug driver sends the magic packet. On the receiving side the magic packet is thrown away or a sysrq is activated depending on what kernel .config options have been set. The generic serial driver was modified as well as the usb serial headers to generically implement sysrq processing in the same way the non usb uart based drivers implement the sysrq handling. This will allow other usb serial devices to implement sysrq handling as desired. The new usb serial functions are named similarly and implemented similarly to the uart functions as follows: usb_serial_handle_break <-> uart_handle_break usb_serial_handle_sysrq_char <-> uart_handle_sysrq_char Signed-off-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 56 +++++++++++++++++++++++++++++++++--------- drivers/usb/serial/usb_debug.c | 39 +++++++++++++++++++++++++++++ include/linux/usb/serial.h | 8 ++++++ 3 files changed, 91 insertions(+), 12 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index c919686521f..9fccc26235a 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -339,6 +339,7 @@ int usb_serial_generic_write(struct tty_struct *tty, /* no bulk out, so return 0 bytes written */ return 0; } +EXPORT_SYMBOL_GPL(usb_serial_generic_write); int usb_serial_generic_write_room(struct tty_struct *tty) { @@ -351,7 +352,9 @@ int usb_serial_generic_write_room(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); if (serial->type->max_in_flight_urbs) { if (port->urbs_in_flight < serial->type->max_in_flight_urbs) - room = port->bulk_out_size; + room = port->bulk_out_size * + (serial->type->max_in_flight_urbs - + port->urbs_in_flight); } else if (serial->num_bulk_out && !(port->write_urb_busy)) { room = port->bulk_out_size; } @@ -385,7 +388,8 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) } -static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) +void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port, + gfp_t mem_flags) { struct urb *urb = port->read_urb; struct usb_serial *serial = port->serial; @@ -406,25 +410,28 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) "%s - failed resubmitting read urb, error %d\n", __func__, result); } +EXPORT_SYMBOL_GPL(usb_serial_generic_resubmit_read_urb); /* Push data to tty layer and resubmit the bulk read URB */ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) { struct urb *urb = port->read_urb; struct tty_struct *tty = tty_port_tty_get(&port->port); - int room; + char *ch = (char *)urb->transfer_buffer; + int i; + + if (!tty) + goto done; /* Push data to tty */ - if (tty && urb->actual_length) { - room = tty_buffer_request_room(tty, urb->actual_length); - if (room) { - tty_insert_flip_string(tty, urb->transfer_buffer, room); - tty_flip_buffer_push(tty); - } + for (i = 0; i < urb->actual_length; i++, ch++) { + if (!usb_serial_handle_sysrq_char(port, *ch)) + tty_insert_flip_char(tty, *ch, TTY_NORMAL); } + tty_flip_buffer_push(tty); tty_kref_put(tty); - - resubmit_read_urb(port, GFP_ATOMIC); +done: + usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC); } void usb_serial_generic_read_bulk_callback(struct urb *urb) @@ -515,10 +522,35 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) if (was_throttled) { /* Resume reading from device */ - resubmit_read_urb(port, GFP_KERNEL); + usb_serial_generic_resubmit_read_urb(port, GFP_KERNEL); } } +int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) +{ + if (port->sysrq) { + if (ch && time_before(jiffies, port->sysrq)) { + handle_sysrq(ch, tty_port_tty_get(&port->port)); + port->sysrq = 0; + return 1; + } + port->sysrq = 0; + } + return 0; +} +EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char); + +int usb_serial_handle_break(struct usb_serial_port *port) +{ + if (!port->sysrq) { + port->sysrq = jiffies + HZ*5; + return 1; + } + port->sysrq = 0; + return 0; +} +EXPORT_SYMBOL_GPL(usb_serial_handle_break); + void usb_serial_generic_shutdown(struct usb_serial *serial) { int i; diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index a9427a8b867..614800972dc 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -17,6 +17,17 @@ #define URB_DEBUG_MAX_IN_FLIGHT_URBS 4000 #define USB_DEBUG_MAX_PACKET_SIZE 8 +#define USB_DEBUG_BRK_SIZE 8 +static char USB_DEBUG_BRK[USB_DEBUG_BRK_SIZE] = { + 0x00, + 0xff, + 0x01, + 0xfe, + 0x00, + 0xfe, + 0x01, + 0xff, +}; static struct usb_device_id id_table [] = { { USB_DEVICE(0x0525, 0x127a) }, @@ -39,6 +50,32 @@ static int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port, return usb_serial_generic_open(tty, port, filp); } +/* This HW really does not support a serial break, so one will be + * emulated when ever the break state is set to true. + */ +static void usb_debug_break_ctl(struct tty_struct *tty, int break_state) +{ + struct usb_serial_port *port = tty->driver_data; + if (!break_state) + return; + usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE); +} + +static void usb_debug_read_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + + if (urb->actual_length == USB_DEBUG_BRK_SIZE && + memcmp(urb->transfer_buffer, USB_DEBUG_BRK, + USB_DEBUG_BRK_SIZE) == 0) { + usb_serial_handle_break(port); + usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC); + return; + } + + usb_serial_generic_read_bulk_callback(urb); +} + static struct usb_serial_driver debug_device = { .driver = { .owner = THIS_MODULE, @@ -48,6 +85,8 @@ static struct usb_serial_driver debug_device = { .num_ports = 1, .open = usb_debug_open, .max_in_flight_urbs = URB_DEBUG_MAX_IN_FLIGHT_URBS, + .break_ctl = usb_debug_break_ctl, + .read_bulk_callback = usb_debug_read_bulk_callback, }; static int __init debug_init(void) diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index e2938fd179e..e29ebcf3287 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -15,6 +15,7 @@ #include #include +#include #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ #define SERIAL_TTY_MINORS 254 /* loads of devices :) */ @@ -99,6 +100,7 @@ struct usb_serial_port { char throttled; char throttle_req; char console; + unsigned long sysrq; /* sysrq timeout */ struct device dev; }; #define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev) @@ -301,6 +303,12 @@ extern void usb_serial_generic_unthrottle(struct tty_struct *tty); extern void usb_serial_generic_shutdown(struct usb_serial *serial); extern int usb_serial_generic_register(int debug); extern void usb_serial_generic_deregister(void); +extern void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port, + gfp_t mem_flags); +extern int usb_serial_handle_sysrq_char(struct usb_serial_port *port, + unsigned int ch); +extern int usb_serial_handle_break(struct usb_serial_port *port); + extern int usb_serial_bus_register(struct usb_serial_driver *device); extern void usb_serial_bus_deregister(struct usb_serial_driver *device); -- cgit v1.2.3-70-g09d2 From 5effabbe9e6e0089f7afdde35cb51e8c8b4cf6bc Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 26 May 2009 18:24:34 +0900 Subject: USB: r8a66597-hcd: use platform_data instead of module_param CPU/board specific parameters (PLL clock, vif etc...) can be set by platform_data instead of module_param. v2: remove irq_sense member in platform_data because it can OR in IRQF_TRIGGER_LOW or IRQF_TRIGGER_FALLING against IORESOURCE_IRQ in the struct resource. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 60 ++++++++++------------------------------- drivers/usb/host/r8a66597.h | 38 +++++++++++++++++++++++--- include/linux/usb/r8a66597.h | 44 ++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 50 deletions(-) create mode 100644 include/linux/usb/r8a66597.h (limited to 'include/linux/usb') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 3e1216ad86b..56976cc0352 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -46,31 +46,10 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yoshihiro Shimoda"); MODULE_ALIAS("platform:r8a66597_hcd"); -#define DRIVER_VERSION "10 Apr 2008" +#define DRIVER_VERSION "2009-05-26" static const char hcd_name[] = "r8a66597_hcd"; -/* module parameters */ -#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597) -static unsigned short clock = XTAL12; -module_param(clock, ushort, 0644); -MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 " - "(default=0)"); -#endif - -static unsigned short vif = LDRV; -module_param(vif, ushort, 0644); -MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0(default=32768)"); - -static unsigned short endian; -module_param(endian, ushort, 0644); -MODULE_PARM_DESC(endian, "data endian: big=256, little=0 (default=0)"); - -static unsigned short irq_sense = 0xff; -module_param(irq_sense, ushort, 0644); -MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=32, falling edge=0 " - "(default=32)"); - static void packet_write(struct r8a66597 *r8a66597, u16 pipenum); static int r8a66597_get_frame(struct usb_hcd *hcd); @@ -136,7 +115,8 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597) } } while ((tmp & USBE) != USBE); r8a66597_bclr(r8a66597, USBE, SYSCFG0); - r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0); + r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata), XTAL, + SYSCFG0); i = 0; r8a66597_bset(r8a66597, XCKE, SYSCFG0); @@ -203,6 +183,9 @@ static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port) static int enable_controller(struct r8a66597 *r8a66597) { int ret, port; + u16 vif = r8a66597->pdata->vif ? LDRV : 0; + u16 irq_sense = r8a66597->irq_sense_low ? INTL : 0; + u16 endian = r8a66597->pdata->endian ? BIGEND : 0; ret = r8a66597_clock_enable(r8a66597); if (ret < 0) @@ -2418,6 +2401,12 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) goto clean_up; } + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "no platform data\n"); + ret = -ENODEV; + goto clean_up; + } + /* initialize hcd */ hcd = usb_create_hcd(&r8a66597_hc_driver, &pdev->dev, (char *)hcd_name); if (!hcd) { @@ -2428,6 +2417,8 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) r8a66597 = hcd_to_r8a66597(hcd); memset(r8a66597, 0, sizeof(struct r8a66597)); dev_set_drvdata(&pdev->dev, r8a66597); + r8a66597->pdata = pdev->dev.platform_data; + r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); @@ -2458,29 +2449,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; - /* irq_sense setting on cmdline takes precedence over resource - * settings, so the introduction of irqflags in IRQ resourse - * won't disturb existing setups */ - switch (irq_sense) { - case INTL: - irq_trigger = IRQF_TRIGGER_LOW; - break; - case 0: - irq_trigger = IRQF_TRIGGER_FALLING; - break; - case 0xff: - if (irq_trigger) - irq_sense = (irq_trigger & IRQF_TRIGGER_LOW) ? - INTL : 0; - else { - irq_sense = INTL; - irq_trigger = IRQF_TRIGGER_LOW; - } - break; - default: - dev_err(&pdev->dev, "Unknown irq_sense value.\n"); - } - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd\n"); diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index f49208f1bb7..d72680b433f 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -30,6 +30,8 @@ #include #endif +#include + #define SYSCFG0 0x00 #define SYSCFG1 0x02 #define SYSSTS0 0x04 @@ -488,6 +490,7 @@ struct r8a66597 { #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) struct clk *clk; #endif + struct r8a66597_platdata *pdata; struct r8a66597_device device0; struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB]; struct list_head pipe_queue[R8A66597_MAX_NUM_PIPE]; @@ -506,6 +509,7 @@ struct r8a66597 { unsigned long child_connect_map[4]; unsigned bus_suspended:1; + unsigned irq_sense_low:1; }; static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd) @@ -660,10 +664,36 @@ static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, { unsigned long dvstctr_reg = get_dvstctr_reg(port); - if (power) - r8a66597_bset(r8a66597, VBOUT, dvstctr_reg); - else - r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg); + if (r8a66597->pdata->port_power) { + r8a66597->pdata->port_power(port, power); + } else { + if (power) + r8a66597_bset(r8a66597, VBOUT, dvstctr_reg); + else + r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg); + } +} + +static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) +{ + u16 clock = 0; + + switch (pdata->xtal) { + case R8A66597_PLATDATA_XTAL_12MHZ: + clock = XTAL12; + break; + case R8A66597_PLATDATA_XTAL_24MHZ: + clock = XTAL24; + break; + case R8A66597_PLATDATA_XTAL_48MHZ: + clock = XTAL48; + break; + default: + printk(KERN_ERR "r8a66597: platdata clock is wrong.\n"); + break; + } + + return clock; } #define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) diff --git a/include/linux/usb/r8a66597.h b/include/linux/usb/r8a66597.h new file mode 100644 index 00000000000..e9f0384fa20 --- /dev/null +++ b/include/linux/usb/r8a66597.h @@ -0,0 +1,44 @@ +/* + * R8A66597 driver platform data + * + * Copyright (C) 2009 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __LINUX_USB_R8A66597_H +#define __LINUX_USB_R8A66597_H + +#define R8A66597_PLATDATA_XTAL_12MHZ 0x01 +#define R8A66597_PLATDATA_XTAL_24MHZ 0x02 +#define R8A66597_PLATDATA_XTAL_48MHZ 0x03 + +struct r8a66597_platdata { + /* This ops can controll port power instead of DVSTCTR register. */ + void (*port_power)(int port, int power); + + /* (external controller only) set R8A66597_PLATDATA_XTAL_nnMHZ */ + unsigned xtal:2; + + /* set one = 3.3V, set zero = 1.5V */ + unsigned vif:1; + + /* set one = big endian, set zero = little endian */ + unsigned endian:1; +}; +#endif + -- cgit v1.2.3-70-g09d2 From c47d7b09891abb4f8b222317c89c7469bed8db3a Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Wed, 3 Jun 2009 09:17:57 -0400 Subject: USB: audio: add USB audio class definitions Signed-off-by: Bryan Wu Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/audio.h | 265 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 263 insertions(+), 2 deletions(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index 8cb025fef63..b5744bc218a 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h @@ -24,10 +24,75 @@ #define USB_SUBCLASS_AUDIOCONTROL 0x01 #define USB_SUBCLASS_AUDIOSTREAMING 0x02 #define USB_SUBCLASS_MIDISTREAMING 0x03 +#define USB_SUBCLASS_VENDOR_SPEC 0xff +/* A.5 Audio Class-Specific AC interface Descriptor Subtypes*/ +#define HEADER 0x01 +#define INPUT_TERMINAL 0x02 +#define OUTPUT_TERMINAL 0x03 +#define MIXER_UNIT 0x04 +#define SELECTOR_UNIT 0x05 +#define FEATURE_UNIT 0x06 +#define PROCESSING_UNIT 0x07 +#define EXTENSION_UNIT 0x08 + +#define AS_GENERAL 0x01 +#define FORMAT_TYPE 0x02 +#define FORMAT_SPECIFIC 0x03 + +#define EP_GENERAL 0x01 + +#define MS_GENERAL 0x01 +#define MIDI_IN_JACK 0x02 +#define MIDI_OUT_JACK 0x03 + +/* endpoint attributes */ +#define EP_ATTR_MASK 0x0c +#define EP_ATTR_ASYNC 0x04 +#define EP_ATTR_ADAPTIVE 0x08 +#define EP_ATTR_SYNC 0x0c + +/* cs endpoint attributes */ +#define EP_CS_ATTR_SAMPLE_RATE 0x01 +#define EP_CS_ATTR_PITCH_CONTROL 0x02 +#define EP_CS_ATTR_FILL_MAX 0x80 + +/* Audio Class specific Request Codes */ +#define USB_AUDIO_SET_INTF 0x21 +#define USB_AUDIO_SET_ENDPOINT 0x22 +#define USB_AUDIO_GET_INTF 0xa1 +#define USB_AUDIO_GET_ENDPOINT 0xa2 + +#define SET_ 0x00 +#define GET_ 0x80 + +#define _CUR 0x1 +#define _MIN 0x2 +#define _MAX 0x3 +#define _RES 0x4 +#define _MEM 0x5 + +#define SET_CUR (SET_ | _CUR) +#define GET_CUR (GET_ | _CUR) +#define SET_MIN (SET_ | _MIN) +#define GET_MIN (GET_ | _MIN) +#define SET_MAX (SET_ | _MAX) +#define GET_MAX (GET_ | _MAX) +#define SET_RES (SET_ | _RES) +#define GET_RES (GET_ | _RES) +#define SET_MEM (SET_ | _MEM) +#define GET_MEM (GET_ | _MEM) + +#define GET_STAT 0xff + +#define USB_AC_TERMINAL_UNDEFINED 0x100 +#define USB_AC_TERMINAL_STREAMING 0x101 +#define USB_AC_TERMINAL_VENDOR_SPEC 0x1FF + +/* Terminal Control Selectors */ /* 4.3.2 Class-Specific AC Interface Descriptor */ struct usb_ac_header_descriptor { - __u8 bLength; /* 8+n */ + __u8 bLength; /* 8 + n */ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ __u8 bDescriptorSubtype; /* USB_MS_HEADER */ __le16 bcdADC; /* 0x0100 */ @@ -36,7 +101,7 @@ struct usb_ac_header_descriptor { __u8 baInterfaceNr[]; /* [n] */ } __attribute__ ((packed)); -#define USB_DT_AC_HEADER_SIZE(n) (8+(n)) +#define USB_DT_AC_HEADER_SIZE(n) (8 + (n)) /* As above, but more useful for defining your own descriptors: */ #define DECLARE_USB_AC_HEADER_DESCRIPTOR(n) \ @@ -50,4 +115,200 @@ struct usb_ac_header_descriptor_##n { \ __u8 baInterfaceNr[n]; \ } __attribute__ ((packed)) +/* 4.3.2.1 Input Terminal Descriptor */ +struct usb_input_terminal_descriptor { + __u8 bLength; /* in bytes: 12 */ + __u8 bDescriptorType; /* CS_INTERFACE descriptor type */ + __u8 bDescriptorSubtype; /* INPUT_TERMINAL descriptor subtype */ + __u8 bTerminalID; /* Constant uniquely terminal ID */ + __le16 wTerminalType; /* USB Audio Terminal Types */ + __u8 bAssocTerminal; /* ID of the Output Terminal associated */ + __u8 bNrChannels; /* Number of logical output channels */ + __le16 wChannelConfig; + __u8 iChannelNames; + __u8 iTerminal; +} __attribute__ ((packed)); + +#define USB_DT_AC_INPUT_TERMINAL_SIZE 12 + +#define USB_AC_INPUT_TERMINAL_UNDEFINED 0x200 +#define USB_AC_INPUT_TERMINAL_MICROPHONE 0x201 +#define USB_AC_INPUT_TERMINAL_DESKTOP_MICROPHONE 0x202 +#define USB_AC_INPUT_TERMINAL_PERSONAL_MICROPHONE 0x203 +#define USB_AC_INPUT_TERMINAL_OMNI_DIR_MICROPHONE 0x204 +#define USB_AC_INPUT_TERMINAL_MICROPHONE_ARRAY 0x205 +#define USB_AC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206 + +/* 4.3.2.2 Output Terminal Descriptor */ +struct usb_output_terminal_descriptor { + __u8 bLength; /* in bytes: 9 */ + __u8 bDescriptorType; /* CS_INTERFACE descriptor type */ + __u8 bDescriptorSubtype; /* OUTPUT_TERMINAL descriptor subtype */ + __u8 bTerminalID; /* Constant uniquely terminal ID */ + __le16 wTerminalType; /* USB Audio Terminal Types */ + __u8 bAssocTerminal; /* ID of the Input Terminal associated */ + __u8 bSourceID; /* ID of the connected Unit or Terminal*/ + __u8 iTerminal; +} __attribute__ ((packed)); + +#define USB_DT_AC_OUTPUT_TERMINAL_SIZE 9 + +#define USB_AC_OUTPUT_TERMINAL_UNDEFINED 0x300 +#define USB_AC_OUTPUT_TERMINAL_SPEAKER 0x301 +#define USB_AC_OUTPUT_TERMINAL_HEADPHONES 0x302 +#define USB_AC_OUTPUT_TERMINAL_HEAD_MOUNTED_DISPLAY_AUDIO 0x303 +#define USB_AC_OUTPUT_TERMINAL_DESKTOP_SPEAKER 0x304 +#define USB_AC_OUTPUT_TERMINAL_ROOM_SPEAKER 0x305 +#define USB_AC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER 0x306 +#define USB_AC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER 0x307 + +/* Set bControlSize = 2 as default setting */ +#define USB_DT_AC_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 2) + +/* As above, but more useful for defining your own descriptors: */ +#define DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(ch) \ +struct usb_ac_feature_unit_descriptor_##ch { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubtype; \ + __u8 bUnitID; \ + __u8 bSourceID; \ + __u8 bControlSize; \ + __le16 bmaControls[ch + 1]; \ + __u8 iFeature; \ +} __attribute__ ((packed)) + +/* 4.5.2 Class-Specific AS Interface Descriptor */ +struct usb_as_header_descriptor { + __u8 bLength; /* in bytes: 7 */ + __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ + __u8 bDescriptorSubtype; /* AS_GENERAL */ + __u8 bTerminalLink; /* Terminal ID of connected Terminal */ + __u8 bDelay; /* Delay introduced by the data path */ + __le16 wFormatTag; /* The Audio Data Format */ +} __attribute__ ((packed)); + +#define USB_DT_AS_HEADER_SIZE 7 + +#define USB_AS_AUDIO_FORMAT_TYPE_I_UNDEFINED 0x0 +#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM 0x1 +#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM8 0x2 +#define USB_AS_AUDIO_FORMAT_TYPE_I_IEEE_FLOAT 0x3 +#define USB_AS_AUDIO_FORMAT_TYPE_I_ALAW 0x4 +#define USB_AS_AUDIO_FORMAT_TYPE_I_MULAW 0x5 + +struct usb_as_format_type_i_continuous_descriptor { + __u8 bLength; /* in bytes: 8 + (ns * 3) */ + __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ + __u8 bDescriptorSubtype; /* FORMAT_TYPE */ + __u8 bFormatType; /* FORMAT_TYPE_1 */ + __u8 bNrChannels; /* physical channels in the stream */ + __u8 bSubframeSize; /* */ + __u8 bBitResolution; + __u8 bSamFreqType; + __u8 tLowerSamFreq[3]; + __u8 tUpperSamFreq[3]; +} __attribute__ ((packed)); + +#define USB_AS_FORMAT_TYPE_I_CONTINUOUS_DESC_SIZE 14 + +struct usb_as_formate_type_i_discrete_descriptor { + __u8 bLength; /* in bytes: 8 + (ns * 3) */ + __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ + __u8 bDescriptorSubtype; /* FORMAT_TYPE */ + __u8 bFormatType; /* FORMAT_TYPE_1 */ + __u8 bNrChannels; /* physical channels in the stream */ + __u8 bSubframeSize; /* */ + __u8 bBitResolution; + __u8 bSamFreqType; + __u8 tSamFreq[][3]; +} __attribute__ ((packed)); + +#define DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(n) \ +struct usb_as_formate_type_i_discrete_descriptor_##n { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubtype; \ + __u8 bFormatType; \ + __u8 bNrChannels; \ + __u8 bSubframeSize; \ + __u8 bBitResolution; \ + __u8 bSamFreqType; \ + __u8 tSamFreq[n][3]; \ +} __attribute__ ((packed)) + +#define USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3)) + +#define USB_AS_FORMAT_TYPE_UNDEFINED 0x0 +#define USB_AS_FORMAT_TYPE_I 0x1 +#define USB_AS_FORMAT_TYPE_II 0x2 +#define USB_AS_FORMAT_TYPE_III 0x3 + +#define USB_AS_ENDPOINT_ASYNC (1 << 2) +#define USB_AS_ENDPOINT_ADAPTIVE (2 << 2) +#define USB_AS_ENDPOINT_SYNC (3 << 2) + +struct usb_as_iso_endpoint_descriptor { + __u8 bLength; /* in bytes: 7 */ + __u8 bDescriptorType; /* USB_DT_CS_ENDPOINT */ + __u8 bDescriptorSubtype; /* EP_GENERAL */ + __u8 bmAttributes; + __u8 bLockDelayUnits; + __le16 wLockDelay; +}; +#define USB_AS_ISO_ENDPOINT_DESC_SIZE 7 + +#define FU_CONTROL_UNDEFINED 0x00 +#define MUTE_CONTROL 0x01 +#define VOLUME_CONTROL 0x02 +#define BASS_CONTROL 0x03 +#define MID_CONTROL 0x04 +#define TREBLE_CONTROL 0x05 +#define GRAPHIC_EQUALIZER_CONTROL 0x06 +#define AUTOMATIC_GAIN_CONTROL 0x07 +#define DELAY_CONTROL 0x08 +#define BASS_BOOST_CONTROL 0x09 +#define LOUDNESS_CONTROL 0x0a + +#define FU_MUTE (1 << (MUTE_CONTROL - 1)) +#define FU_VOLUME (1 << (VOLUME_CONTROL - 1)) +#define FU_BASS (1 << (BASS_CONTROL - 1)) +#define FU_MID (1 << (MID_CONTROL - 1)) +#define FU_TREBLE (1 << (TREBLE_CONTROL - 1)) +#define FU_GRAPHIC_EQ (1 << (GRAPHIC_EQUALIZER_CONTROL - 1)) +#define FU_AUTO_GAIN (1 << (AUTOMATIC_GAIN_CONTROL - 1)) +#define FU_DELAY (1 << (DELAY_CONTROL - 1)) +#define FU_BASS_BOOST (1 << (BASS_BOOST_CONTROL - 1)) +#define FU_LOUDNESS (1 << (LOUDNESS_CONTROL - 1)) + +struct usb_audio_control { + struct list_head list; + const char *name; + u8 type; + int data[5]; + int (*set)(struct usb_audio_control *con, u8 cmd, int value); + int (*get)(struct usb_audio_control *con, u8 cmd); +}; + +static inline int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) +{ + con->data[cmd] = value; + + return 0; +} + +static inline int generic_get_cmd(struct usb_audio_control *con, u8 cmd) +{ + return con->data[cmd]; +} + +struct usb_audio_control_selector { + struct list_head list; + struct list_head control; + u8 id; + const char *name; + u8 type; + struct usb_descriptor_header *desc; +}; + #endif /* __LINUX_USB_AUDIO_H */ -- cgit v1.2.3-70-g09d2 From c706ebdfc8955b850e477255a8c0f93f9f14712d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 2 Jun 2009 11:54:11 -0400 Subject: USB: usb-serial: call port_probe and port_remove at the right times This patch (as1253) prevents the usb-serial core from calling a driver's port_probe and port_remove methods more than once per port. It also removes some unnecessary try_module_get() calls and adds a missing port_remove method call in a failure path. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/bus.c | 27 +++++++++++---------------- drivers/usb/serial/usb-serial.c | 24 ++++++++++++++++++++++-- include/linux/usb/serial.h | 8 ++++++++ 3 files changed, 41 insertions(+), 18 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 83bbb5bca2e..ba555c528cc 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -59,23 +59,22 @@ static int usb_serial_device_probe(struct device *dev) retval = -ENODEV; goto exit; } + if (port->dev_state != PORT_REGISTERING) + goto exit; driver = port->serial->type; if (driver->port_probe) { - if (!try_module_get(driver->driver.owner)) { - dev_err(dev, "module get failed, exiting\n"); - retval = -EIO; - goto exit; - } retval = driver->port_probe(port); - module_put(driver->driver.owner); if (retval) goto exit; } retval = device_create_file(dev, &dev_attr_port_number); - if (retval) + if (retval) { + if (driver->port_remove) + retval = driver->port_remove(port); goto exit; + } minor = port->number; tty_register_device(usb_serial_tty_driver, minor, dev); @@ -98,19 +97,15 @@ static int usb_serial_device_remove(struct device *dev) if (!port) return -ENODEV; + if (port->dev_state != PORT_UNREGISTERING) + return retval; + device_remove_file(&port->dev, &dev_attr_port_number); driver = port->serial->type; - if (driver->port_remove) { - if (!try_module_get(driver->driver.owner)) { - dev_err(dev, "module get failed, exiting\n"); - retval = -EIO; - goto exit; - } + if (driver->port_remove) retval = driver->port_remove(port); - module_put(driver->driver.owner); - } -exit: + minor = port->number; tty_unregister_device(usb_serial_tty_driver, minor); dev_info(dev, "%s converter now disconnected from ttyUSB%d\n", diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 1967a7edc10..da890f030fa 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1046,10 +1046,15 @@ int usb_serial_probe(struct usb_interface *interface, dev_set_name(&port->dev, "ttyUSB%d", port->number); dbg ("%s - registering %s", __func__, dev_name(&port->dev)); + port->dev_state = PORT_REGISTERING; retval = device_register(&port->dev); - if (retval) + if (retval) { dev_err(&port->dev, "Error registering port device, " "continuing\n"); + port->dev_state = PORT_UNREGISTERED; + } else { + port->dev_state = PORT_REGISTERED; + } } usb_serial_console_init(debug, minor); @@ -1130,7 +1135,22 @@ void usb_serial_disconnect(struct usb_interface *interface) } kill_traffic(port); cancel_work_sync(&port->work); - device_del(&port->dev); + if (port->dev_state == PORT_REGISTERED) { + + /* Make sure the port is bound so that the + * driver's port_remove method is called. + */ + if (!port->dev.driver) { + int rc; + + port->dev.driver = + &serial->type->driver; + rc = device_bind_driver(&port->dev); + } + port->dev_state = PORT_UNREGISTERING; + device_del(&port->dev); + port->dev_state = PORT_UNREGISTERED; + } } } serial->type->shutdown(serial); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index e29ebcf3287..ed4aa0fa7ed 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -27,6 +27,13 @@ /* parity check flag */ #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) +enum port_dev_state { + PORT_UNREGISTERED, + PORT_REGISTERING, + PORT_REGISTERED, + PORT_UNREGISTERING, +}; + /** * usb_serial_port: structure for the specific ports of a device. * @serial: pointer back to the struct usb_serial owner of this port. @@ -102,6 +109,7 @@ struct usb_serial_port { char console; unsigned long sysrq; /* sysrq timeout */ struct device dev; + enum port_dev_state dev_state; }; #define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev) -- cgit v1.2.3-70-g09d2 From f9c99bb8b3a1ec81af68d484a551307326c2e933 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 2 Jun 2009 11:53:55 -0400 Subject: USB: usb-serial: replace shutdown with disconnect, release This patch (as1254) splits up the shutdown method of usb_serial_driver into a disconnect and a release method. The problem is that the usb-serial core was calling shutdown during disconnect handling, but drivers didn't expect it to be called until after all the open file references had been closed. The result was an oops when the close method tried to use memory that had been deallocated by shutdown. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/staging/uc2322/aten2011.c | 4 ++-- drivers/usb/serial/aircable.c | 5 ++--- drivers/usb/serial/belkin_sa.c | 7 +++--- drivers/usb/serial/cp210x.c | 6 ++--- drivers/usb/serial/cyberjack.c | 20 ++++++++++++----- drivers/usb/serial/cypress_m8.c | 11 +++++---- drivers/usb/serial/digi_acceleport.c | 20 ++++++++++++----- drivers/usb/serial/empeg.c | 8 ------- drivers/usb/serial/ftdi_sio.c | 14 ------------ drivers/usb/serial/garmin_gps.c | 16 ++++++++++--- drivers/usb/serial/generic.c | 9 ++++++-- drivers/usb/serial/io_edgeport.c | 29 ++++++++++++++++-------- drivers/usb/serial/io_tables.h | 12 ++++++---- drivers/usb/serial/io_ti.c | 22 +++++++++++++----- drivers/usb/serial/ipaq.c | 7 ------ drivers/usb/serial/iuu_phoenix.c | 6 ++--- drivers/usb/serial/keyspan.c | 13 ++++++++++- drivers/usb/serial/keyspan.h | 12 ++++++---- drivers/usb/serial/keyspan_pda.c | 4 ++-- drivers/usb/serial/kl5kusb105.c | 39 ++++++++++++++++++-------------- drivers/usb/serial/kobil_sct.c | 12 ++++------ drivers/usb/serial/mct_u232.c | 13 +++++------ drivers/usb/serial/mos7720.c | 9 +++----- drivers/usb/serial/mos7840.c | 42 ++++++++++++++++++++++++++++++----- drivers/usb/serial/omninet.c | 19 ++++++++++++---- drivers/usb/serial/opticon.c | 14 +++++++++--- drivers/usb/serial/option.c | 17 +++++++++----- drivers/usb/serial/oti6858.c | 7 +++--- drivers/usb/serial/pl2303.c | 5 ++--- drivers/usb/serial/sierra.c | 4 ++-- drivers/usb/serial/spcp8x5.c | 5 ++--- drivers/usb/serial/symbolserial.c | 14 +++++++++--- drivers/usb/serial/ti_usb_3410_5052.c | 10 ++++----- drivers/usb/serial/usb-serial.c | 29 +++++++++++------------- drivers/usb/serial/visor.c | 13 +++++------ drivers/usb/serial/whiteheat.c | 6 ++--- include/linux/usb/serial.h | 12 ++++++---- 37 files changed, 297 insertions(+), 198 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/staging/uc2322/aten2011.c b/drivers/staging/uc2322/aten2011.c index 9c62f787cc9..39d0926d1a9 100644 --- a/drivers/staging/uc2322/aten2011.c +++ b/drivers/staging/uc2322/aten2011.c @@ -2336,7 +2336,7 @@ static int ATEN2011_startup(struct usb_serial *serial) return 0; } -static void ATEN2011_shutdown(struct usb_serial *serial) +static void ATEN2011_release(struct usb_serial *serial) { int i; struct ATENINTL_port *ATEN2011_port; @@ -2382,7 +2382,7 @@ static struct usb_serial_driver aten_serial_driver = { .tiocmget = ATEN2011_tiocmget, .tiocmset = ATEN2011_tiocmset, .attach = ATEN2011_startup, - .shutdown = ATEN2011_shutdown, + .release = ATEN2011_release, .read_bulk_callback = ATEN2011_bulk_in_callback, .read_int_callback = ATEN2011_interrupt_callback, }; diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 6d106e74265..2cbfab3716e 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -364,7 +364,7 @@ static int aircable_attach(struct usb_serial *serial) return 0; } -static void aircable_shutdown(struct usb_serial *serial) +static void aircable_release(struct usb_serial *serial) { struct usb_serial_port *port = serial->port[0]; @@ -375,7 +375,6 @@ static void aircable_shutdown(struct usb_serial *serial) if (priv) { serial_buf_free(priv->tx_buf); serial_buf_free(priv->rx_buf); - usb_set_serial_port_data(port, NULL); kfree(priv); } } @@ -601,7 +600,7 @@ static struct usb_serial_driver aircable_device = { .num_ports = 1, .attach = aircable_attach, .probe = aircable_probe, - .shutdown = aircable_shutdown, + .release = aircable_release, .write = aircable_write, .write_room = aircable_write_room, .write_bulk_callback = aircable_write_bulk_callback, diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 2bfd6dd85b5..7033b031b44 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -90,7 +90,7 @@ static int debug; /* function prototypes for a Belkin USB Serial Adapter F5U103 */ static int belkin_sa_startup(struct usb_serial *serial); -static void belkin_sa_shutdown(struct usb_serial *serial); +static void belkin_sa_release(struct usb_serial *serial); static int belkin_sa_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void belkin_sa_close(struct usb_serial_port *port); @@ -142,7 +142,7 @@ static struct usb_serial_driver belkin_device = { .tiocmget = belkin_sa_tiocmget, .tiocmset = belkin_sa_tiocmset, .attach = belkin_sa_startup, - .shutdown = belkin_sa_shutdown, + .release = belkin_sa_release, }; @@ -197,14 +197,13 @@ static int belkin_sa_startup(struct usb_serial *serial) } -static void belkin_sa_shutdown(struct usb_serial *serial) +static void belkin_sa_release(struct usb_serial *serial) { struct belkin_sa_private *priv; int i; dbg("%s", __func__); - /* stop reads and writes on all ports */ for (i = 0; i < serial->num_ports; ++i) { /* My special items, the standard routines free my urbs */ priv = usb_get_serial_port_data(serial->port[i]); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 16a154d3b2f..2b9eeda62bf 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -50,7 +50,7 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *, unsigned int, unsigned int); static void cp210x_break_ctl(struct tty_struct *, int); static int cp210x_startup(struct usb_serial *); -static void cp210x_shutdown(struct usb_serial *); +static void cp210x_disconnect(struct usb_serial *); static int debug; @@ -137,7 +137,7 @@ static struct usb_serial_driver cp210x_device = { .tiocmget = cp210x_tiocmget, .tiocmset = cp210x_tiocmset, .attach = cp210x_startup, - .shutdown = cp210x_shutdown, + .disconnect = cp210x_disconnect, }; /* Config request types */ @@ -792,7 +792,7 @@ static int cp210x_startup(struct usb_serial *serial) return 0; } -static void cp210x_shutdown(struct usb_serial *serial) +static void cp210x_disconnect(struct usb_serial *serial) { int i; diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 933ba913e66..336523fd736 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -58,7 +58,8 @@ static int debug; /* Function prototypes */ static int cyberjack_startup(struct usb_serial *serial); -static void cyberjack_shutdown(struct usb_serial *serial); +static void cyberjack_disconnect(struct usb_serial *serial); +static void cyberjack_release(struct usb_serial *serial); static int cyberjack_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void cyberjack_close(struct usb_serial_port *port); @@ -94,7 +95,8 @@ static struct usb_serial_driver cyberjack_device = { .id_table = id_table, .num_ports = 1, .attach = cyberjack_startup, - .shutdown = cyberjack_shutdown, + .disconnect = cyberjack_disconnect, + .release = cyberjack_release, .open = cyberjack_open, .close = cyberjack_close, .write = cyberjack_write, @@ -148,17 +150,25 @@ static int cyberjack_startup(struct usb_serial *serial) return 0; } -static void cyberjack_shutdown(struct usb_serial *serial) +static void cyberjack_disconnect(struct usb_serial *serial) { int i; dbg("%s", __func__); - for (i = 0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) usb_kill_urb(serial->port[i]->interrupt_in_urb); +} + +static void cyberjack_release(struct usb_serial *serial) +{ + int i; + + dbg("%s", __func__); + + for (i = 0; i < serial->num_ports; ++i) { /* My special items, the standard routines free my urbs */ kfree(usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); } } diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 669f9384853..9734085fd2f 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -171,7 +171,7 @@ struct cypress_buf { static int cypress_earthmate_startup(struct usb_serial *serial); static int cypress_hidcom_startup(struct usb_serial *serial); static int cypress_ca42v2_startup(struct usb_serial *serial); -static void cypress_shutdown(struct usb_serial *serial); +static void cypress_release(struct usb_serial *serial); static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void cypress_close(struct usb_serial_port *port); @@ -215,7 +215,7 @@ static struct usb_serial_driver cypress_earthmate_device = { .id_table = id_table_earthmate, .num_ports = 1, .attach = cypress_earthmate_startup, - .shutdown = cypress_shutdown, + .release = cypress_release, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -242,7 +242,7 @@ static struct usb_serial_driver cypress_hidcom_device = { .id_table = id_table_cyphidcomrs232, .num_ports = 1, .attach = cypress_hidcom_startup, - .shutdown = cypress_shutdown, + .release = cypress_release, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -269,7 +269,7 @@ static struct usb_serial_driver cypress_ca42v2_device = { .id_table = id_table_nokiaca42v2, .num_ports = 1, .attach = cypress_ca42v2_startup, - .shutdown = cypress_shutdown, + .release = cypress_release, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -616,7 +616,7 @@ static int cypress_ca42v2_startup(struct usb_serial *serial) } /* cypress_ca42v2_startup */ -static void cypress_shutdown(struct usb_serial *serial) +static void cypress_release(struct usb_serial *serial) { struct cypress_private *priv; @@ -629,7 +629,6 @@ static void cypress_shutdown(struct usb_serial *serial) if (priv) { cypress_buf_free(priv->buf); kfree(priv); - usb_set_serial_port_data(serial->port[0], NULL); } } diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 30f5140eff0..f4808091c47 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -460,7 +460,8 @@ static int digi_carrier_raised(struct usb_serial_port *port); static void digi_dtr_rts(struct usb_serial_port *port, int on); static int digi_startup_device(struct usb_serial *serial); static int digi_startup(struct usb_serial *serial); -static void digi_shutdown(struct usb_serial *serial); +static void digi_disconnect(struct usb_serial *serial); +static void digi_release(struct usb_serial *serial); static void digi_read_bulk_callback(struct urb *urb); static int digi_read_inb_callback(struct urb *urb); static int digi_read_oob_callback(struct urb *urb); @@ -524,7 +525,8 @@ static struct usb_serial_driver digi_acceleport_2_device = { .tiocmget = digi_tiocmget, .tiocmset = digi_tiocmset, .attach = digi_startup, - .shutdown = digi_shutdown, + .disconnect = digi_disconnect, + .release = digi_release, }; static struct usb_serial_driver digi_acceleport_4_device = { @@ -550,7 +552,8 @@ static struct usb_serial_driver digi_acceleport_4_device = { .tiocmget = digi_tiocmget, .tiocmset = digi_tiocmset, .attach = digi_startup, - .shutdown = digi_shutdown, + .disconnect = digi_disconnect, + .release = digi_release, }; @@ -1556,16 +1559,23 @@ static int digi_startup(struct usb_serial *serial) } -static void digi_shutdown(struct usb_serial *serial) +static void digi_disconnect(struct usb_serial *serial) { int i; - dbg("digi_shutdown: TOP, in_interrupt()=%ld", in_interrupt()); + dbg("digi_disconnect: TOP, in_interrupt()=%ld", in_interrupt()); /* stop reads and writes on all ports */ for (i = 0; i < serial->type->num_ports + 1; i++) { usb_kill_urb(serial->port[i]->read_urb); usb_kill_urb(serial->port[i]->write_urb); } +} + + +static void digi_release(struct usb_serial *serial) +{ + int i; + dbg("digi_release: TOP, in_interrupt()=%ld", in_interrupt()); /* free the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 2b141ccb0cd..80cb3471adb 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -90,7 +90,6 @@ static int empeg_chars_in_buffer(struct tty_struct *tty); static void empeg_throttle(struct tty_struct *tty); static void empeg_unthrottle(struct tty_struct *tty); static int empeg_startup(struct usb_serial *serial); -static void empeg_shutdown(struct usb_serial *serial); static void empeg_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); static void empeg_write_bulk_callback(struct urb *urb); @@ -124,7 +123,6 @@ static struct usb_serial_driver empeg_device = { .throttle = empeg_throttle, .unthrottle = empeg_unthrottle, .attach = empeg_startup, - .shutdown = empeg_shutdown, .set_termios = empeg_set_termios, .write = empeg_write, .write_room = empeg_write_room, @@ -427,12 +425,6 @@ static int empeg_startup(struct usb_serial *serial) } -static void empeg_shutdown(struct usb_serial *serial) -{ - dbg("%s", __func__); -} - - static void empeg_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index fc423583eed..3dc3768ca71 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -720,7 +720,6 @@ static const char *ftdi_chip_name[] = { /* function prototypes for a FTDI serial converter */ static int ftdi_sio_probe(struct usb_serial *serial, const struct usb_device_id *id); -static void ftdi_shutdown(struct usb_serial *serial); static int ftdi_sio_port_probe(struct usb_serial_port *port); static int ftdi_sio_port_remove(struct usb_serial_port *port); static int ftdi_open(struct tty_struct *tty, @@ -779,7 +778,6 @@ static struct usb_serial_driver ftdi_sio_device = { .ioctl = ftdi_ioctl, .set_termios = ftdi_set_termios, .break_ctl = ftdi_break_ctl, - .shutdown = ftdi_shutdown, }; @@ -1594,18 +1592,6 @@ static int ftdi_mtxorb_hack_setup(struct usb_serial *serial) return 0; } -/* ftdi_shutdown is called from usbserial:usb_serial_disconnect - * it is called when the usb device is disconnected - * - * usbserial:usb_serial_disconnect - * calls __serial_close for each open of the port - * shutdown is called then (ie ftdi_shutdown) - */ -static void ftdi_shutdown(struct usb_serial *serial) -{ - dbg("%s", __func__); -} - static void ftdi_sio_priv_release(struct kref *k) { struct ftdi_private *priv = container_of(k, struct ftdi_private, kref); diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 5092d6aba65..8839f1c70b7 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1475,7 +1475,7 @@ static int garmin_attach(struct usb_serial *serial) } -static void garmin_shutdown(struct usb_serial *serial) +static void garmin_disconnect(struct usb_serial *serial) { struct usb_serial_port *port = serial->port[0]; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); @@ -1484,8 +1484,17 @@ static void garmin_shutdown(struct usb_serial *serial) usb_kill_urb(port->interrupt_in_urb); del_timer_sync(&garmin_data_p->timer); +} + + +static void garmin_release(struct usb_serial *serial) +{ + struct usb_serial_port *port = serial->port[0]; + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); + + dbg("%s", __func__); + kfree(garmin_data_p); - usb_set_serial_port_data(port, NULL); } @@ -1504,7 +1513,8 @@ static struct usb_serial_driver garmin_device = { .throttle = garmin_throttle, .unthrottle = garmin_unthrottle, .attach = garmin_attach, - .shutdown = garmin_shutdown, + .disconnect = garmin_disconnect, + .release = garmin_release, .write = garmin_write, .write_room = garmin_write_room, .write_bulk_callback = garmin_write_bulk_callback, diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 8dabac1929b..932d6241b78 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -63,7 +63,8 @@ struct usb_serial_driver usb_serial_generic_device = { .id_table = generic_device_ids, .usb_driver = &generic_driver, .num_ports = 1, - .shutdown = usb_serial_generic_shutdown, + .disconnect = usb_serial_generic_disconnect, + .release = usb_serial_generic_release, .throttle = usb_serial_generic_throttle, .unthrottle = usb_serial_generic_unthrottle, .resume = usb_serial_generic_resume, @@ -551,7 +552,7 @@ int usb_serial_handle_break(struct usb_serial_port *port) } EXPORT_SYMBOL_GPL(usb_serial_handle_break); -void usb_serial_generic_shutdown(struct usb_serial *serial) +void usb_serial_generic_disconnect(struct usb_serial *serial) { int i; @@ -562,3 +563,7 @@ void usb_serial_generic_shutdown(struct usb_serial *serial) generic_cleanup(serial->port[i]); } +void usb_serial_generic_release(struct usb_serial *serial) +{ + dbg("%s", __func__); +} diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 53ef5996e33..0191693625d 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -224,7 +224,8 @@ static int edge_tiocmget(struct tty_struct *tty, struct file *file); static int edge_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int edge_startup(struct usb_serial *serial); -static void edge_shutdown(struct usb_serial *serial); +static void edge_disconnect(struct usb_serial *serial); +static void edge_release(struct usb_serial *serial); #include "io_tables.h" /* all of the devices that this driver supports */ @@ -3193,21 +3194,16 @@ static int edge_startup(struct usb_serial *serial) /**************************************************************************** - * edge_shutdown + * edge_disconnect * This function is called whenever the device is removed from the usb bus. ****************************************************************************/ -static void edge_shutdown(struct usb_serial *serial) +static void edge_disconnect(struct usb_serial *serial) { struct edgeport_serial *edge_serial = usb_get_serial_data(serial); - int i; dbg("%s", __func__); /* stop reads and writes on all ports */ - for (i = 0; i < serial->num_ports; ++i) { - kfree(usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); - } /* free up our endpoint stuff */ if (edge_serial->is_epic) { usb_kill_urb(edge_serial->interrupt_read_urb); @@ -3218,9 +3214,24 @@ static void edge_shutdown(struct usb_serial *serial) usb_free_urb(edge_serial->read_urb); kfree(edge_serial->bulk_in_buffer); } +} + + +/**************************************************************************** + * edge_release + * This function is called when the device structure is deallocated. + ****************************************************************************/ +static void edge_release(struct usb_serial *serial) +{ + struct edgeport_serial *edge_serial = usb_get_serial_data(serial); + int i; + + dbg("%s", __func__); + + for (i = 0; i < serial->num_ports; ++i) + kfree(usb_get_serial_port_data(serial->port[i])); kfree(edge_serial); - usb_set_serial_data(serial, NULL); } diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h index 7eb9d67b81b..9241d314751 100644 --- a/drivers/usb/serial/io_tables.h +++ b/drivers/usb/serial/io_tables.h @@ -117,7 +117,8 @@ static struct usb_serial_driver edgeport_2port_device = { .throttle = edge_throttle, .unthrottle = edge_unthrottle, .attach = edge_startup, - .shutdown = edge_shutdown, + .disconnect = edge_disconnect, + .release = edge_release, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -145,7 +146,8 @@ static struct usb_serial_driver edgeport_4port_device = { .throttle = edge_throttle, .unthrottle = edge_unthrottle, .attach = edge_startup, - .shutdown = edge_shutdown, + .disconnect = edge_disconnect, + .release = edge_release, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -173,7 +175,8 @@ static struct usb_serial_driver edgeport_8port_device = { .throttle = edge_throttle, .unthrottle = edge_unthrottle, .attach = edge_startup, - .shutdown = edge_shutdown, + .disconnect = edge_disconnect, + .release = edge_release, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -200,7 +203,8 @@ static struct usb_serial_driver epic_device = { .throttle = edge_throttle, .unthrottle = edge_unthrottle, .attach = edge_startup, - .shutdown = edge_shutdown, + .disconnect = edge_disconnect, + .release = edge_release, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index db964db42d3..e8bc42f92e7 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2663,7 +2663,7 @@ cleanup: return -ENOMEM; } -static void edge_shutdown(struct usb_serial *serial) +static void edge_disconnect(struct usb_serial *serial) { int i; struct edgeport_port *edge_port; @@ -2673,12 +2673,22 @@ static void edge_shutdown(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { edge_port = usb_get_serial_port_data(serial->port[i]); edge_remove_sysfs_attrs(edge_port->port); + } +} + +static void edge_release(struct usb_serial *serial) +{ + int i; + struct edgeport_port *edge_port; + + dbg("%s", __func__); + + for (i = 0; i < serial->num_ports; ++i) { + edge_port = usb_get_serial_port_data(serial->port[i]); edge_buf_free(edge_port->ep_out_buf); kfree(edge_port); - usb_set_serial_port_data(serial->port[i], NULL); } kfree(usb_get_serial_data(serial)); - usb_set_serial_data(serial, NULL); } @@ -2915,7 +2925,8 @@ static struct usb_serial_driver edgeport_1port_device = { .throttle = edge_throttle, .unthrottle = edge_unthrottle, .attach = edge_startup, - .shutdown = edge_shutdown, + .disconnect = edge_disconnect, + .release = edge_release, .port_probe = edge_create_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, @@ -2944,7 +2955,8 @@ static struct usb_serial_driver edgeport_2port_device = { .throttle = edge_throttle, .unthrottle = edge_unthrottle, .attach = edge_startup, - .shutdown = edge_shutdown, + .disconnect = edge_disconnect, + .release = edge_release, .port_probe = edge_create_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index c610a99fa47..2545d45ce16 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -79,7 +79,6 @@ static int ipaq_open(struct tty_struct *tty, static void ipaq_close(struct usb_serial_port *port); static int ipaq_calc_num_ports(struct usb_serial *serial); static int ipaq_startup(struct usb_serial *serial); -static void ipaq_shutdown(struct usb_serial *serial); static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int ipaq_write_bulk(struct usb_serial_port *port, @@ -576,7 +575,6 @@ static struct usb_serial_driver ipaq_device = { .close = ipaq_close, .attach = ipaq_startup, .calc_num_ports = ipaq_calc_num_ports, - .shutdown = ipaq_shutdown, .write = ipaq_write, .write_room = ipaq_write_room, .chars_in_buffer = ipaq_chars_in_buffer, @@ -990,11 +988,6 @@ static int ipaq_startup(struct usb_serial *serial) return usb_reset_configuration(serial->dev); } -static void ipaq_shutdown(struct usb_serial *serial) -{ - dbg("%s", __func__); -} - static int __init ipaq_init(void) { int retval; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 76a3cc327bb..96873a7a32b 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -121,8 +121,8 @@ static int iuu_startup(struct usb_serial *serial) return 0; } -/* Shutdown function */ -static void iuu_shutdown(struct usb_serial *serial) +/* Release function */ +static void iuu_release(struct usb_serial *serial) { struct usb_serial_port *port = serial->port[0]; struct iuu_private *priv = usb_get_serial_port_data(port); @@ -1202,7 +1202,7 @@ static struct usb_serial_driver iuu_device = { .tiocmset = iuu_tiocmset, .set_termios = iuu_set_termios, .attach = iuu_startup, - .shutdown = iuu_shutdown, + .release = iuu_release, }; static int __init iuu_init(void) diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index f1195a98f31..2594b8743d3 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -2689,7 +2689,7 @@ static int keyspan_startup(struct usb_serial *serial) return 0; } -static void keyspan_shutdown(struct usb_serial *serial) +static void keyspan_disconnect(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; @@ -2729,6 +2729,17 @@ static void keyspan_shutdown(struct usb_serial *serial) usb_free_urb(p_priv->out_urbs[j]); } } +} + +static void keyspan_release(struct usb_serial *serial) +{ + int i; + struct usb_serial_port *port; + struct keyspan_serial_private *s_priv; + + dbg("%s", __func__); + + s_priv = usb_get_serial_data(serial); /* dbg("Freeing serial->private."); */ kfree(s_priv); diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 0d4569b6076..3107ed15af6 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -41,7 +41,8 @@ static int keyspan_open (struct tty_struct *tty, static void keyspan_close (struct usb_serial_port *port); static void keyspan_dtr_rts (struct usb_serial_port *port, int on); static int keyspan_startup (struct usb_serial *serial); -static void keyspan_shutdown (struct usb_serial *serial); +static void keyspan_disconnect (struct usb_serial *serial); +static void keyspan_release (struct usb_serial *serial); static int keyspan_write_room (struct tty_struct *tty); static int keyspan_write (struct tty_struct *tty, @@ -569,7 +570,8 @@ static struct usb_serial_driver keyspan_1port_device = { .tiocmget = keyspan_tiocmget, .tiocmset = keyspan_tiocmset, .attach = keyspan_startup, - .shutdown = keyspan_shutdown, + .disconnect = keyspan_disconnect, + .release = keyspan_release, }; static struct usb_serial_driver keyspan_2port_device = { @@ -590,7 +592,8 @@ static struct usb_serial_driver keyspan_2port_device = { .tiocmget = keyspan_tiocmget, .tiocmset = keyspan_tiocmset, .attach = keyspan_startup, - .shutdown = keyspan_shutdown, + .disconnect = keyspan_disconnect, + .release = keyspan_release, }; static struct usb_serial_driver keyspan_4port_device = { @@ -611,7 +614,8 @@ static struct usb_serial_driver keyspan_4port_device = { .tiocmget = keyspan_tiocmget, .tiocmset = keyspan_tiocmset, .attach = keyspan_startup, - .shutdown = keyspan_shutdown, + .disconnect = keyspan_disconnect, + .release = keyspan_release, }; #endif diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index ab769dbea1b..d0b12e40c2b 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -809,7 +809,7 @@ static int keyspan_pda_startup(struct usb_serial *serial) return 0; } -static void keyspan_pda_shutdown(struct usb_serial *serial) +static void keyspan_pda_release(struct usb_serial *serial) { dbg("%s", __func__); @@ -869,7 +869,7 @@ static struct usb_serial_driver keyspan_pda_device = { .tiocmget = keyspan_pda_tiocmget, .tiocmset = keyspan_pda_tiocmset, .attach = keyspan_pda_startup, - .shutdown = keyspan_pda_shutdown, + .release = keyspan_pda_release, }; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index fa817c66b3e..0f44bb8e8d4 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -73,7 +73,8 @@ static int debug; * Function prototypes */ static int klsi_105_startup(struct usb_serial *serial); -static void klsi_105_shutdown(struct usb_serial *serial); +static void klsi_105_disconnect(struct usb_serial *serial); +static void klsi_105_release(struct usb_serial *serial); static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void klsi_105_close(struct usb_serial_port *port); @@ -131,7 +132,8 @@ static struct usb_serial_driver kl5kusb105d_device = { .tiocmget = klsi_105_tiocmget, .tiocmset = klsi_105_tiocmset, .attach = klsi_105_startup, - .shutdown = klsi_105_shutdown, + .disconnect = klsi_105_disconnect, + .release = klsi_105_release, .throttle = klsi_105_throttle, .unthrottle = klsi_105_unthrottle, }; @@ -315,7 +317,7 @@ err_cleanup: } /* klsi_105_startup */ -static void klsi_105_shutdown(struct usb_serial *serial) +static void klsi_105_disconnect(struct usb_serial *serial) { int i; @@ -325,33 +327,36 @@ static void klsi_105_shutdown(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { struct klsi_105_private *priv = usb_get_serial_port_data(serial->port[i]); - unsigned long flags; if (priv) { /* kill our write urb pool */ int j; struct urb **write_urbs = priv->write_urb_pool; - spin_lock_irqsave(&priv->lock, flags); for (j = 0; j < NUM_URBS; j++) { if (write_urbs[j]) { - /* FIXME - uncomment the following - * usb_kill_urb call when the host - * controllers get fixed to set - * urb->dev = NULL after the urb is - * finished. Otherwise this call - * oopses. */ - /* usb_kill_urb(write_urbs[j]); */ - kfree(write_urbs[j]->transfer_buffer); + usb_kill_urb(write_urbs[j]); usb_free_urb(write_urbs[j]); } } - spin_unlock_irqrestore(&priv->lock, flags); - kfree(priv); - usb_set_serial_port_data(serial->port[i], NULL); } } -} /* klsi_105_shutdown */ +} /* klsi_105_disconnect */ + + +static void klsi_105_release(struct usb_serial *serial) +{ + int i; + + dbg("%s", __func__); + + for (i = 0; i < serial->num_ports; ++i) { + struct klsi_105_private *priv = + usb_get_serial_port_data(serial->port[i]); + + kfree(priv); + } +} /* klsi_105_release */ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 6b570498287..6db0e561f68 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -69,7 +69,7 @@ static int debug; /* Function prototypes */ static int kobil_startup(struct usb_serial *serial); -static void kobil_shutdown(struct usb_serial *serial); +static void kobil_release(struct usb_serial *serial); static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void kobil_close(struct usb_serial_port *port); @@ -117,7 +117,7 @@ static struct usb_serial_driver kobil_device = { .id_table = id_table, .num_ports = 1, .attach = kobil_startup, - .shutdown = kobil_shutdown, + .release = kobil_release, .ioctl = kobil_ioctl, .set_termios = kobil_set_termios, .tiocmget = kobil_tiocmget, @@ -201,17 +201,13 @@ static int kobil_startup(struct usb_serial *serial) } -static void kobil_shutdown(struct usb_serial *serial) +static void kobil_release(struct usb_serial *serial) { int i; dbg("%s - port %d", __func__, serial->port[0]->number); - for (i = 0; i < serial->num_ports; ++i) { - while (serial->port[i]->port.count > 0) - kobil_close(serial->port[i]); + for (i = 0; i < serial->num_ports; ++i) kfree(usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); - } } diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 873795548fc..d8825e159aa 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -92,7 +92,7 @@ static int debug; * Function prototypes */ static int mct_u232_startup(struct usb_serial *serial); -static void mct_u232_shutdown(struct usb_serial *serial); +static void mct_u232_release(struct usb_serial *serial); static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void mct_u232_close(struct usb_serial_port *port); @@ -149,7 +149,7 @@ static struct usb_serial_driver mct_u232_device = { .tiocmget = mct_u232_tiocmget, .tiocmset = mct_u232_tiocmset, .attach = mct_u232_startup, - .shutdown = mct_u232_shutdown, + .release = mct_u232_release, }; @@ -407,7 +407,7 @@ static int mct_u232_startup(struct usb_serial *serial) } /* mct_u232_startup */ -static void mct_u232_shutdown(struct usb_serial *serial) +static void mct_u232_release(struct usb_serial *serial) { struct mct_u232_private *priv; int i; @@ -417,12 +417,9 @@ static void mct_u232_shutdown(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { /* My special items, the standard routines free my urbs */ priv = usb_get_serial_port_data(serial->port[i]); - if (priv) { - usb_set_serial_port_data(serial->port[i], NULL); - kfree(priv); - } + kfree(priv); } -} /* mct_u232_shutdown */ +} /* mct_u232_release */ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 9e1a013ee7f..bfc5ce000ef 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1521,19 +1521,16 @@ static int mos7720_startup(struct usb_serial *serial) return 0; } -static void mos7720_shutdown(struct usb_serial *serial) +static void mos7720_release(struct usb_serial *serial) { int i; /* free private structure allocated for serial port */ - for (i = 0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) kfree(usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); - } /* free private structure allocated for serial device */ kfree(usb_get_serial_data(serial)); - usb_set_serial_data(serial, NULL); } static struct usb_driver usb_driver = { @@ -1558,7 +1555,7 @@ static struct usb_serial_driver moschip7720_2port_driver = { .throttle = mos7720_throttle, .unthrottle = mos7720_unthrottle, .attach = mos7720_startup, - .shutdown = mos7720_shutdown, + .release = mos7720_release, .ioctl = mos7720_ioctl, .set_termios = mos7720_set_termios, .write = mos7720_write, diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 5fe9fe3df77..c40f95c1951 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2633,16 +2633,16 @@ error: } /**************************************************************************** - * mos7840_shutdown + * mos7840_disconnect * This function is called whenever the device is removed from the usb bus. ****************************************************************************/ -static void mos7840_shutdown(struct usb_serial *serial) +static void mos7840_disconnect(struct usb_serial *serial) { int i; unsigned long flags; struct moschip_port *mos7840_port; - dbg("%s", " shutdown :entering.........."); + dbg("%s", " disconnect :entering.........."); if (!serial) { dbg("%s", "Invalid Handler"); @@ -2662,11 +2662,42 @@ static void mos7840_shutdown(struct usb_serial *serial) mos7840_port->zombie = 1; spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); usb_kill_urb(mos7840_port->control_urb); + } + } + + dbg("%s", "Thank u :: "); + +} + +/**************************************************************************** + * mos7840_release + * This function is called when the usb_serial structure is freed. + ****************************************************************************/ + +static void mos7840_release(struct usb_serial *serial) +{ + int i; + struct moschip_port *mos7840_port; + dbg("%s", " release :entering.........."); + + if (!serial) { + dbg("%s", "Invalid Handler"); + return; + } + + /* check for the ports to be closed,close the ports and disconnect */ + + /* free private structure allocated for serial port * + * stop reads and writes on all ports */ + + for (i = 0; i < serial->num_ports; ++i) { + mos7840_port = mos7840_get_port_private(serial->port[i]); + dbg("mos7840_port %d = %p", i, mos7840_port); + if (mos7840_port) { kfree(mos7840_port->ctrl_buf); kfree(mos7840_port->dr); kfree(mos7840_port); } - mos7840_set_port_private(serial->port[i], NULL); } dbg("%s", "Thank u :: "); @@ -2707,7 +2738,8 @@ static struct usb_serial_driver moschip7840_4port_device = { .tiocmget = mos7840_tiocmget, .tiocmset = mos7840_tiocmset, .attach = mos7840_startup, - .shutdown = mos7840_shutdown, + .disconnect = mos7840_disconnect, + .release = mos7840_release, .read_bulk_callback = mos7840_bulk_in_callback, .read_int_callback = mos7840_interrupt_callback, }; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 1104617334f..56857ddbd70 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -72,7 +72,8 @@ static void omninet_write_bulk_callback(struct urb *urb); static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int omninet_write_room(struct tty_struct *tty); -static void omninet_shutdown(struct usb_serial *serial); +static void omninet_disconnect(struct usb_serial *serial); +static void omninet_release(struct usb_serial *serial); static int omninet_attach(struct usb_serial *serial); static struct usb_device_id id_table[] = { @@ -108,7 +109,8 @@ static struct usb_serial_driver zyxel_omninet_device = { .write_room = omninet_write_room, .read_bulk_callback = omninet_read_bulk_callback, .write_bulk_callback = omninet_write_bulk_callback, - .shutdown = omninet_shutdown, + .disconnect = omninet_disconnect, + .release = omninet_release, }; @@ -345,13 +347,22 @@ static void omninet_write_bulk_callback(struct urb *urb) } -static void omninet_shutdown(struct usb_serial *serial) +static void omninet_disconnect(struct usb_serial *serial) { struct usb_serial_port *wport = serial->port[1]; - struct usb_serial_port *port = serial->port[0]; + dbg("%s", __func__); usb_kill_urb(wport->write_urb); +} + + +static void omninet_release(struct usb_serial *serial) +{ + struct usb_serial_port *port = serial->port[0]; + + dbg("%s", __func__); + kfree(usb_get_serial_port_data(port)); } diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index c20480aa975..336bba79ad3 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -463,7 +463,7 @@ error: return retval; } -static void opticon_shutdown(struct usb_serial *serial) +static void opticon_disconnect(struct usb_serial *serial) { struct opticon_private *priv = usb_get_serial_data(serial); @@ -471,9 +471,16 @@ static void opticon_shutdown(struct usb_serial *serial) usb_kill_urb(priv->bulk_read_urb); usb_free_urb(priv->bulk_read_urb); +} + +static void opticon_release(struct usb_serial *serial) +{ + struct opticon_private *priv = usb_get_serial_data(serial); + + dbg("%s", __func__); + kfree(priv->bulk_in_buffer); kfree(priv); - usb_set_serial_data(serial, NULL); } static int opticon_suspend(struct usb_interface *intf, pm_message_t message) @@ -524,7 +531,8 @@ static struct usb_serial_driver opticon_device = { .close = opticon_close, .write = opticon_write, .write_room = opticon_write_room, - .shutdown = opticon_shutdown, + .disconnect = opticon_disconnect, + .release = opticon_release, .throttle = opticon_throttle, .unthrottle = opticon_unthrottle, .ioctl = opticon_ioctl, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index a38971cc373..575816e6ba3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -51,7 +51,8 @@ static void option_close(struct usb_serial_port *port); static void option_dtr_rts(struct usb_serial_port *port, int on); static int option_startup(struct usb_serial *serial); -static void option_shutdown(struct usb_serial *serial); +static void option_disconnect(struct usb_serial *serial); +static void option_release(struct usb_serial *serial); static int option_write_room(struct tty_struct *tty); static void option_instat_callback(struct urb *urb); @@ -568,7 +569,8 @@ static struct usb_serial_driver option_1port_device = { .tiocmget = option_tiocmget, .tiocmset = option_tiocmset, .attach = option_startup, - .shutdown = option_shutdown, + .disconnect = option_disconnect, + .release = option_release, .read_int_callback = option_instat_callback, .suspend = option_suspend, .resume = option_resume, @@ -1149,7 +1151,14 @@ static void stop_read_write_urbs(struct usb_serial *serial) } } -static void option_shutdown(struct usb_serial *serial) +static void option_disconnect(struct usb_serial *serial) +{ + dbg("%s", __func__); + + stop_read_write_urbs(serial); +} + +static void option_release(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; @@ -1157,8 +1166,6 @@ static void option_shutdown(struct usb_serial *serial) dbg("%s", __func__); - stop_read_write_urbs(serial); - /* Now free them */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 7de54781fe6..3cece27325e 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -159,7 +159,7 @@ static int oti6858_tiocmget(struct tty_struct *tty, struct file *file); static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int oti6858_startup(struct usb_serial *serial); -static void oti6858_shutdown(struct usb_serial *serial); +static void oti6858_release(struct usb_serial *serial); /* functions operating on buffers */ static struct oti6858_buf *oti6858_buf_alloc(unsigned int size); @@ -194,7 +194,7 @@ static struct usb_serial_driver oti6858_device = { .write_room = oti6858_write_room, .chars_in_buffer = oti6858_chars_in_buffer, .attach = oti6858_startup, - .shutdown = oti6858_shutdown, + .release = oti6858_release, }; struct oti6858_private { @@ -782,7 +782,7 @@ static int oti6858_ioctl(struct tty_struct *tty, struct file *file, } -static void oti6858_shutdown(struct usb_serial *serial) +static void oti6858_release(struct usb_serial *serial) { struct oti6858_private *priv; int i; @@ -794,7 +794,6 @@ static void oti6858_shutdown(struct usb_serial *serial) if (priv) { oti6858_buf_free(priv->buf); kfree(priv); - usb_set_serial_port_data(serial->port[i], NULL); } } } diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 6357b57f628..ec6c132a25b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -878,7 +878,7 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state) dbg("%s - error sending break = %d", __func__, result); } -static void pl2303_shutdown(struct usb_serial *serial) +static void pl2303_release(struct usb_serial *serial) { int i; struct pl2303_private *priv; @@ -890,7 +890,6 @@ static void pl2303_shutdown(struct usb_serial *serial) if (priv) { pl2303_buf_free(priv->buf); kfree(priv); - usb_set_serial_port_data(serial->port[i], NULL); } } } @@ -1123,7 +1122,7 @@ static struct usb_serial_driver pl2303_device = { .write_room = pl2303_write_room, .chars_in_buffer = pl2303_chars_in_buffer, .attach = pl2303_startup, - .shutdown = pl2303_shutdown, + .release = pl2303_release, }; static int __init pl2303_init(void) diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index a88cde95016..032f7aeb40a 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -814,7 +814,7 @@ static int sierra_startup(struct usb_serial *serial) return 0; } -static void sierra_shutdown(struct usb_serial *serial) +static void sierra_disconnect(struct usb_serial *serial) { int i; struct usb_serial_port *port; @@ -853,7 +853,7 @@ static struct usb_serial_driver sierra_device = { .tiocmget = sierra_tiocmget, .tiocmset = sierra_tiocmset, .attach = sierra_startup, - .shutdown = sierra_shutdown, + .disconnect = sierra_disconnect, .read_int_callback = sierra_instat_callback, }; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 8f7ed8f1399..3c249d8e8b8 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -356,7 +356,7 @@ cleanup: } /* call when the device plug out. free all the memory alloced by probe */ -static void spcp8x5_shutdown(struct usb_serial *serial) +static void spcp8x5_release(struct usb_serial *serial) { int i; struct spcp8x5_private *priv; @@ -366,7 +366,6 @@ static void spcp8x5_shutdown(struct usb_serial *serial) if (priv) { free_ringbuf(priv->buf); kfree(priv); - usb_set_serial_port_data(serial->port[i] , NULL); } } } @@ -1020,7 +1019,7 @@ static struct usb_serial_driver spcp8x5_device = { .write_bulk_callback = spcp8x5_write_bulk_callback, .chars_in_buffer = spcp8x5_chars_in_buffer, .attach = spcp8x5_startup, - .shutdown = spcp8x5_shutdown, + .release = spcp8x5_release, }; static int __init spcp8x5_init(void) diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 8b07ebc6bae..6157fac9366 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -267,7 +267,7 @@ error: return retval; } -static void symbol_shutdown(struct usb_serial *serial) +static void symbol_disconnect(struct usb_serial *serial) { struct symbol_private *priv = usb_get_serial_data(serial); @@ -275,9 +275,16 @@ static void symbol_shutdown(struct usb_serial *serial) usb_kill_urb(priv->int_urb); usb_free_urb(priv->int_urb); +} + +static void symbol_release(struct usb_serial *serial) +{ + struct symbol_private *priv = usb_get_serial_data(serial); + + dbg("%s", __func__); + kfree(priv->int_buffer); kfree(priv); - usb_set_serial_data(serial, NULL); } static struct usb_driver symbol_driver = { @@ -299,7 +306,8 @@ static struct usb_serial_driver symbol_device = { .attach = symbol_startup, .open = symbol_open, .close = symbol_close, - .shutdown = symbol_shutdown, + .disconnect = symbol_disconnect, + .release = symbol_release, .throttle = symbol_throttle, .unthrottle = symbol_unthrottle, }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 42cb04c403b..991d8232e37 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -97,7 +97,7 @@ struct ti_device { /* Function Declarations */ static int ti_startup(struct usb_serial *serial); -static void ti_shutdown(struct usb_serial *serial); +static void ti_release(struct usb_serial *serial); static int ti_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *file); static void ti_close(struct usb_serial_port *port); @@ -230,7 +230,7 @@ static struct usb_serial_driver ti_1port_device = { .id_table = ti_id_table_3410, .num_ports = 1, .attach = ti_startup, - .shutdown = ti_shutdown, + .release = ti_release, .open = ti_open, .close = ti_close, .write = ti_write, @@ -258,7 +258,7 @@ static struct usb_serial_driver ti_2port_device = { .id_table = ti_id_table_5052, .num_ports = 2, .attach = ti_startup, - .shutdown = ti_shutdown, + .release = ti_release, .open = ti_open, .close = ti_close, .write = ti_write, @@ -473,7 +473,7 @@ free_tdev: } -static void ti_shutdown(struct usb_serial *serial) +static void ti_release(struct usb_serial *serial) { int i; struct ti_device *tdev = usb_get_serial_data(serial); @@ -486,12 +486,10 @@ static void ti_shutdown(struct usb_serial *serial) if (tport) { ti_buf_free(tport->tp_write_buf); kfree(tport); - usb_set_serial_port_data(serial->port[i], NULL); } } kfree(tdev); - usb_set_serial_data(serial, NULL); } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index da890f030fa..d595aa5586a 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -141,6 +141,14 @@ static void destroy_serial(struct kref *kref) if (serial->minor != SERIAL_TTY_NO_MINOR) return_serial(serial); + serial->type->release(serial); + + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + if (port) + put_device(&port->dev); + } + /* 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 */ @@ -148,9 +156,8 @@ static void destroy_serial(struct kref *kref) for (i = serial->num_ports; i < serial->num_port_pointers; ++i) { port = serial->port[i]; - if (!port) - continue; - port_free(port); + if (port) + port_free(port); } } @@ -1118,10 +1125,6 @@ void usb_serial_disconnect(struct usb_interface *interface) serial->disconnected = 1; mutex_unlock(&serial->disc_mutex); - /* Unfortunately, many of the sub-drivers expect the port structures - * to exist when their shutdown method is called, so we have to go - * through this awkward two-step unregistration procedure. - */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) { @@ -1153,14 +1156,7 @@ void usb_serial_disconnect(struct usb_interface *interface) } } } - serial->type->shutdown(serial); - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - if (port) { - put_device(&port->dev); - serial->port[i] = NULL; - } - } + serial->type->disconnect(serial); /* let the last holder of this object * cause it to be cleaned up */ @@ -1338,7 +1334,8 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, chars_in_buffer); set_to_generic_if_null(device, read_bulk_callback); set_to_generic_if_null(device, write_bulk_callback); - set_to_generic_if_null(device, shutdown); + set_to_generic_if_null(device, disconnect); + set_to_generic_if_null(device, release); } int usb_serial_register(struct usb_serial_driver *driver) diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index b15f1c0e1d4..f5d0f64dcc5 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -47,7 +47,7 @@ static void visor_unthrottle(struct tty_struct *tty); static int visor_probe(struct usb_serial *serial, const struct usb_device_id *id); static int visor_calc_num_ports(struct usb_serial *serial); -static void visor_shutdown(struct usb_serial *serial); +static void visor_release(struct usb_serial *serial); static void visor_write_bulk_callback(struct urb *urb); static void visor_read_bulk_callback(struct urb *urb); static void visor_read_int_callback(struct urb *urb); @@ -202,7 +202,7 @@ static struct usb_serial_driver handspring_device = { .attach = treo_attach, .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, - .shutdown = visor_shutdown, + .release = visor_release, .write = visor_write, .write_room = visor_write_room, .write_bulk_callback = visor_write_bulk_callback, @@ -227,7 +227,7 @@ static struct usb_serial_driver clie_5_device = { .attach = clie_5_attach, .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, - .shutdown = visor_shutdown, + .release = visor_release, .write = visor_write, .write_room = visor_write_room, .write_bulk_callback = visor_write_bulk_callback, @@ -918,7 +918,7 @@ static int clie_5_attach(struct usb_serial *serial) return generic_startup(serial); } -static void visor_shutdown(struct usb_serial *serial) +static void visor_release(struct usb_serial *serial) { struct visor_private *priv; int i; @@ -927,10 +927,7 @@ static void visor_shutdown(struct usb_serial *serial) for (i = 0; i < serial->num_ports; i++) { priv = usb_get_serial_port_data(serial->port[i]); - if (priv) { - usb_set_serial_port_data(serial->port[i], NULL); - kfree(priv); - } + kfree(priv); } } diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 7c7295d09f3..8d126dd7a02 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -144,7 +144,7 @@ static int whiteheat_firmware_attach(struct usb_serial *serial); /* function prototypes for the Connect Tech WhiteHEAT serial converter */ static int whiteheat_attach(struct usb_serial *serial); -static void whiteheat_shutdown(struct usb_serial *serial); +static void whiteheat_release(struct usb_serial *serial); static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void whiteheat_close(struct usb_serial_port *port); @@ -189,7 +189,7 @@ static struct usb_serial_driver whiteheat_device = { .id_table = id_table_std, .num_ports = 4, .attach = whiteheat_attach, - .shutdown = whiteheat_shutdown, + .release = whiteheat_release, .open = whiteheat_open, .close = whiteheat_close, .write = whiteheat_write, @@ -617,7 +617,7 @@ no_command_buffer: } -static void whiteheat_shutdown(struct usb_serial *serial) +static void whiteheat_release(struct usb_serial *serial) { struct usb_serial_port *command_port; struct usb_serial_port *port; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index ed4aa0fa7ed..44801d26a37 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -194,8 +194,10 @@ static inline void usb_set_serial_data(struct usb_serial *serial, void *data) * This will be called when the struct usb_serial structure is fully set * set up. Do any local initialization of the device, or any private * memory structure allocation at this point in time. - * @shutdown: pointer to the driver's shutdown function. This will be - * called when the device is removed from the system. + * @disconnect: pointer to the driver's disconnect function. This will be + * called when the device is unplugged or unbound from the driver. + * @release: pointer to the driver's release function. This will be called + * when the usb_serial data structure is about to be destroyed. * @usb_driver: pointer to the struct usb_driver that controls this * device. This is necessary to allow dynamic ids to be added to * the driver from sysfs. @@ -226,7 +228,8 @@ struct usb_serial_driver { int (*attach)(struct usb_serial *serial); int (*calc_num_ports) (struct usb_serial *serial); - void (*shutdown)(struct usb_serial *serial); + void (*disconnect)(struct usb_serial *serial); + void (*release)(struct usb_serial *serial); int (*port_probe)(struct usb_serial_port *port); int (*port_remove)(struct usb_serial_port *port); @@ -308,7 +311,8 @@ extern void usb_serial_generic_read_bulk_callback(struct urb *urb); extern void usb_serial_generic_write_bulk_callback(struct urb *urb); extern void usb_serial_generic_throttle(struct tty_struct *tty); extern void usb_serial_generic_unthrottle(struct tty_struct *tty); -extern void usb_serial_generic_shutdown(struct usb_serial *serial); +extern void usb_serial_generic_disconnect(struct usb_serial *serial); +extern void usb_serial_generic_release(struct usb_serial *serial); extern int usb_serial_generic_register(int debug); extern void usb_serial_generic_deregister(void); extern void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port, -- cgit v1.2.3-70-g09d2 From 5be19a9daa2df2507adf5b4676a7db8d131cf56e Mon Sep 17 00:00:00 2001 From: Xiaochen Shen Date: Thu, 4 Jun 2009 15:34:49 +0800 Subject: USB: Add Intel Langwell USB Device Controller driver Intel Langwell USB Device Controller is a High-Speed USB OTG device controller in Intel Moorestown platform. It can work in OTG device mode with Intel Langwell USB OTG transceiver driver as well as device-only mode. The number of programmable endpoints is different through controller revision. NOTE: This patch is the first version Intel Langwell USB OTG device controller driver. The bug fixing is on going for some hardware and software issues. Intel Langwell USB OTG transceiver driver and EHCI driver patches will be submitted later. Supported features: - USB OTG protocol support with Intel Langwell USB OTG transceiver driver (turn on CONFIG_USB_LANGWELL_OTG) - Support control, bulk, interrupt and isochronous endpoints (isochronous not tested) - PCI D0/D3 power management support - Link Power Management (LPM) support Tested gadget drivers: - g_file_storage - g_ether - g_zero The passed tests: - g_file_storage: USBCV Chapter 9 tests - g_file_storage: USBCV MSC tests - g_file_storage: from/to host files copying - g_ether: ping, ftp and scp files from/to host - Hotplug, with and without hubs Known issues: - g_ether: failed part of USBCV chap9 tests - LPM support not fully tested TODO: - g_ether: pass all USBCV chap9 tests - g_zero: pass usbtest tests - Stress tests on different gadget drivers - On-chip private SRAM caching support Signed-off-by: Xiaochen Shen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 21 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gadget_chips.h | 8 + drivers/usb/gadget/langwell_udc.c | 3373 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/langwell_udc.h | 228 +++ include/linux/usb/langwell_udc.h | 310 ++++ 6 files changed, 3941 insertions(+) create mode 100644 drivers/usb/gadget/langwell_udc.c create mode 100644 drivers/usb/gadget/langwell_udc.h create mode 100644 include/linux/usb/langwell_udc.h (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ef9d9086cdd..5d1ddf485d1 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -474,6 +474,27 @@ config USB_GOKU default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_LANGWELL + boolean "Intel Langwell USB Device Controller" + depends on PCI + select USB_GADGET_DUALSPEED + help + Intel Langwell USB Device Controller is a High-Speed USB + On-The-Go device controller. + + The number of programmable endpoints is different through + controller revision. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "langwell_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_LANGWELL + tristate + depends on USB_GADGET_LANGWELL + default USB_GADGET + select USB_GADGET_SELECTED + # # LAST -- dummy/emulated controller diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index fc9a7dc0104..e6017e6bf6d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o +obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index ec6d439a2aa..8e0e9a0b736 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -137,6 +137,12 @@ #define gadget_is_musbhdrc(g) 0 #endif +#ifdef CONFIG_USB_GADGET_LANGWELL +#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name)) +#else +#define gadget_is_langwell(g) 0 +#endif + /* from Montavista kernel (?) */ #ifdef CONFIG_USB_GADGET_MPC8272 #define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name) @@ -231,6 +237,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x22; else if (gadget_is_ci13xxx(gadget)) return 0x23; + else if (gadget_is_langwell(gadget)) + return 0x24; return -ENOENT; } diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c new file mode 100644 index 00000000000..6829d596135 --- /dev/null +++ b/drivers/usb/gadget/langwell_udc.c @@ -0,0 +1,3373 @@ +/* + * Intel Langwell USB Device Controller driver + * Copyright (C) 2008-2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +/* #undef DEBUG */ +/* #undef VERBOSE */ + +#if defined(CONFIG_USB_LANGWELL_OTG) +#define OTG_TRANSCEIVER +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "langwell_udc.h" + + +#define DRIVER_DESC "Intel Langwell USB Device Controller driver" +#define DRIVER_VERSION "16 May 2009" + +static const char driver_name[] = "langwell_udc"; +static const char driver_desc[] = DRIVER_DESC; + + +/* controller device global variable */ +static struct langwell_udc *the_controller; + +/* for endpoint 0 operations */ +static const struct usb_endpoint_descriptor +langwell_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = EP0_MAX_PKT_SIZE, +}; + + +/*-------------------------------------------------------------------------*/ +/* debugging */ + +#ifdef DEBUG +#define DBG(dev, fmt, args...) \ + pr_debug("%s %s: " fmt , driver_name, \ + pci_name(dev->pdev), ## args) +#else +#define DBG(dev, fmt, args...) \ + do { } while (0) +#endif /* DEBUG */ + + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(dev, fmt, args...) \ + do { } while (0) +#endif /* VERBOSE */ + + +#define ERROR(dev, fmt, args...) \ + pr_err("%s %s: " fmt , driver_name, \ + pci_name(dev->pdev), ## args) + +#define WARNING(dev, fmt, args...) \ + pr_warning("%s %s: " fmt , driver_name, \ + pci_name(dev->pdev), ## args) + +#define INFO(dev, fmt, args...) \ + pr_info("%s %s: " fmt , driver_name, \ + pci_name(dev->pdev), ## args) + + +#ifdef VERBOSE +static inline void print_all_registers(struct langwell_udc *dev) +{ + int i; + + /* Capability Registers */ + printk(KERN_DEBUG "Capability Registers (offset: " + "0x%04x, length: 0x%08x)\n", + CAP_REG_OFFSET, + (u32)sizeof(struct langwell_cap_regs)); + printk(KERN_DEBUG "caplength=0x%02x\n", + readb(&dev->cap_regs->caplength)); + printk(KERN_DEBUG "hciversion=0x%04x\n", + readw(&dev->cap_regs->hciversion)); + printk(KERN_DEBUG "hcsparams=0x%08x\n", + readl(&dev->cap_regs->hcsparams)); + printk(KERN_DEBUG "hccparams=0x%08x\n", + readl(&dev->cap_regs->hccparams)); + printk(KERN_DEBUG "dciversion=0x%04x\n", + readw(&dev->cap_regs->dciversion)); + printk(KERN_DEBUG "dccparams=0x%08x\n", + readl(&dev->cap_regs->dccparams)); + + /* Operational Registers */ + printk(KERN_DEBUG "Operational Registers (offset: " + "0x%04x, length: 0x%08x)\n", + OP_REG_OFFSET, + (u32)sizeof(struct langwell_op_regs)); + printk(KERN_DEBUG "extsts=0x%08x\n", + readl(&dev->op_regs->extsts)); + printk(KERN_DEBUG "extintr=0x%08x\n", + readl(&dev->op_regs->extintr)); + printk(KERN_DEBUG "usbcmd=0x%08x\n", + readl(&dev->op_regs->usbcmd)); + printk(KERN_DEBUG "usbsts=0x%08x\n", + readl(&dev->op_regs->usbsts)); + printk(KERN_DEBUG "usbintr=0x%08x\n", + readl(&dev->op_regs->usbintr)); + printk(KERN_DEBUG "frindex=0x%08x\n", + readl(&dev->op_regs->frindex)); + printk(KERN_DEBUG "ctrldssegment=0x%08x\n", + readl(&dev->op_regs->ctrldssegment)); + printk(KERN_DEBUG "deviceaddr=0x%08x\n", + readl(&dev->op_regs->deviceaddr)); + printk(KERN_DEBUG "endpointlistaddr=0x%08x\n", + readl(&dev->op_regs->endpointlistaddr)); + printk(KERN_DEBUG "ttctrl=0x%08x\n", + readl(&dev->op_regs->ttctrl)); + printk(KERN_DEBUG "burstsize=0x%08x\n", + readl(&dev->op_regs->burstsize)); + printk(KERN_DEBUG "txfilltuning=0x%08x\n", + readl(&dev->op_regs->txfilltuning)); + printk(KERN_DEBUG "txttfilltuning=0x%08x\n", + readl(&dev->op_regs->txttfilltuning)); + printk(KERN_DEBUG "ic_usb=0x%08x\n", + readl(&dev->op_regs->ic_usb)); + printk(KERN_DEBUG "ulpi_viewport=0x%08x\n", + readl(&dev->op_regs->ulpi_viewport)); + printk(KERN_DEBUG "configflag=0x%08x\n", + readl(&dev->op_regs->configflag)); + printk(KERN_DEBUG "portsc1=0x%08x\n", + readl(&dev->op_regs->portsc1)); + printk(KERN_DEBUG "devlc=0x%08x\n", + readl(&dev->op_regs->devlc)); + printk(KERN_DEBUG "otgsc=0x%08x\n", + readl(&dev->op_regs->otgsc)); + printk(KERN_DEBUG "usbmode=0x%08x\n", + readl(&dev->op_regs->usbmode)); + printk(KERN_DEBUG "endptnak=0x%08x\n", + readl(&dev->op_regs->endptnak)); + printk(KERN_DEBUG "endptnaken=0x%08x\n", + readl(&dev->op_regs->endptnaken)); + printk(KERN_DEBUG "endptsetupstat=0x%08x\n", + readl(&dev->op_regs->endptsetupstat)); + printk(KERN_DEBUG "endptprime=0x%08x\n", + readl(&dev->op_regs->endptprime)); + printk(KERN_DEBUG "endptflush=0x%08x\n", + readl(&dev->op_regs->endptflush)); + printk(KERN_DEBUG "endptstat=0x%08x\n", + readl(&dev->op_regs->endptstat)); + printk(KERN_DEBUG "endptcomplete=0x%08x\n", + readl(&dev->op_regs->endptcomplete)); + + for (i = 0; i < dev->ep_max / 2; i++) { + printk(KERN_DEBUG "endptctrl[%d]=0x%08x\n", + i, readl(&dev->op_regs->endptctrl[i])); + } +} +#endif /* VERBOSE */ + + +/*-------------------------------------------------------------------------*/ + +#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") + +#define is_in(ep) (((ep)->ep_num == 0) ? ((ep)->dev->ep0_dir == \ + USB_DIR_IN) : ((ep)->desc->bEndpointAddress \ + & USB_DIR_IN) == USB_DIR_IN) + + +#ifdef DEBUG +static char *type_string(u8 bmAttributes) +{ + switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + return "bulk"; + case USB_ENDPOINT_XFER_ISOC: + return "iso"; + case USB_ENDPOINT_XFER_INT: + return "int"; + }; + + return "control"; +} +#endif + + +/* configure endpoint control registers */ +static void ep_reset(struct langwell_ep *ep, unsigned char ep_num, + unsigned char is_in, unsigned char ep_type) +{ + struct langwell_udc *dev; + u32 endptctrl; + + dev = ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); + if (is_in) { /* TX */ + if (ep_num) + endptctrl |= EPCTRL_TXR; + endptctrl |= EPCTRL_TXE; + endptctrl |= ep_type << EPCTRL_TXT_SHIFT; + } else { /* RX */ + if (ep_num) + endptctrl |= EPCTRL_RXR; + endptctrl |= EPCTRL_RXE; + endptctrl |= ep_type << EPCTRL_RXT_SHIFT; + } + + writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); + + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* reset ep0 dQH and endptctrl */ +static void ep0_reset(struct langwell_udc *dev) +{ + struct langwell_ep *ep; + int i; + + VDBG(dev, "---> %s()\n", __func__); + + /* ep0 in and out */ + for (i = 0; i < 2; i++) { + ep = &dev->ep[i]; + ep->dev = dev; + + /* ep0 dQH */ + ep->dqh = &dev->ep_dqh[i]; + + /* configure ep0 endpoint capabilities in dQH */ + ep->dqh->dqh_ios = 1; + ep->dqh->dqh_mpl = EP0_MAX_PKT_SIZE; + + /* FIXME: enable ep0-in HW zero length termination select */ + if (is_in(ep)) + ep->dqh->dqh_zlt = 0; + ep->dqh->dqh_mult = 0; + + /* configure ep0 control registers */ + ep_reset(&dev->ep[0], 0, i, USB_ENDPOINT_XFER_CONTROL); + } + + VDBG(dev, "<--- %s()\n", __func__); + return; +} + + +/*-------------------------------------------------------------------------*/ + +/* endpoints operations */ + +/* configure endpoint, making it usable */ +static int langwell_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct langwell_udc *dev; + struct langwell_ep *ep; + u16 max = 0; + unsigned long flags; + int retval = 0; + unsigned char zlt, ios = 0, mult = 0; + + ep = container_of(_ep, struct langwell_ep, ep); + dev = ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + if (!_ep || !desc || ep->desc + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize); + + /* + * disable HW zero length termination select + * driver handles zero length packet through req->req.zero + */ + zlt = 1; + + /* + * sanity check type, direction, address, and then + * initialize the endpoint capabilities fields in dQH + */ + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + ios = 1; + break; + case USB_ENDPOINT_XFER_BULK: + if ((dev->gadget.speed == USB_SPEED_HIGH + && max != 512) + || (dev->gadget.speed == USB_SPEED_FULL + && max > 64)) { + goto done; + } + break; + case USB_ENDPOINT_XFER_INT: + if (strstr(ep->ep.name, "-iso")) /* bulk is ok */ + goto done; + + switch (dev->gadget.speed) { + case USB_SPEED_HIGH: + if (max <= 1024) + break; + case USB_SPEED_FULL: + if (max <= 64) + break; + default: + if (max <= 8) + break; + goto done; + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (strstr(ep->ep.name, "-bulk") + || strstr(ep->ep.name, "-int")) + goto done; + + switch (dev->gadget.speed) { + case USB_SPEED_HIGH: + if (max <= 1024) + break; + case USB_SPEED_FULL: + if (max <= 1023) + break; + default: + goto done; + } + /* + * FIXME: + * calculate transactions needed for high bandwidth iso + */ + mult = (unsigned char)(1 + ((max >> 11) & 0x03)); + max = max & 0x8ff; /* bit 0~10 */ + /* 3 transactions at most */ + if (mult > 3) + goto done; + break; + default: + goto done; + } + + spin_lock_irqsave(&dev->lock, flags); + + /* configure endpoint capabilities in dQH */ + ep->dqh->dqh_ios = ios; + ep->dqh->dqh_mpl = cpu_to_le16(max); + ep->dqh->dqh_zlt = zlt; + ep->dqh->dqh_mult = mult; + + ep->ep.maxpacket = max; + ep->desc = desc; + ep->stopped = 0; + ep->ep_num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + + /* ep_type */ + ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + /* configure endpoint control registers */ + ep_reset(ep, ep->ep_num, is_in(ep), ep->ep_type); + + DBG(dev, "enabled %s (ep%d%s-%s), max %04x\n", + _ep->name, + ep->ep_num, + DIR_STRING(desc->bEndpointAddress), + type_string(desc->bmAttributes), + max); + + spin_unlock_irqrestore(&dev->lock, flags); +done: + VDBG(dev, "<--- %s()\n", __func__); + return retval; +} + + +/*-------------------------------------------------------------------------*/ + +/* retire a request */ +static void done(struct langwell_ep *ep, struct langwell_request *req, + int status) +{ + struct langwell_udc *dev = ep->dev; + unsigned stopped = ep->stopped; + struct langwell_dtd *curr_dtd, *next_dtd; + int i; + + VDBG(dev, "---> %s()\n", __func__); + + /* remove the req from ep->queue */ + list_del_init(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + /* free dTD for the request */ + next_dtd = req->head; + for (i = 0; i < req->dtd_count; i++) { + curr_dtd = next_dtd; + if (i != req->dtd_count - 1) + next_dtd = curr_dtd->next_dtd_virt; + dma_pool_free(dev->dtd_pool, curr_dtd, curr_dtd->dtd_dma); + } + + if (req->mapped) { + dma_unmap_single(&dev->pdev->dev, req->req.dma, req->req.length, + is_in(ep) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else + dma_sync_single_for_cpu(&dev->pdev->dev, req->req.dma, + req->req.length, + is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (status != -ESHUTDOWN) + DBG(dev, "complete %s, req %p, stat %d, len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + + spin_unlock(&dev->lock); + /* complete routine from gadget driver */ + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + + spin_lock(&dev->lock); + ep->stopped = stopped; + + VDBG(dev, "<--- %s()\n", __func__); +} + + +static void langwell_ep_fifo_flush(struct usb_ep *_ep); + +/* delete all endpoint requests, called with spinlock held */ +static void nuke(struct langwell_ep *ep, int status) +{ + /* called with spinlock held */ + ep->stopped = 1; + + /* endpoint fifo flush */ + if (&ep->ep && ep->desc) + langwell_ep_fifo_flush(&ep->ep); + + while (!list_empty(&ep->queue)) { + struct langwell_request *req = NULL; + req = list_entry(ep->queue.next, struct langwell_request, + queue); + done(ep, req, status); + } +} + + +/*-------------------------------------------------------------------------*/ + +/* endpoint is no longer usable */ +static int langwell_ep_disable(struct usb_ep *_ep) +{ + struct langwell_ep *ep; + unsigned long flags; + struct langwell_udc *dev; + int ep_num; + u32 endptctrl; + + ep = container_of(_ep, struct langwell_ep, ep); + dev = ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + if (!_ep || !ep->desc) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + + /* disable endpoint control register */ + ep_num = ep->ep_num; + endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); + if (is_in(ep)) + endptctrl &= ~EPCTRL_TXE; + else + endptctrl &= ~EPCTRL_RXE; + writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); + + /* nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + ep->desc = NULL; + ep->stopped = 1; + + spin_unlock_irqrestore(&dev->lock, flags); + + DBG(dev, "disabled %s\n", _ep->name); + VDBG(dev, "<--- %s()\n", __func__); + + return 0; +} + + +/* allocate a request object to use with this endpoint */ +static struct usb_request *langwell_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct langwell_ep *ep; + struct langwell_udc *dev; + struct langwell_request *req = NULL; + + if (!_ep) + return NULL; + + ep = container_of(_ep, struct langwell_ep, ep); + dev = ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + + VDBG(dev, "alloc request for %s\n", _ep->name); + VDBG(dev, "<--- %s()\n", __func__); + return &req->req; +} + + +/* free a request object */ +static void langwell_free_request(struct usb_ep *_ep, + struct usb_request *_req) +{ + struct langwell_ep *ep; + struct langwell_udc *dev; + struct langwell_request *req = NULL; + + ep = container_of(_ep, struct langwell_ep, ep); + dev = ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + if (!_ep || !_req) + return; + + req = container_of(_req, struct langwell_request, req); + WARN_ON(!list_empty(&req->queue)); + + if (_req) + kfree(req); + + VDBG(dev, "free request for %s\n", _ep->name); + VDBG(dev, "<--- %s()\n", __func__); +} + + +/*-------------------------------------------------------------------------*/ + +/* queue dTD and PRIME endpoint */ +static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req) +{ + u32 bit_mask, usbcmd, endptstat, dtd_dma; + u8 dtd_status; + int i; + struct langwell_dqh *dqh; + struct langwell_udc *dev; + + dev = ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + i = ep->ep_num * 2 + is_in(ep); + dqh = &dev->ep_dqh[i]; + + if (ep->ep_num) + VDBG(dev, "%s\n", ep->name); + else + /* ep0 */ + VDBG(dev, "%s-%s\n", ep->name, is_in(ep) ? "in" : "out"); + + VDBG(dev, "ep_dqh[%d] addr: 0x%08x\n", i, (u32)&(dev->ep_dqh[i])); + + bit_mask = is_in(ep) ? + (1 << (ep->ep_num + 16)) : (1 << (ep->ep_num)); + + VDBG(dev, "bit_mask = 0x%08x\n", bit_mask); + + /* check if the pipe is empty */ + if (!(list_empty(&ep->queue))) { + /* add dTD to the end of linked list */ + struct langwell_request *lastreq; + lastreq = list_entry(ep->queue.prev, + struct langwell_request, queue); + + lastreq->tail->dtd_next = + cpu_to_le32(req->head->dtd_dma & DTD_NEXT_MASK); + + /* read prime bit, if 1 goto out */ + if (readl(&dev->op_regs->endptprime) & bit_mask) + goto out; + + do { + /* set ATDTW bit in USBCMD */ + usbcmd = readl(&dev->op_regs->usbcmd); + writel(usbcmd | CMD_ATDTW, &dev->op_regs->usbcmd); + + /* read correct status bit */ + endptstat = readl(&dev->op_regs->endptstat) & bit_mask; + + } while (!(readl(&dev->op_regs->usbcmd) & CMD_ATDTW)); + + /* write ATDTW bit to 0 */ + usbcmd = readl(&dev->op_regs->usbcmd); + writel(usbcmd & ~CMD_ATDTW, &dev->op_regs->usbcmd); + + if (endptstat) + goto out; + } + + /* write dQH next pointer and terminate bit to 0 */ + dtd_dma = req->head->dtd_dma & DTD_NEXT_MASK; + dqh->dtd_next = cpu_to_le32(dtd_dma); + + /* clear active and halt bit */ + dtd_status = (u8) ~(DTD_STS_ACTIVE | DTD_STS_HALTED); + dqh->dtd_status &= dtd_status; + VDBG(dev, "dqh->dtd_status = 0x%x\n", dqh->dtd_status); + + /* write 1 to endptprime register to PRIME endpoint */ + bit_mask = is_in(ep) ? (1 << (ep->ep_num + 16)) : (1 << ep->ep_num); + VDBG(dev, "endprime bit_mask = 0x%08x\n", bit_mask); + writel(bit_mask, &dev->op_regs->endptprime); +out: + VDBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* fill in the dTD structure to build a transfer descriptor */ +static struct langwell_dtd *build_dtd(struct langwell_request *req, + unsigned *length, dma_addr_t *dma, int *is_last) +{ + u32 buf_ptr; + struct langwell_dtd *dtd; + struct langwell_udc *dev; + int i; + + dev = req->ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + /* the maximum transfer length, up to 16k bytes */ + *length = min(req->req.length - req->req.actual, + (unsigned)DTD_MAX_TRANSFER_LENGTH); + + /* create dTD dma_pool resource */ + dtd = dma_pool_alloc(dev->dtd_pool, GFP_KERNEL, dma); + if (dtd == NULL) + return dtd; + dtd->dtd_dma = *dma; + + /* initialize buffer page pointers */ + buf_ptr = (u32)(req->req.dma + req->req.actual); + for (i = 0; i < 5; i++) + dtd->dtd_buf[i] = cpu_to_le32(buf_ptr + i * PAGE_SIZE); + + req->req.actual += *length; + + /* fill in total bytes with transfer size */ + dtd->dtd_total = cpu_to_le16(*length); + VDBG(dev, "dtd->dtd_total = %d\n", dtd->dtd_total); + + /* set is_last flag if req->req.zero is set or not */ + if (req->req.zero) { + if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + *is_last = 1; + else + *is_last = 0; + } else if (req->req.length == req->req.actual) { + *is_last = 1; + } else + *is_last = 0; + + if (*is_last == 0) + VDBG(dev, "multi-dtd request!\n"); + + /* set interrupt on complete bit for the last dTD */ + if (*is_last && !req->req.no_interrupt) + dtd->dtd_ioc = 1; + + /* set multiplier override 0 for non-ISO and non-TX endpoint */ + dtd->dtd_multo = 0; + + /* set the active bit of status field to 1 */ + dtd->dtd_status = DTD_STS_ACTIVE; + VDBG(dev, "dtd->dtd_status = 0x%02x\n", dtd->dtd_status); + + VDBG(dev, "length = %d, dma addr= 0x%08x\n", *length, (int)*dma); + VDBG(dev, "<--- %s()\n", __func__); + return dtd; +} + + +/* generate dTD linked list for a request */ +static int req_to_dtd(struct langwell_request *req) +{ + unsigned count; + int is_last, is_first = 1; + struct langwell_dtd *dtd, *last_dtd = NULL; + struct langwell_udc *dev; + dma_addr_t dma; + + dev = req->ep->dev; + VDBG(dev, "---> %s()\n", __func__); + do { + dtd = build_dtd(req, &count, &dma, &is_last); + if (dtd == NULL) + return -ENOMEM; + + if (is_first) { + is_first = 0; + req->head = dtd; + } else { + last_dtd->dtd_next = cpu_to_le32(dma); + last_dtd->next_dtd_virt = dtd; + } + last_dtd = dtd; + req->dtd_count++; + } while (!is_last); + + /* set terminate bit to 1 for the last dTD */ + dtd->dtd_next = DTD_TERM; + + req->tail = dtd; + + VDBG(dev, "<--- %s()\n", __func__); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* queue (submits) an I/O requests to an endpoint */ +static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct langwell_request *req; + struct langwell_ep *ep; + struct langwell_udc *dev; + unsigned long flags; + int is_iso = 0, zlflag = 0; + + /* always require a cpu-view buffer */ + req = container_of(_req, struct langwell_request, req); + ep = container_of(_ep, struct langwell_ep, ep); + + if (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue)) { + return -EINVAL; + } + + if (unlikely(!_ep || !ep->desc)) + return -EINVAL; + + dev = ep->dev; + req->ep = ep; + VDBG(dev, "---> %s()\n", __func__); + + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) + return -EMSGSIZE; + is_iso = 1; + } + + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + /* set up dma mapping in case the caller didn't */ + if (_req->dma == DMA_ADDR_INVALID) { + /* WORKAROUND: WARN_ON(size == 0) */ + if (_req->length == 0) { + VDBG(dev, "req->length: 0->1\n"); + zlflag = 1; + _req->length++; + } + + _req->dma = dma_map_single(&dev->pdev->dev, + _req->buf, _req->length, + is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (zlflag && (_req->length == 1)) { + VDBG(dev, "req->length: 1->0\n"); + zlflag = 0; + _req->length = 0; + } + + req->mapped = 1; + VDBG(dev, "req->mapped = 1\n"); + } else { + dma_sync_single_for_device(&dev->pdev->dev, + _req->dma, _req->length, + is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = 0; + VDBG(dev, "req->mapped = 0\n"); + } + + DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n", + _ep->name, + _req, _req->length, _req->buf, _req->dma); + + _req->status = -EINPROGRESS; + _req->actual = 0; + req->dtd_count = 0; + + spin_lock_irqsave(&dev->lock, flags); + + /* build and put dTDs to endpoint queue */ + if (!req_to_dtd(req)) { + queue_dtd(ep, req); + } else { + spin_unlock_irqrestore(&dev->lock, flags); + return -ENOMEM; + } + + /* update ep0 state */ + if (ep->ep_num == 0) + dev->ep0_state = DATA_STATE_XMIT; + + if (likely(req != NULL)) { + list_add_tail(&req->queue, &ep->queue); + VDBG(dev, "list_add_tail() \n"); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + VDBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* dequeue (cancels, unlinks) an I/O request from an endpoint */ +static int langwell_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct langwell_ep *ep; + struct langwell_udc *dev; + struct langwell_request *req; + unsigned long flags; + int stopped, ep_num, retval = 0; + u32 endptctrl; + + ep = container_of(_ep, struct langwell_ep, ep); + dev = ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + if (!_ep || !ep->desc || !_req) + return -EINVAL; + + if (!dev->driver) + return -ESHUTDOWN; + + spin_lock_irqsave(&dev->lock, flags); + stopped = ep->stopped; + + /* quiesce dma while we patch the queue */ + ep->stopped = 1; + ep_num = ep->ep_num; + + /* disable endpoint control register */ + endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); + if (is_in(ep)) + endptctrl &= ~EPCTRL_TXE; + else + endptctrl &= ~EPCTRL_RXE; + writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); + + /* make sure it's still queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + + if (&req->req != _req) { + retval = -EINVAL; + goto done; + } + + /* queue head may be partially complete. */ + if (ep->queue.next == &req->queue) { + DBG(dev, "unlink (%s) dma\n", _ep->name); + _req->status = -ECONNRESET; + langwell_ep_fifo_flush(&ep->ep); + + /* not the last request in endpoint queue */ + if (likely(ep->queue.next == &req->queue)) { + struct langwell_dqh *dqh; + struct langwell_request *next_req; + + dqh = ep->dqh; + next_req = list_entry(req->queue.next, + struct langwell_request, queue); + + /* point the dQH to the first dTD of next request */ + writel((u32) next_req->head, &dqh->dqh_current); + } + } else { + struct langwell_request *prev_req; + + prev_req = list_entry(req->queue.prev, + struct langwell_request, queue); + writel(readl(&req->tail->dtd_next), + &prev_req->tail->dtd_next); + } + + done(ep, req, -ECONNRESET); + +done: + /* enable endpoint again */ + endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); + if (is_in(ep)) + endptctrl |= EPCTRL_TXE; + else + endptctrl |= EPCTRL_RXE; + writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); + + ep->stopped = stopped; + spin_unlock_irqrestore(&dev->lock, flags); + + VDBG(dev, "<--- %s()\n", __func__); + return retval; +} + + +/*-------------------------------------------------------------------------*/ + +/* endpoint set/clear halt */ +static void ep_set_halt(struct langwell_ep *ep, int value) +{ + u32 endptctrl = 0; + int ep_num; + struct langwell_udc *dev = ep->dev; + VDBG(dev, "---> %s()\n", __func__); + + ep_num = ep->ep_num; + endptctrl = readl(&dev->op_regs->endptctrl[ep_num]); + + /* value: 1 - set halt, 0 - clear halt */ + if (value) { + /* set the stall bit */ + if (is_in(ep)) + endptctrl |= EPCTRL_TXS; + else + endptctrl |= EPCTRL_RXS; + } else { + /* clear the stall bit and reset data toggle */ + if (is_in(ep)) { + endptctrl &= ~EPCTRL_TXS; + endptctrl |= EPCTRL_TXR; + } else { + endptctrl &= ~EPCTRL_RXS; + endptctrl |= EPCTRL_RXR; + } + } + + writel(endptctrl, &dev->op_regs->endptctrl[ep_num]); + + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* set the endpoint halt feature */ +static int langwell_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct langwell_ep *ep; + struct langwell_udc *dev; + unsigned long flags; + int retval = 0; + + ep = container_of(_ep, struct langwell_ep, ep); + dev = ep->dev; + + VDBG(dev, "---> %s()\n", __func__); + + if (!_ep || !ep->desc) + return -EINVAL; + + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + if (ep->desc && (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_ISOC) + return -EOPNOTSUPP; + + spin_lock_irqsave(&dev->lock, flags); + + /* + * attempt to halt IN ep will fail if any transfer requests + * are still queue + */ + if (!list_empty(&ep->queue) && is_in(ep) && value) { + /* IN endpoint FIFO holds bytes */ + DBG(dev, "%s FIFO holds bytes\n", _ep->name); + retval = -EAGAIN; + goto done; + } + + /* endpoint set/clear halt */ + if (ep->ep_num) { + ep_set_halt(ep, value); + } else { /* endpoint 0 */ + dev->ep0_state = WAIT_FOR_SETUP; + dev->ep0_dir = USB_DIR_OUT; + } +done: + spin_unlock_irqrestore(&dev->lock, flags); + DBG(dev, "%s %s halt\n", _ep->name, value ? "set" : "clear"); + VDBG(dev, "<--- %s()\n", __func__); + return retval; +} + + +/* set the halt feature and ignores clear requests */ +static int langwell_ep_set_wedge(struct usb_ep *_ep) +{ + struct langwell_ep *ep; + struct langwell_udc *dev; + + ep = container_of(_ep, struct langwell_ep, ep); + dev = ep->dev; + + VDBG(dev, "---> %s()\n", __func__); + + if (!_ep || !ep->desc) + return -EINVAL; + + VDBG(dev, "<--- %s()\n", __func__); + return usb_ep_set_halt(_ep); +} + + +/* flush contents of a fifo */ +static void langwell_ep_fifo_flush(struct usb_ep *_ep) +{ + struct langwell_ep *ep; + struct langwell_udc *dev; + u32 flush_bit; + unsigned long timeout; + + ep = container_of(_ep, struct langwell_ep, ep); + dev = ep->dev; + + VDBG(dev, "---> %s()\n", __func__); + + if (!_ep || !ep->desc) { + VDBG(dev, "ep or ep->desc is NULL\n"); + VDBG(dev, "<--- %s()\n", __func__); + return; + } + + VDBG(dev, "%s-%s fifo flush\n", _ep->name, is_in(ep) ? "in" : "out"); + + /* flush endpoint buffer */ + if (ep->ep_num == 0) + flush_bit = (1 << 16) | 1; + else if (is_in(ep)) + flush_bit = 1 << (ep->ep_num + 16); /* TX */ + else + flush_bit = 1 << ep->ep_num; /* RX */ + + /* wait until flush complete */ + timeout = jiffies + FLUSH_TIMEOUT; + do { + writel(flush_bit, &dev->op_regs->endptflush); + while (readl(&dev->op_regs->endptflush)) { + if (time_after(jiffies, timeout)) { + ERROR(dev, "ep flush timeout\n"); + goto done; + } + cpu_relax(); + } + } while (readl(&dev->op_regs->endptstat) & flush_bit); +done: + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* endpoints operations structure */ +static const struct usb_ep_ops langwell_ep_ops = { + + /* configure endpoint, making it usable */ + .enable = langwell_ep_enable, + + /* endpoint is no longer usable */ + .disable = langwell_ep_disable, + + /* allocate a request object to use with this endpoint */ + .alloc_request = langwell_alloc_request, + + /* free a request object */ + .free_request = langwell_free_request, + + /* queue (submits) an I/O requests to an endpoint */ + .queue = langwell_ep_queue, + + /* dequeue (cancels, unlinks) an I/O request from an endpoint */ + .dequeue = langwell_ep_dequeue, + + /* set the endpoint halt feature */ + .set_halt = langwell_ep_set_halt, + + /* set the halt feature and ignores clear requests */ + .set_wedge = langwell_ep_set_wedge, + + /* flush contents of a fifo */ + .fifo_flush = langwell_ep_fifo_flush, +}; + + +/*-------------------------------------------------------------------------*/ + +/* device controller usb_gadget_ops structure */ + +/* returns the current frame number */ +static int langwell_get_frame(struct usb_gadget *_gadget) +{ + struct langwell_udc *dev; + u16 retval; + + if (!_gadget) + return -ENODEV; + + dev = container_of(_gadget, struct langwell_udc, gadget); + VDBG(dev, "---> %s()\n", __func__); + + retval = readl(&dev->op_regs->frindex) & FRINDEX_MASK; + + VDBG(dev, "<--- %s()\n", __func__); + return retval; +} + + +/* tries to wake up the host connected to this gadget */ +static int langwell_wakeup(struct usb_gadget *_gadget) +{ + struct langwell_udc *dev; + u32 portsc1, devlc; + unsigned long flags; + + if (!_gadget) + return 0; + + dev = container_of(_gadget, struct langwell_udc, gadget); + VDBG(dev, "---> %s()\n", __func__); + + /* Remote Wakeup feature not enabled by host */ + if (!dev->remote_wakeup) + return -ENOTSUPP; + + spin_lock_irqsave(&dev->lock, flags); + + portsc1 = readl(&dev->op_regs->portsc1); + if (!(portsc1 & PORTS_SUSP)) { + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + + /* LPM L1 to L0, remote wakeup */ + if (dev->lpm && dev->lpm_state == LPM_L1) { + portsc1 |= PORTS_SLP; + writel(portsc1, &dev->op_regs->portsc1); + } + + /* force port resume */ + if (dev->usb_state == USB_STATE_SUSPENDED) { + portsc1 |= PORTS_FPR; + writel(portsc1, &dev->op_regs->portsc1); + } + + /* exit PHY low power suspend */ + devlc = readl(&dev->op_regs->devlc); + VDBG(dev, "devlc = 0x%08x\n", devlc); + devlc &= ~LPM_PHCD; + writel(devlc, &dev->op_regs->devlc); + + spin_unlock_irqrestore(&dev->lock, flags); + + VDBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* notify controller that VBUS is powered or not */ +static int langwell_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + struct langwell_udc *dev; + unsigned long flags; + u32 usbcmd; + + if (!_gadget) + return -ENODEV; + + dev = container_of(_gadget, struct langwell_udc, gadget); + VDBG(dev, "---> %s()\n", __func__); + + spin_lock_irqsave(&dev->lock, flags); + VDBG(dev, "VBUS status: %s\n", is_active ? "on" : "off"); + + dev->vbus_active = (is_active != 0); + if (dev->driver && dev->softconnected && dev->vbus_active) { + usbcmd = readl(&dev->op_regs->usbcmd); + usbcmd |= CMD_RUNSTOP; + writel(usbcmd, &dev->op_regs->usbcmd); + } else { + usbcmd = readl(&dev->op_regs->usbcmd); + usbcmd &= ~CMD_RUNSTOP; + writel(usbcmd, &dev->op_regs->usbcmd); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + VDBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* constrain controller's VBUS power usage */ +static int langwell_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +{ + struct langwell_udc *dev; + + if (!_gadget) + return -ENODEV; + + dev = container_of(_gadget, struct langwell_udc, gadget); + VDBG(dev, "---> %s()\n", __func__); + + if (dev->transceiver) { + VDBG(dev, "otg_set_power\n"); + VDBG(dev, "<--- %s()\n", __func__); + return otg_set_power(dev->transceiver, mA); + } + + VDBG(dev, "<--- %s()\n", __func__); + return -ENOTSUPP; +} + + +/* D+ pullup, software-controlled connect/disconnect to USB host */ +static int langwell_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct langwell_udc *dev; + u32 usbcmd; + unsigned long flags; + + if (!_gadget) + return -ENODEV; + + dev = container_of(_gadget, struct langwell_udc, gadget); + + VDBG(dev, "---> %s()\n", __func__); + + spin_lock_irqsave(&dev->lock, flags); + dev->softconnected = (is_on != 0); + + if (dev->driver && dev->softconnected && dev->vbus_active) { + usbcmd = readl(&dev->op_regs->usbcmd); + usbcmd |= CMD_RUNSTOP; + writel(usbcmd, &dev->op_regs->usbcmd); + } else { + usbcmd = readl(&dev->op_regs->usbcmd); + usbcmd &= ~CMD_RUNSTOP; + writel(usbcmd, &dev->op_regs->usbcmd); + } + spin_unlock_irqrestore(&dev->lock, flags); + + VDBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* device controller usb_gadget_ops structure */ +static const struct usb_gadget_ops langwell_ops = { + + /* returns the current frame number */ + .get_frame = langwell_get_frame, + + /* tries to wake up the host connected to this gadget */ + .wakeup = langwell_wakeup, + + /* set the device selfpowered feature, always selfpowered */ + /* .set_selfpowered = langwell_set_selfpowered, */ + + /* notify controller that VBUS is powered or not */ + .vbus_session = langwell_vbus_session, + + /* constrain controller's VBUS power usage */ + .vbus_draw = langwell_vbus_draw, + + /* D+ pullup, software-controlled connect/disconnect to USB host */ + .pullup = langwell_pullup, +}; + + +/*-------------------------------------------------------------------------*/ + +/* device controller operations */ + +/* reset device controller */ +static int langwell_udc_reset(struct langwell_udc *dev) +{ + u32 usbcmd, usbmode, devlc, endpointlistaddr; + unsigned long timeout; + + if (!dev) + return -EINVAL; + + DBG(dev, "---> %s()\n", __func__); + + /* set controller to stop state */ + usbcmd = readl(&dev->op_regs->usbcmd); + usbcmd &= ~CMD_RUNSTOP; + writel(usbcmd, &dev->op_regs->usbcmd); + + /* reset device controller */ + usbcmd = readl(&dev->op_regs->usbcmd); + usbcmd |= CMD_RST; + writel(usbcmd, &dev->op_regs->usbcmd); + + /* wait for reset to complete */ + timeout = jiffies + RESET_TIMEOUT; + while (readl(&dev->op_regs->usbcmd) & CMD_RST) { + if (time_after(jiffies, timeout)) { + ERROR(dev, "device reset timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + /* set controller to device mode */ + usbmode = readl(&dev->op_regs->usbmode); + usbmode |= MODE_DEVICE; + + /* turn setup lockout off, require setup tripwire in usbcmd */ + usbmode |= MODE_SLOM; + + writel(usbmode, &dev->op_regs->usbmode); + usbmode = readl(&dev->op_regs->usbmode); + VDBG(dev, "usbmode=0x%08x\n", usbmode); + + /* Write-Clear setup status */ + writel(0, &dev->op_regs->usbsts); + + /* if support USB LPM, ACK all LPM token */ + if (dev->lpm) { + devlc = readl(&dev->op_regs->devlc); + devlc &= ~LPM_STL; /* don't STALL LPM token */ + devlc &= ~LPM_NYT_ACK; /* ACK LPM token */ + writel(devlc, &dev->op_regs->devlc); + } + + /* fill endpointlistaddr register */ + endpointlistaddr = dev->ep_dqh_dma; + endpointlistaddr &= ENDPOINTLISTADDR_MASK; + writel(endpointlistaddr, &dev->op_regs->endpointlistaddr); + + VDBG(dev, "dQH base (vir: %p, phy: 0x%08x), endpointlistaddr=0x%08x\n", + dev->ep_dqh, endpointlistaddr, + readl(&dev->op_regs->endpointlistaddr)); + DBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* reinitialize device controller endpoints */ +static int eps_reinit(struct langwell_udc *dev) +{ + struct langwell_ep *ep; + char name[14]; + int i; + + VDBG(dev, "---> %s()\n", __func__); + + /* initialize ep0 */ + ep = &dev->ep[0]; + ep->dev = dev; + strncpy(ep->name, "ep0", sizeof(ep->name)); + ep->ep.name = ep->name; + ep->ep.ops = &langwell_ep_ops; + ep->stopped = 0; + ep->ep.maxpacket = EP0_MAX_PKT_SIZE; + ep->ep_num = 0; + ep->desc = &langwell_ep0_desc; + INIT_LIST_HEAD(&ep->queue); + + ep->ep_type = USB_ENDPOINT_XFER_CONTROL; + + /* initialize other endpoints */ + for (i = 2; i < dev->ep_max; i++) { + ep = &dev->ep[i]; + if (i % 2) + snprintf(name, sizeof(name), "ep%din", i / 2); + else + snprintf(name, sizeof(name), "ep%dout", i / 2); + ep->dev = dev; + strncpy(ep->name, name, sizeof(ep->name)); + ep->ep.name = ep->name; + + ep->ep.ops = &langwell_ep_ops; + ep->stopped = 0; + ep->ep.maxpacket = (unsigned short) ~0; + ep->ep_num = i / 2; + + INIT_LIST_HEAD(&ep->queue); + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->dqh = &dev->ep_dqh[i]; + } + + VDBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* enable interrupt and set controller to run state */ +static void langwell_udc_start(struct langwell_udc *dev) +{ + u32 usbintr, usbcmd; + DBG(dev, "---> %s()\n", __func__); + + /* enable interrupts */ + usbintr = INTR_ULPIE /* ULPI */ + | INTR_SLE /* suspend */ + /* | INTR_SRE SOF received */ + | INTR_URE /* USB reset */ + | INTR_AAE /* async advance */ + | INTR_SEE /* system error */ + | INTR_FRE /* frame list rollover */ + | INTR_PCE /* port change detect */ + | INTR_UEE /* USB error interrupt */ + | INTR_UE; /* USB interrupt */ + writel(usbintr, &dev->op_regs->usbintr); + + /* clear stopped bit */ + dev->stopped = 0; + + /* set controller to run */ + usbcmd = readl(&dev->op_regs->usbcmd); + usbcmd |= CMD_RUNSTOP; + writel(usbcmd, &dev->op_regs->usbcmd); + + DBG(dev, "<--- %s()\n", __func__); + return; +} + + +/* disable interrupt and set controller to stop state */ +static void langwell_udc_stop(struct langwell_udc *dev) +{ + u32 usbcmd; + + DBG(dev, "---> %s()\n", __func__); + + /* disable all interrupts */ + writel(0, &dev->op_regs->usbintr); + + /* set stopped bit */ + dev->stopped = 1; + + /* set controller to stop state */ + usbcmd = readl(&dev->op_regs->usbcmd); + usbcmd &= ~CMD_RUNSTOP; + writel(usbcmd, &dev->op_regs->usbcmd); + + DBG(dev, "<--- %s()\n", __func__); + return; +} + + +/* stop all USB activities */ +static void stop_activity(struct langwell_udc *dev, + struct usb_gadget_driver *driver) +{ + struct langwell_ep *ep; + DBG(dev, "---> %s()\n", __func__); + + nuke(&dev->ep[0], -ESHUTDOWN); + + list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + DBG(dev, "<--- %s()\n", __func__); +} + + +/*-------------------------------------------------------------------------*/ + +/* device "function" sysfs attribute file */ +static ssize_t show_function(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct langwell_udc *dev = the_controller; + + if (!dev->driver || !dev->driver->function + || strlen(dev->driver->function) > PAGE_SIZE) + return 0; + + return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); +} +static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); + + +/* device "langwell_udc" sysfs attribute file */ +static ssize_t show_langwell_udc(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct langwell_udc *dev = the_controller; + struct langwell_request *req; + struct langwell_ep *ep = NULL; + char *next; + unsigned size; + unsigned t; + unsigned i; + unsigned long flags; + u32 tmp_reg; + + next = buf; + size = PAGE_SIZE; + spin_lock_irqsave(&dev->lock, flags); + + /* driver basic information */ + t = scnprintf(next, size, + DRIVER_DESC "\n" + "%s version: %s\n" + "Gadget driver: %s\n\n", + driver_name, DRIVER_VERSION, + dev->driver ? dev->driver->driver.name : "(none)"); + size -= t; + next += t; + + /* device registers */ + tmp_reg = readl(&dev->op_regs->usbcmd); + t = scnprintf(next, size, + "USBCMD reg:\n" + "SetupTW: %d\n" + "Run/Stop: %s\n\n", + (tmp_reg & CMD_SUTW) ? 1 : 0, + (tmp_reg & CMD_RUNSTOP) ? "Run" : "Stop"); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->usbsts); + t = scnprintf(next, size, + "USB Status Reg:\n" + "Device Suspend: %d\n" + "Reset Received: %d\n" + "System Error: %s\n" + "USB Error Interrupt: %s\n\n", + (tmp_reg & STS_SLI) ? 1 : 0, + (tmp_reg & STS_URI) ? 1 : 0, + (tmp_reg & STS_SEI) ? "Error" : "No error", + (tmp_reg & STS_UEI) ? "Error detected" : "No error"); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->usbintr); + t = scnprintf(next, size, + "USB Intrrupt Enable Reg:\n" + "Sleep Enable: %d\n" + "SOF Received Enable: %d\n" + "Reset Enable: %d\n" + "System Error Enable: %d\n" + "Port Change Dectected Enable: %d\n" + "USB Error Intr Enable: %d\n" + "USB Intr Enable: %d\n\n", + (tmp_reg & INTR_SLE) ? 1 : 0, + (tmp_reg & INTR_SRE) ? 1 : 0, + (tmp_reg & INTR_URE) ? 1 : 0, + (tmp_reg & INTR_SEE) ? 1 : 0, + (tmp_reg & INTR_PCE) ? 1 : 0, + (tmp_reg & INTR_UEE) ? 1 : 0, + (tmp_reg & INTR_UE) ? 1 : 0); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->frindex); + t = scnprintf(next, size, + "USB Frame Index Reg:\n" + "Frame Number is 0x%08x\n\n", + (tmp_reg & FRINDEX_MASK)); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->deviceaddr); + t = scnprintf(next, size, + "USB Device Address Reg:\n" + "Device Addr is 0x%x\n\n", + USBADR(tmp_reg)); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->endpointlistaddr); + t = scnprintf(next, size, + "USB Endpoint List Address Reg:\n" + "Endpoint List Pointer is 0x%x\n\n", + EPBASE(tmp_reg)); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->portsc1); + t = scnprintf(next, size, + "USB Port Status & Control Reg:\n" + "Port Reset: %s\n" + "Port Suspend Mode: %s\n" + "Over-current Change: %s\n" + "Port Enable/Disable Change: %s\n" + "Port Enabled/Disabled: %s\n" + "Current Connect Status: %s\n\n", + (tmp_reg & PORTS_PR) ? "Reset" : "Not Reset", + (tmp_reg & PORTS_SUSP) ? "Suspend " : "Not Suspend", + (tmp_reg & PORTS_OCC) ? "Detected" : "No", + (tmp_reg & PORTS_PEC) ? "Changed" : "Not Changed", + (tmp_reg & PORTS_PE) ? "Enable" : "Not Correct", + (tmp_reg & PORTS_CCS) ? "Attached" : "Not Attached"); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->devlc); + t = scnprintf(next, size, + "Device LPM Control Reg:\n" + "Parallel Transceiver : %d\n" + "Serial Transceiver : %d\n" + "Port Speed: %s\n" + "Port Force Full Speed Connenct: %s\n" + "PHY Low Power Suspend Clock Disable: %s\n" + "BmAttributes: %d\n\n", + LPM_PTS(tmp_reg), + (tmp_reg & LPM_STS) ? 1 : 0, + ({ + char *s; + switch (LPM_PSPD(tmp_reg)) { + case LPM_SPEED_FULL: + s = "Full Speed"; break; + case LPM_SPEED_LOW: + s = "Low Speed"; break; + case LPM_SPEED_HIGH: + s = "High Speed"; break; + default: + s = "Unknown Speed"; break; + } + s; + }), + (tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force", + (tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled", + LPM_BA(tmp_reg)); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->usbmode); + t = scnprintf(next, size, + "USB Mode Reg:\n" + "Controller Mode is : %s\n\n", ({ + char *s; + switch (MODE_CM(tmp_reg)) { + case MODE_IDLE: + s = "Idle"; break; + case MODE_DEVICE: + s = "Device Controller"; break; + case MODE_HOST: + s = "Host Controller"; break; + default: + s = "None"; break; + } + s; + })); + size -= t; + next += t; + + tmp_reg = readl(&dev->op_regs->endptsetupstat); + t = scnprintf(next, size, + "Endpoint Setup Status Reg:\n" + "SETUP on ep 0x%04x\n\n", + tmp_reg & SETUPSTAT_MASK); + size -= t; + next += t; + + for (i = 0; i < dev->ep_max / 2; i++) { + tmp_reg = readl(&dev->op_regs->endptctrl[i]); + t = scnprintf(next, size, "EP Ctrl Reg [%d]: 0x%08x\n", + i, tmp_reg); + size -= t; + next += t; + } + tmp_reg = readl(&dev->op_regs->endptprime); + t = scnprintf(next, size, "EP Prime Reg: 0x%08x\n\n", tmp_reg); + size -= t; + next += t; + + /* langwell_udc, langwell_ep, langwell_request structure information */ + ep = &dev->ep[0]; + t = scnprintf(next, size, "%s MaxPacketSize: 0x%x, ep_num: %d\n", + ep->ep.name, ep->ep.maxpacket, ep->ep_num); + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "its req queue is empty\n\n"); + size -= t; + next += t; + } else { + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "req %p actual 0x%x length 0x%x buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + size -= t; + next += t; + } + } + /* other gadget->eplist ep */ + list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { + if (ep->desc) { + t = scnprintf(next, size, + "\n%s MaxPacketSize: 0x%x, " + "ep_num: %d\n", + ep->ep.name, ep->ep.maxpacket, + ep->ep_num); + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, + "its req queue is empty\n\n"); + size -= t; + next += t; + } else { + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "req %p actual 0x%x length " + "0x%x buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + size -= t; + next += t; + } + } + } + } + + spin_unlock_irqrestore(&dev->lock, flags); + return PAGE_SIZE - size; +} +static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL); + + +/*-------------------------------------------------------------------------*/ + +/* + * when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct langwell_udc *dev = the_controller; + unsigned long flags; + int retval; + + if (!dev) + return -ENODEV; + + DBG(dev, "---> %s()\n", __func__); + + if (dev->driver) + return -EBUSY; + + spin_lock_irqsave(&dev->lock, flags); + + /* hook up the driver ... */ + driver->driver.bus = NULL; + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + spin_unlock_irqrestore(&dev->lock, flags); + + retval = driver->bind(&dev->gadget); + if (retval) { + DBG(dev, "bind to driver %s --> %d\n", + driver->driver.name, retval); + dev->driver = NULL; + dev->gadget.dev.driver = NULL; + return retval; + } + + retval = device_create_file(&dev->pdev->dev, &dev_attr_function); + if (retval) + goto err_unbind; + + dev->usb_state = USB_STATE_ATTACHED; + dev->ep0_state = WAIT_FOR_SETUP; + dev->ep0_dir = USB_DIR_OUT; + + /* enable interrupt and set controller to run state */ + if (dev->got_irq) + langwell_udc_start(dev); + + VDBG(dev, "After langwell_udc_start(), print all registers:\n"); +#ifdef VERBOSE + print_all_registers(dev); +#endif + + INFO(dev, "register driver: %s\n", driver->driver.name); + VDBG(dev, "<--- %s()\n", __func__); + return 0; + +err_unbind: + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + DBG(dev, "<--- %s()\n", __func__); + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + + +/* unregister gadget driver */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct langwell_udc *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + + DBG(dev, "---> %s()\n", __func__); + + if (unlikely(!driver || !driver->bind || !driver->unbind)) + return -EINVAL; + + /* unbind OTG transceiver */ + if (dev->transceiver) + (void)otg_set_peripheral(dev->transceiver, 0); + + /* disable interrupt and set controller to stop state */ + langwell_udc_stop(dev); + + dev->usb_state = USB_STATE_ATTACHED; + dev->ep0_state = WAIT_FOR_SETUP; + dev->ep0_dir = USB_DIR_OUT; + + spin_lock_irqsave(&dev->lock, flags); + + /* stop all usb activities */ + dev->gadget.speed = USB_SPEED_UNKNOWN; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + /* unbind gadget driver */ + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + device_remove_file(&dev->pdev->dev, &dev_attr_function); + + INFO(dev, "unregistered driver '%s'\n", driver->driver.name); + DBG(dev, "<--- %s()\n", __func__); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +/*-------------------------------------------------------------------------*/ + +/* + * setup tripwire is used as a semaphore to ensure that the setup data + * payload is extracted from a dQH without being corrupted + */ +static void setup_tripwire(struct langwell_udc *dev) +{ + u32 usbcmd, + endptsetupstat; + unsigned long timeout; + struct langwell_dqh *dqh; + + VDBG(dev, "---> %s()\n", __func__); + + /* ep0 OUT dQH */ + dqh = &dev->ep_dqh[EP_DIR_OUT]; + + /* Write-Clear endptsetupstat */ + endptsetupstat = readl(&dev->op_regs->endptsetupstat); + writel(endptsetupstat, &dev->op_regs->endptsetupstat); + + /* wait until endptsetupstat is cleared */ + timeout = jiffies + SETUPSTAT_TIMEOUT; + while (readl(&dev->op_regs->endptsetupstat)) { + if (time_after(jiffies, timeout)) { + ERROR(dev, "setup_tripwire timeout\n"); + break; + } + cpu_relax(); + } + + /* while a hazard exists when setup packet arrives */ + do { + /* set setup tripwire bit */ + usbcmd = readl(&dev->op_regs->usbcmd); + writel(usbcmd | CMD_SUTW, &dev->op_regs->usbcmd); + + /* copy the setup packet to local buffer */ + memcpy(&dev->local_setup_buff, &dqh->dqh_setup, 8); + } while (!(readl(&dev->op_regs->usbcmd) & CMD_SUTW)); + + /* Write-Clear setup tripwire bit */ + usbcmd = readl(&dev->op_regs->usbcmd); + writel(usbcmd & ~CMD_SUTW, &dev->op_regs->usbcmd); + + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* protocol ep0 stall, will automatically be cleared on new transaction */ +static void ep0_stall(struct langwell_udc *dev) +{ + u32 endptctrl; + + VDBG(dev, "---> %s()\n", __func__); + + /* set TX and RX to stall */ + endptctrl = readl(&dev->op_regs->endptctrl[0]); + endptctrl |= EPCTRL_TXS | EPCTRL_RXS; + writel(endptctrl, &dev->op_regs->endptctrl[0]); + + /* update ep0 state */ + dev->ep0_state = WAIT_FOR_SETUP; + dev->ep0_dir = USB_DIR_OUT; + + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* PRIME a status phase for ep0 */ +static int prime_status_phase(struct langwell_udc *dev, int dir) +{ + struct langwell_request *req; + struct langwell_ep *ep; + int status = 0; + + VDBG(dev, "---> %s()\n", __func__); + + if (dir == EP_DIR_IN) + dev->ep0_dir = USB_DIR_IN; + else + dev->ep0_dir = USB_DIR_OUT; + + ep = &dev->ep[0]; + dev->ep0_state = WAIT_FOR_OUT_STATUS; + + req = dev->status_req; + + req->ep = ep; + req->req.length = 0; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + if (!req_to_dtd(req)) + status = queue_dtd(ep, req); + else + return -ENOMEM; + + if (status) + ERROR(dev, "can't queue ep0 status request\n"); + + list_add_tail(&req->queue, &ep->queue); + + VDBG(dev, "<--- %s()\n", __func__); + return status; +} + + +/* SET_ADDRESS request routine */ +static void set_address(struct langwell_udc *dev, u16 value, + u16 index, u16 length) +{ + VDBG(dev, "---> %s()\n", __func__); + + /* save the new address to device struct */ + dev->dev_addr = (u8) value; + VDBG(dev, "dev->dev_addr = %d\n", dev->dev_addr); + + /* update usb state */ + dev->usb_state = USB_STATE_ADDRESS; + + /* STATUS phase */ + if (prime_status_phase(dev, EP_DIR_IN)) + ep0_stall(dev); + + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* return endpoint by windex */ +static struct langwell_ep *get_ep_by_windex(struct langwell_udc *dev, + u16 wIndex) +{ + struct langwell_ep *ep; + VDBG(dev, "---> %s()\n", __func__); + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) + return &dev->ep[0]; + + list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { + u8 bEndpointAddress; + if (!ep->desc) + continue; + + bEndpointAddress = ep->desc->bEndpointAddress; + if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) + continue; + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) + == (bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) + return ep; + } + + VDBG(dev, "<--- %s()\n", __func__); + return NULL; +} + + +/* return whether endpoint is stalled, 0: not stalled; 1: stalled */ +static int ep_is_stall(struct langwell_ep *ep) +{ + struct langwell_udc *dev = ep->dev; + u32 endptctrl; + int retval; + + VDBG(dev, "---> %s()\n", __func__); + + endptctrl = readl(&dev->op_regs->endptctrl[ep->ep_num]); + if (is_in(ep)) + retval = endptctrl & EPCTRL_TXS ? 1 : 0; + else + retval = endptctrl & EPCTRL_RXS ? 1 : 0; + + VDBG(dev, "<--- %s()\n", __func__); + return retval; +} + + +/* GET_STATUS request routine */ +static void get_status(struct langwell_udc *dev, u8 request_type, u16 value, + u16 index, u16 length) +{ + struct langwell_request *req; + struct langwell_ep *ep; + u16 status_data = 0; /* 16 bits cpu view status data */ + int status = 0; + + VDBG(dev, "---> %s()\n", __func__); + + ep = &dev->ep[0]; + + if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + /* get device status */ + status_data = 1 << USB_DEVICE_SELF_POWERED; + status_data |= dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { + /* get interface status */ + status_data = 0; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { + /* get endpoint status */ + struct langwell_ep *epn; + epn = get_ep_by_windex(dev, index); + /* stall if endpoint doesn't exist */ + if (!epn) + goto stall; + + status_data = ep_is_stall(epn) << USB_ENDPOINT_HALT; + } + + dev->ep0_dir = USB_DIR_IN; + + /* borrow the per device status_req */ + req = dev->status_req; + + /* fill in the reqest structure */ + *((u16 *) req->req.buf) = cpu_to_le16(status_data); + req->ep = ep; + req->req.length = 2; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + /* prime the data phase */ + if (!req_to_dtd(req)) + status = queue_dtd(ep, req); + else /* no mem */ + goto stall; + + if (status) { + ERROR(dev, "response error on GET_STATUS request\n"); + goto stall; + } + + list_add_tail(&req->queue, &ep->queue); + dev->ep0_state = DATA_STATE_XMIT; + + VDBG(dev, "<--- %s()\n", __func__); + return; +stall: + ep0_stall(dev); + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* setup packet interrupt handler */ +static void handle_setup_packet(struct langwell_udc *dev, + struct usb_ctrlrequest *setup) +{ + u16 wValue = le16_to_cpu(setup->wValue); + u16 wIndex = le16_to_cpu(setup->wIndex); + u16 wLength = le16_to_cpu(setup->wLength); + + VDBG(dev, "---> %s()\n", __func__); + + /* ep0 fifo flush */ + nuke(&dev->ep[0], -ESHUTDOWN); + + DBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", + setup->bRequestType, setup->bRequest, + wValue, wIndex, wLength); + + /* RNDIS gadget delegate */ + if ((setup->bRequestType == 0x21) && (setup->bRequest == 0x00)) { + /* USB_CDC_SEND_ENCAPSULATED_COMMAND */ + goto delegate; + } + + /* USB_CDC_GET_ENCAPSULATED_RESPONSE */ + if ((setup->bRequestType == 0xa1) && (setup->bRequest == 0x01)) { + /* USB_CDC_GET_ENCAPSULATED_RESPONSE */ + goto delegate; + } + + /* We process some stardard setup requests here */ + switch (setup->bRequest) { + case USB_REQ_GET_STATUS: + DBG(dev, "SETUP: USB_REQ_GET_STATUS\n"); + /* get status, DATA and STATUS phase */ + if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) + != (USB_DIR_IN | USB_TYPE_STANDARD)) + break; + get_status(dev, setup->bRequestType, wValue, wIndex, wLength); + goto end; + + case USB_REQ_SET_ADDRESS: + DBG(dev, "SETUP: USB_REQ_SET_ADDRESS\n"); + /* STATUS phase */ + if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD + | USB_RECIP_DEVICE)) + break; + set_address(dev, wValue, wIndex, wLength); + goto end; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* STATUS phase */ + { + int rc = -EOPNOTSUPP; + if (setup->bRequest == USB_REQ_SET_FEATURE) + DBG(dev, "SETUP: USB_REQ_SET_FEATURE\n"); + else if (setup->bRequest == USB_REQ_CLEAR_FEATURE) + DBG(dev, "SETUP: USB_REQ_CLEAR_FEATURE\n"); + + if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) + == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { + struct langwell_ep *epn; + epn = get_ep_by_windex(dev, wIndex); + /* stall if endpoint doesn't exist */ + if (!epn) { + ep0_stall(dev); + goto end; + } + + if (wValue != 0 || wLength != 0 + || epn->ep_num > dev->ep_max) + break; + + spin_unlock(&dev->lock); + rc = langwell_ep_set_halt(&epn->ep, + (setup->bRequest == USB_REQ_SET_FEATURE) + ? 1 : 0); + spin_lock(&dev->lock); + + } else if ((setup->bRequestType & (USB_RECIP_MASK + | USB_TYPE_MASK)) == (USB_RECIP_DEVICE + | USB_TYPE_STANDARD)) { + if (!gadget_is_otg(&dev->gadget)) + break; + else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) { + dev->gadget.b_hnp_enable = 1; +#ifdef OTG_TRANSCEIVER + if (!dev->lotg->otg.default_a) + dev->lotg->hsm.b_hnp_enable = 1; +#endif + } else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) + dev->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + dev->gadget.a_alt_hnp_support = 1; + else + break; + rc = 0; + } else + break; + + if (rc == 0) { + if (prime_status_phase(dev, EP_DIR_IN)) + ep0_stall(dev); + } + goto end; + } + + case USB_REQ_GET_DESCRIPTOR: + DBG(dev, "SETUP: USB_REQ_GET_DESCRIPTOR\n"); + goto delegate; + + case USB_REQ_SET_DESCRIPTOR: + DBG(dev, "SETUP: USB_REQ_SET_DESCRIPTOR unsupported\n"); + goto delegate; + + case USB_REQ_GET_CONFIGURATION: + DBG(dev, "SETUP: USB_REQ_GET_CONFIGURATION\n"); + goto delegate; + + case USB_REQ_SET_CONFIGURATION: + DBG(dev, "SETUP: USB_REQ_SET_CONFIGURATION\n"); + goto delegate; + + case USB_REQ_GET_INTERFACE: + DBG(dev, "SETUP: USB_REQ_GET_INTERFACE\n"); + goto delegate; + + case USB_REQ_SET_INTERFACE: + DBG(dev, "SETUP: USB_REQ_SET_INTERFACE\n"); + goto delegate; + + case USB_REQ_SYNCH_FRAME: + DBG(dev, "SETUP: USB_REQ_SYNCH_FRAME unsupported\n"); + goto delegate; + + default: + /* delegate USB standard requests to the gadget driver */ + goto delegate; +delegate: + /* USB requests handled by gadget */ + if (wLength) { + /* DATA phase from gadget, STATUS phase from udc */ + dev->ep0_dir = (setup->bRequestType & USB_DIR_IN) + ? USB_DIR_IN : USB_DIR_OUT; + VDBG(dev, "dev->ep0_dir = 0x%x, wLength = %d\n", + dev->ep0_dir, wLength); + spin_unlock(&dev->lock); + if (dev->driver->setup(&dev->gadget, + &dev->local_setup_buff) < 0) + ep0_stall(dev); + spin_lock(&dev->lock); + dev->ep0_state = (setup->bRequestType & USB_DIR_IN) + ? DATA_STATE_XMIT : DATA_STATE_RECV; + } else { + /* no DATA phase, IN STATUS phase from gadget */ + dev->ep0_dir = USB_DIR_IN; + VDBG(dev, "dev->ep0_dir = 0x%x, wLength = %d\n", + dev->ep0_dir, wLength); + spin_unlock(&dev->lock); + if (dev->driver->setup(&dev->gadget, + &dev->local_setup_buff) < 0) + ep0_stall(dev); + spin_lock(&dev->lock); + dev->ep0_state = WAIT_FOR_OUT_STATUS; + } + break; + } +end: + VDBG(dev, "<--- %s()\n", __func__); + return; +} + + +/* transfer completion, process endpoint request and free the completed dTDs + * for this request + */ +static int process_ep_req(struct langwell_udc *dev, int index, + struct langwell_request *curr_req) +{ + struct langwell_dtd *curr_dtd; + struct langwell_dqh *curr_dqh; + int td_complete, actual, remaining_length; + int i, dir; + u8 dtd_status = 0; + int retval = 0; + + curr_dqh = &dev->ep_dqh[index]; + dir = index % 2; + + curr_dtd = curr_req->head; + td_complete = 0; + actual = curr_req->req.length; + + VDBG(dev, "---> %s()\n", __func__); + + for (i = 0; i < curr_req->dtd_count; i++) { + remaining_length = le16_to_cpu(curr_dtd->dtd_total); + actual -= remaining_length; + + /* command execution states by dTD */ + dtd_status = curr_dtd->dtd_status; + + if (!dtd_status) { + /* transfers completed successfully */ + if (!remaining_length) { + td_complete++; + VDBG(dev, "dTD transmitted successfully\n"); + } else { + if (dir) { + VDBG(dev, "TX dTD remains data\n"); + retval = -EPROTO; + break; + + } else { + td_complete++; + break; + } + } + } else { + /* transfers completed with errors */ + if (dtd_status & DTD_STS_ACTIVE) { + DBG(dev, "request not completed\n"); + retval = 1; + return retval; + } else if (dtd_status & DTD_STS_HALTED) { + ERROR(dev, "dTD error %08x dQH[%d]\n", + dtd_status, index); + /* clear the errors and halt condition */ + curr_dqh->dtd_status = 0; + retval = -EPIPE; + break; + } else if (dtd_status & DTD_STS_DBE) { + DBG(dev, "data buffer (overflow) error\n"); + retval = -EPROTO; + break; + } else if (dtd_status & DTD_STS_TRE) { + DBG(dev, "transaction(ISO) error\n"); + retval = -EILSEQ; + break; + } else + ERROR(dev, "unknown error (0x%x)!\n", + dtd_status); + } + + if (i != curr_req->dtd_count - 1) + curr_dtd = (struct langwell_dtd *) + curr_dtd->next_dtd_virt; + } + + if (retval) + return retval; + + curr_req->req.actual = actual; + + VDBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* complete DATA or STATUS phase of ep0 prime status phase if needed */ +static void ep0_req_complete(struct langwell_udc *dev, + struct langwell_ep *ep0, struct langwell_request *req) +{ + u32 new_addr; + VDBG(dev, "---> %s()\n", __func__); + + if (dev->usb_state == USB_STATE_ADDRESS) { + /* set the new address */ + new_addr = (u32)dev->dev_addr; + writel(new_addr << USBADR_SHIFT, &dev->op_regs->deviceaddr); + + new_addr = USBADR(readl(&dev->op_regs->deviceaddr)); + VDBG(dev, "new_addr = %d\n", new_addr); + } + + done(ep0, req, 0); + + switch (dev->ep0_state) { + case DATA_STATE_XMIT: + /* receive status phase */ + if (prime_status_phase(dev, EP_DIR_OUT)) + ep0_stall(dev); + break; + case DATA_STATE_RECV: + /* send status phase */ + if (prime_status_phase(dev, EP_DIR_IN)) + ep0_stall(dev); + break; + case WAIT_FOR_OUT_STATUS: + dev->ep0_state = WAIT_FOR_SETUP; + break; + case WAIT_FOR_SETUP: + ERROR(dev, "unexpect ep0 packets\n"); + break; + default: + ep0_stall(dev); + break; + } + + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* USB transfer completion interrupt */ +static void handle_trans_complete(struct langwell_udc *dev) +{ + u32 complete_bits; + int i, ep_num, dir, bit_mask, status; + struct langwell_ep *epn; + struct langwell_request *curr_req, *temp_req; + + VDBG(dev, "---> %s()\n", __func__); + + complete_bits = readl(&dev->op_regs->endptcomplete); + VDBG(dev, "endptcomplete register: 0x%08x\n", complete_bits); + + /* Write-Clear the bits in endptcomplete register */ + writel(complete_bits, &dev->op_regs->endptcomplete); + + if (!complete_bits) { + DBG(dev, "complete_bits = 0\n"); + goto done; + } + + for (i = 0; i < dev->ep_max; i++) { + ep_num = i / 2; + dir = i % 2; + + bit_mask = 1 << (ep_num + 16 * dir); + + if (!(complete_bits & bit_mask)) + continue; + + /* ep0 */ + if (i == 1) + epn = &dev->ep[0]; + else + epn = &dev->ep[i]; + + if (epn->name == NULL) { + WARNING(dev, "invalid endpoint\n"); + continue; + } + + if (i < 2) + /* ep0 in and out */ + DBG(dev, "%s-%s transfer completed\n", + epn->name, + is_in(epn) ? "in" : "out"); + else + DBG(dev, "%s transfer completed\n", epn->name); + + /* process the req queue until an uncomplete request */ + list_for_each_entry_safe(curr_req, temp_req, + &epn->queue, queue) { + status = process_ep_req(dev, i, curr_req); + VDBG(dev, "%s req status: %d\n", epn->name, status); + + if (status) + break; + + /* write back status to req */ + curr_req->req.status = status; + + /* ep0 request completion */ + if (ep_num == 0) { + ep0_req_complete(dev, epn, curr_req); + break; + } else { + done(epn, curr_req, status); + } + } + } +done: + VDBG(dev, "<--- %s()\n", __func__); + return; +} + + +/* port change detect interrupt handler */ +static void handle_port_change(struct langwell_udc *dev) +{ + u32 portsc1, devlc; + u32 speed; + + VDBG(dev, "---> %s()\n", __func__); + + if (dev->bus_reset) + dev->bus_reset = 0; + + portsc1 = readl(&dev->op_regs->portsc1); + devlc = readl(&dev->op_regs->devlc); + VDBG(dev, "portsc1 = 0x%08x, devlc = 0x%08x\n", + portsc1, devlc); + + /* bus reset is finished */ + if (!(portsc1 & PORTS_PR)) { + /* get the speed */ + speed = LPM_PSPD(devlc); + switch (speed) { + case LPM_SPEED_HIGH: + dev->gadget.speed = USB_SPEED_HIGH; + break; + case LPM_SPEED_FULL: + dev->gadget.speed = USB_SPEED_FULL; + break; + case LPM_SPEED_LOW: + dev->gadget.speed = USB_SPEED_LOW; + break; + default: + dev->gadget.speed = USB_SPEED_UNKNOWN; + break; + } + VDBG(dev, "speed = %d, dev->gadget.speed = %d\n", + speed, dev->gadget.speed); + } + + /* LPM L0 to L1 */ + if (dev->lpm && dev->lpm_state == LPM_L0) + if (portsc1 & PORTS_SUSP && portsc1 & PORTS_SLP) { + INFO(dev, "LPM L0 to L1\n"); + dev->lpm_state = LPM_L1; + } + + /* LPM L1 to L0, force resume or remote wakeup finished */ + if (dev->lpm && dev->lpm_state == LPM_L1) + if (!(portsc1 & PORTS_SUSP)) { + if (portsc1 & PORTS_SLP) + INFO(dev, "LPM L1 to L0, force resume\n"); + else + INFO(dev, "LPM L1 to L0, remote wakeup\n"); + + dev->lpm_state = LPM_L0; + } + + /* update USB state */ + if (!dev->resume_state) + dev->usb_state = USB_STATE_DEFAULT; + + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* USB reset interrupt handler */ +static void handle_usb_reset(struct langwell_udc *dev) +{ + u32 deviceaddr, + endptsetupstat, + endptcomplete; + unsigned long timeout; + + VDBG(dev, "---> %s()\n", __func__); + + /* Write-Clear the device address */ + deviceaddr = readl(&dev->op_regs->deviceaddr); + writel(deviceaddr & ~USBADR_MASK, &dev->op_regs->deviceaddr); + + dev->dev_addr = 0; + + /* clear usb state */ + dev->resume_state = 0; + + /* LPM L1 to L0, reset */ + if (dev->lpm) + dev->lpm_state = LPM_L0; + + dev->ep0_dir = USB_DIR_OUT; + dev->ep0_state = WAIT_FOR_SETUP; + dev->remote_wakeup = 0; /* default to 0 on reset */ + dev->gadget.b_hnp_enable = 0; + dev->gadget.a_hnp_support = 0; + dev->gadget.a_alt_hnp_support = 0; + + /* Write-Clear all the setup token semaphores */ + endptsetupstat = readl(&dev->op_regs->endptsetupstat); + writel(endptsetupstat, &dev->op_regs->endptsetupstat); + + /* Write-Clear all the endpoint complete status bits */ + endptcomplete = readl(&dev->op_regs->endptcomplete); + writel(endptcomplete, &dev->op_regs->endptcomplete); + + /* wait until all endptprime bits cleared */ + timeout = jiffies + PRIME_TIMEOUT; + while (readl(&dev->op_regs->endptprime)) { + if (time_after(jiffies, timeout)) { + ERROR(dev, "USB reset timeout\n"); + break; + } + cpu_relax(); + } + + /* write 1s to endptflush register to clear any primed buffers */ + writel((u32) ~0, &dev->op_regs->endptflush); + + if (readl(&dev->op_regs->portsc1) & PORTS_PR) { + VDBG(dev, "USB bus reset\n"); + /* bus is reseting */ + dev->bus_reset = 1; + + /* reset all the queues, stop all USB activities */ + stop_activity(dev, dev->driver); + dev->usb_state = USB_STATE_DEFAULT; + } else { + VDBG(dev, "device controller reset\n"); + /* controller reset */ + langwell_udc_reset(dev); + + /* reset all the queues, stop all USB activities */ + stop_activity(dev, dev->driver); + + /* reset ep0 dQH and endptctrl */ + ep0_reset(dev); + + /* enable interrupt and set controller to run state */ + langwell_udc_start(dev); + + dev->usb_state = USB_STATE_ATTACHED; + } + +#ifdef OTG_TRANSCEIVER + /* refer to USB OTG 6.6.2.3 b_hnp_en is cleared */ + if (!dev->lotg->otg.default_a) + dev->lotg->hsm.b_hnp_enable = 0; +#endif + + VDBG(dev, "<--- %s()\n", __func__); +} + + +/* USB bus suspend/resume interrupt */ +static void handle_bus_suspend(struct langwell_udc *dev) +{ + u32 devlc; + DBG(dev, "---> %s()\n", __func__); + + dev->resume_state = dev->usb_state; + dev->usb_state = USB_STATE_SUSPENDED; + +#ifdef OTG_TRANSCEIVER + if (dev->lotg->otg.default_a) { + if (dev->lotg->hsm.b_bus_suspend_vld == 1) { + dev->lotg->hsm.b_bus_suspend = 1; + /* notify transceiver the state changes */ + if (spin_trylock(&dev->lotg->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&dev->lotg->wq_lock); + } + } + dev->lotg->hsm.b_bus_suspend_vld++; + } else { + if (!dev->lotg->hsm.a_bus_suspend) { + dev->lotg->hsm.a_bus_suspend = 1; + /* notify transceiver the state changes */ + if (spin_trylock(&dev->lotg->wq_lock)) { + langwell_update_transceiver(); + spin_unlock(&dev->lotg->wq_lock); + } + } + } +#endif + + /* report suspend to the driver */ + if (dev->driver) { + if (dev->driver->suspend) { + spin_unlock(&dev->lock); + dev->driver->suspend(&dev->gadget); + spin_lock(&dev->lock); + DBG(dev, "suspend %s\n", dev->driver->driver.name); + } + } + + /* enter PHY low power suspend */ + devlc = readl(&dev->op_regs->devlc); + VDBG(dev, "devlc = 0x%08x\n", devlc); + devlc |= LPM_PHCD; + writel(devlc, &dev->op_regs->devlc); + + DBG(dev, "<--- %s()\n", __func__); +} + + +static void handle_bus_resume(struct langwell_udc *dev) +{ + u32 devlc; + DBG(dev, "---> %s()\n", __func__); + + dev->usb_state = dev->resume_state; + dev->resume_state = 0; + + /* exit PHY low power suspend */ + devlc = readl(&dev->op_regs->devlc); + VDBG(dev, "devlc = 0x%08x\n", devlc); + devlc &= ~LPM_PHCD; + writel(devlc, &dev->op_regs->devlc); + +#ifdef OTG_TRANSCEIVER + if (dev->lotg->otg.default_a == 0) + dev->lotg->hsm.a_bus_suspend = 0; +#endif + + /* report resume to the driver */ + if (dev->driver) { + if (dev->driver->resume) { + spin_unlock(&dev->lock); + dev->driver->resume(&dev->gadget); + spin_lock(&dev->lock); + DBG(dev, "resume %s\n", dev->driver->driver.name); + } + } + + DBG(dev, "<--- %s()\n", __func__); +} + + +/* USB device controller interrupt handler */ +static irqreturn_t langwell_irq(int irq, void *_dev) +{ + struct langwell_udc *dev = _dev; + u32 usbsts, + usbintr, + irq_sts, + portsc1; + + VDBG(dev, "---> %s()\n", __func__); + + if (dev->stopped) { + VDBG(dev, "handle IRQ_NONE\n"); + VDBG(dev, "<--- %s()\n", __func__); + return IRQ_NONE; + } + + spin_lock(&dev->lock); + + /* USB status */ + usbsts = readl(&dev->op_regs->usbsts); + + /* USB interrupt enable */ + usbintr = readl(&dev->op_regs->usbintr); + + irq_sts = usbsts & usbintr; + VDBG(dev, "usbsts = 0x%08x, usbintr = 0x%08x, irq_sts = 0x%08x\n", + usbsts, usbintr, irq_sts); + + if (!irq_sts) { + VDBG(dev, "handle IRQ_NONE\n"); + VDBG(dev, "<--- %s()\n", __func__); + spin_unlock(&dev->lock); + return IRQ_NONE; + } + + /* Write-Clear interrupt status bits */ + writel(irq_sts, &dev->op_regs->usbsts); + + /* resume from suspend */ + portsc1 = readl(&dev->op_regs->portsc1); + if (dev->usb_state == USB_STATE_SUSPENDED) + if (!(portsc1 & PORTS_SUSP)) + handle_bus_resume(dev); + + /* USB interrupt */ + if (irq_sts & STS_UI) { + VDBG(dev, "USB interrupt\n"); + + /* setup packet received from ep0 */ + if (readl(&dev->op_regs->endptsetupstat) + & EP0SETUPSTAT_MASK) { + VDBG(dev, "USB SETUP packet received interrupt\n"); + /* setup tripwire semaphone */ + setup_tripwire(dev); + handle_setup_packet(dev, &dev->local_setup_buff); + } + + /* USB transfer completion */ + if (readl(&dev->op_regs->endptcomplete)) { + VDBG(dev, "USB transfer completion interrupt\n"); + handle_trans_complete(dev); + } + } + + /* SOF received interrupt (for ISO transfer) */ + if (irq_sts & STS_SRI) { + /* FIXME */ + /* VDBG(dev, "SOF received interrupt\n"); */ + } + + /* port change detect interrupt */ + if (irq_sts & STS_PCI) { + VDBG(dev, "port change detect interrupt\n"); + handle_port_change(dev); + } + + /* suspend interrrupt */ + if (irq_sts & STS_SLI) { + VDBG(dev, "suspend interrupt\n"); + handle_bus_suspend(dev); + } + + /* USB reset interrupt */ + if (irq_sts & STS_URI) { + VDBG(dev, "USB reset interrupt\n"); + handle_usb_reset(dev); + } + + /* USB error or system error interrupt */ + if (irq_sts & (STS_UEI | STS_SEI)) { + /* FIXME */ + WARNING(dev, "error IRQ, irq_sts: %x\n", irq_sts); + } + + spin_unlock(&dev->lock); + + VDBG(dev, "<--- %s()\n", __func__); + return IRQ_HANDLED; +} + + +/*-------------------------------------------------------------------------*/ + +/* release device structure */ +static void gadget_release(struct device *_dev) +{ + struct langwell_udc *dev = the_controller; + + DBG(dev, "---> %s()\n", __func__); + + complete(dev->done); + + DBG(dev, "<--- %s()\n", __func__); + kfree(dev); +} + + +/* tear down the binding between this driver and the pci device */ +static void langwell_udc_remove(struct pci_dev *pdev) +{ + struct langwell_udc *dev = the_controller; + + DECLARE_COMPLETION(done); + + BUG_ON(dev->driver); + DBG(dev, "---> %s()\n", __func__); + + dev->done = &done; + + /* free memory allocated in probe */ + if (dev->dtd_pool) + dma_pool_destroy(dev->dtd_pool); + + if (dev->status_req) { + kfree(dev->status_req->req.buf); + kfree(dev->status_req); + } + + if (dev->ep_dqh) + dma_free_coherent(&pdev->dev, dev->ep_dqh_size, + dev->ep_dqh, dev->ep_dqh_dma); + + kfree(dev->ep); + + /* diable IRQ handler */ + if (dev->got_irq) + free_irq(pdev->irq, dev); + +#ifndef OTG_TRANSCEIVER + if (dev->cap_regs) + iounmap(dev->cap_regs); + + if (dev->region) + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + + if (dev->enabled) + pci_disable_device(pdev); +#else + if (dev->transceiver) { + otg_put_transceiver(dev->transceiver); + dev->transceiver = NULL; + dev->lotg = NULL; + } +#endif + + dev->cap_regs = NULL; + + INFO(dev, "unbind\n"); + DBG(dev, "<--- %s()\n", __func__); + + device_unregister(&dev->gadget.dev); + device_remove_file(&pdev->dev, &dev_attr_langwell_udc); + +#ifndef OTG_TRANSCEIVER + pci_set_drvdata(pdev, NULL); +#endif + + /* free dev, wait for the release() finished */ + wait_for_completion(&done); + + the_controller = NULL; +} + + +/* + * wrap this driver around the specified device, but + * don't respond over USB until a gadget driver binds to us. + */ +static int langwell_udc_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct langwell_udc *dev; +#ifndef OTG_TRANSCEIVER + unsigned long resource, len; +#endif + void __iomem *base = NULL; + size_t size; + int retval; + + if (the_controller) { + dev_warn(&pdev->dev, "ignoring\n"); + return -EBUSY; + } + + /* alloc, and start init */ + dev = kzalloc(sizeof *dev, GFP_KERNEL); + if (dev == NULL) { + retval = -ENOMEM; + goto error; + } + + /* initialize device spinlock */ + spin_lock_init(&dev->lock); + + dev->pdev = pdev; + DBG(dev, "---> %s()\n", __func__); + +#ifdef OTG_TRANSCEIVER + /* PCI device is already enabled by otg_transceiver driver */ + dev->enabled = 1; + + /* mem region and register base */ + dev->region = 1; + dev->transceiver = otg_get_transceiver(); + dev->lotg = otg_to_langwell(dev->transceiver); + base = dev->lotg->regs; +#else + pci_set_drvdata(pdev, dev); + + /* now all the pci goodies ... */ + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto error; + } + dev->enabled = 1; + + /* control register: BAR 0 */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!request_mem_region(resource, len, driver_name)) { + ERROR(dev, "controller already in use\n"); + retval = -EBUSY; + goto error; + } + dev->region = 1; + + base = ioremap_nocache(resource, len); +#endif + if (base == NULL) { + ERROR(dev, "can't map memory\n"); + retval = -EFAULT; + goto error; + } + + dev->cap_regs = (struct langwell_cap_regs __iomem *) base; + VDBG(dev, "dev->cap_regs: %p\n", dev->cap_regs); + dev->op_regs = (struct langwell_op_regs __iomem *) + (base + OP_REG_OFFSET); + VDBG(dev, "dev->op_regs: %p\n", dev->op_regs); + + /* irq setup after old hardware is cleaned up */ + if (!pdev->irq) { + ERROR(dev, "No IRQ. Check PCI setup!\n"); + retval = -ENODEV; + goto error; + } + +#ifndef OTG_TRANSCEIVER + INFO(dev, "irq %d, io mem: 0x%08lx, len: 0x%08lx, pci mem 0x%p\n", + pdev->irq, resource, len, base); + /* enables bus-mastering for device dev */ + pci_set_master(pdev); + + if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED, + driver_name, dev) != 0) { + ERROR(dev, "request interrupt %d failed\n", pdev->irq); + retval = -EBUSY; + goto error; + } + dev->got_irq = 1; +#endif + + /* set stopped bit */ + dev->stopped = 1; + + /* capabilities and endpoint number */ + dev->lpm = (readl(&dev->cap_regs->hccparams) & HCC_LEN) ? 1 : 0; + dev->dciversion = readw(&dev->cap_regs->dciversion); + dev->devcap = (readl(&dev->cap_regs->dccparams) & DEVCAP) ? 1 : 0; + VDBG(dev, "dev->lpm: %d\n", dev->lpm); + VDBG(dev, "dev->dciversion: 0x%04x\n", dev->dciversion); + VDBG(dev, "dccparams: 0x%08x\n", readl(&dev->cap_regs->dccparams)); + VDBG(dev, "dev->devcap: %d\n", dev->devcap); + if (!dev->devcap) { + ERROR(dev, "can't support device mode\n"); + retval = -ENODEV; + goto error; + } + + /* a pair of endpoints (out/in) for each address */ + dev->ep_max = DEN(readl(&dev->cap_regs->dccparams)) * 2; + VDBG(dev, "dev->ep_max: %d\n", dev->ep_max); + + /* allocate endpoints memory */ + dev->ep = kzalloc(sizeof(struct langwell_ep) * dev->ep_max, + GFP_KERNEL); + if (!dev->ep) { + ERROR(dev, "allocate endpoints memory failed\n"); + retval = -ENOMEM; + goto error; + } + + /* allocate device dQH memory */ + size = dev->ep_max * sizeof(struct langwell_dqh); + VDBG(dev, "orig size = %d\n", size); + if (size < DQH_ALIGNMENT) + size = DQH_ALIGNMENT; + else if ((size % DQH_ALIGNMENT) != 0) { + size += DQH_ALIGNMENT + 1; + size &= ~(DQH_ALIGNMENT - 1); + } + dev->ep_dqh = dma_alloc_coherent(&pdev->dev, size, + &dev->ep_dqh_dma, GFP_KERNEL); + if (!dev->ep_dqh) { + ERROR(dev, "allocate dQH memory failed\n"); + retval = -ENOMEM; + goto error; + } + dev->ep_dqh_size = size; + VDBG(dev, "ep_dqh_size = %d\n", dev->ep_dqh_size); + + /* initialize ep0 status request structure */ + dev->status_req = kzalloc(sizeof(struct langwell_request), GFP_KERNEL); + if (!dev->status_req) { + ERROR(dev, "allocate status_req memory failed\n"); + retval = -ENOMEM; + goto error; + } + INIT_LIST_HEAD(&dev->status_req->queue); + + /* allocate a small amount of memory to get valid address */ + dev->status_req->req.buf = kmalloc(8, GFP_KERNEL); + dev->status_req->req.dma = virt_to_phys(dev->status_req->req.buf); + + dev->resume_state = USB_STATE_NOTATTACHED; + dev->usb_state = USB_STATE_POWERED; + dev->ep0_dir = USB_DIR_OUT; + dev->remote_wakeup = 0; /* default to 0 on reset */ + +#ifndef OTG_TRANSCEIVER + /* reset device controller */ + langwell_udc_reset(dev); +#endif + + /* initialize gadget structure */ + dev->gadget.ops = &langwell_ops; /* usb_gadget_ops */ + dev->gadget.ep0 = &dev->ep[0].ep; /* gadget ep0 */ + INIT_LIST_HEAD(&dev->gadget.ep_list); /* ep_list */ + dev->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ + dev->gadget.is_dualspeed = 1; /* support dual speed */ +#ifdef OTG_TRANSCEIVER + dev->gadget.is_otg = 1; /* support otg mode */ +#endif + + /* the "gadget" abstracts/virtualizes the controller */ + dev_set_name(&dev->gadget.dev, "gadget"); + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + dev->gadget.dev.release = gadget_release; + dev->gadget.name = driver_name; /* gadget name */ + + /* controller endpoints reinit */ + eps_reinit(dev); + +#ifndef OTG_TRANSCEIVER + /* reset ep0 dQH and endptctrl */ + ep0_reset(dev); +#endif + + /* create dTD dma_pool resource */ + dev->dtd_pool = dma_pool_create("langwell_dtd", + &dev->pdev->dev, + sizeof(struct langwell_dtd), + DTD_ALIGNMENT, + DMA_BOUNDARY); + + if (!dev->dtd_pool) { + retval = -ENOMEM; + goto error; + } + + /* done */ + INFO(dev, "%s\n", driver_desc); + INFO(dev, "irq %d, pci mem %p\n", pdev->irq, base); + INFO(dev, "Driver version: " DRIVER_VERSION "\n"); + INFO(dev, "Support (max) %d endpoints\n", dev->ep_max); + INFO(dev, "Device interface version: 0x%04x\n", dev->dciversion); + INFO(dev, "Controller mode: %s\n", dev->devcap ? "Device" : "Host"); + INFO(dev, "Support USB LPM: %s\n", dev->lpm ? "Yes" : "No"); + + VDBG(dev, "After langwell_udc_probe(), print all registers:\n"); +#ifdef VERBOSE + print_all_registers(dev); +#endif + + the_controller = dev; + + retval = device_register(&dev->gadget.dev); + if (retval) + goto error; + + retval = device_create_file(&pdev->dev, &dev_attr_langwell_udc); + if (retval) + goto error; + + VDBG(dev, "<--- %s()\n", __func__); + return 0; + +error: + if (dev) { + DBG(dev, "<--- %s()\n", __func__); + langwell_udc_remove(pdev); + } + + return retval; +} + + +/* device controller suspend */ +static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct langwell_udc *dev = the_controller; + u32 devlc; + + DBG(dev, "---> %s()\n", __func__); + + /* disable interrupt and set controller to stop state */ + langwell_udc_stop(dev); + + /* diable IRQ handler */ + if (dev->got_irq) + free_irq(pdev->irq, dev); + dev->got_irq = 0; + + + /* save PCI state */ + pci_save_state(pdev); + + /* set device power state */ + pci_set_power_state(pdev, PCI_D3hot); + + /* enter PHY low power suspend */ + devlc = readl(&dev->op_regs->devlc); + VDBG(dev, "devlc = 0x%08x\n", devlc); + devlc |= LPM_PHCD; + writel(devlc, &dev->op_regs->devlc); + + DBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* device controller resume */ +static int langwell_udc_resume(struct pci_dev *pdev) +{ + struct langwell_udc *dev = the_controller; + u32 devlc; + + DBG(dev, "---> %s()\n", __func__); + + /* exit PHY low power suspend */ + devlc = readl(&dev->op_regs->devlc); + VDBG(dev, "devlc = 0x%08x\n", devlc); + devlc &= ~LPM_PHCD; + writel(devlc, &dev->op_regs->devlc); + + /* set device D0 power state */ + pci_set_power_state(pdev, PCI_D0); + + /* restore PCI state */ + pci_restore_state(pdev); + + /* enable IRQ handler */ + if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED, driver_name, dev) + != 0) { + ERROR(dev, "request interrupt %d failed\n", pdev->irq); + return -1; + } + dev->got_irq = 1; + + /* reset and start controller to run state */ + if (dev->stopped) { + /* reset device controller */ + langwell_udc_reset(dev); + + /* reset ep0 dQH and endptctrl */ + ep0_reset(dev); + + /* start device if gadget is loaded */ + if (dev->driver) + langwell_udc_start(dev); + } + + /* reset USB status */ + dev->usb_state = USB_STATE_ATTACHED; + dev->ep0_state = WAIT_FOR_SETUP; + dev->ep0_dir = USB_DIR_OUT; + + DBG(dev, "<--- %s()\n", __func__); + return 0; +} + + +/* pci driver shutdown */ +static void langwell_udc_shutdown(struct pci_dev *pdev) +{ + struct langwell_udc *dev = the_controller; + u32 usbmode; + + DBG(dev, "---> %s()\n", __func__); + + /* reset controller mode to IDLE */ + usbmode = readl(&dev->op_regs->usbmode); + DBG(dev, "usbmode = 0x%08x\n", usbmode); + usbmode &= (~3 | MODE_IDLE); + writel(usbmode, &dev->op_regs->usbmode); + + DBG(dev, "<--- %s()\n", __func__); +} + +/*-------------------------------------------------------------------------*/ + +static const struct pci_device_id pci_ids[] = { { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = 0x8086, + .device = 0x0811, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +}, { /* end: all zeroes */ } +}; + + +MODULE_DEVICE_TABLE(pci, pci_ids); + + +static struct pci_driver langwell_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_ids, + + .probe = langwell_udc_probe, + .remove = langwell_udc_remove, + + /* device controller suspend/resume */ + .suspend = langwell_udc_suspend, + .resume = langwell_udc_resume, + + .shutdown = langwell_udc_shutdown, +}; + + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Xiaochen Shen "); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + + +static int __init init(void) +{ +#ifdef OTG_TRANSCEIVER + return langwell_register_peripheral(&langwell_pci_driver); +#else + return pci_register_driver(&langwell_pci_driver); +#endif +} +module_init(init); + + +static void __exit cleanup(void) +{ +#ifdef OTG_TRANSCEIVER + return langwell_unregister_peripheral(&langwell_pci_driver); +#else + pci_unregister_driver(&langwell_pci_driver); +#endif +} +module_exit(cleanup); + diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h new file mode 100644 index 00000000000..9719934e1c0 --- /dev/null +++ b/drivers/usb/gadget/langwell_udc.h @@ -0,0 +1,228 @@ +/* + * Intel Langwell USB Device Controller driver + * Copyright (C) 2008-2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include + +#if defined(CONFIG_USB_LANGWELL_OTG) +#include +#endif + + +/*-------------------------------------------------------------------------*/ + +/* driver data structures and utilities */ + +/* + * dTD: Device Endpoint Transfer Descriptor + * describe to the device controller the location and quantity of + * data to be send/received for given transfer + */ +struct langwell_dtd { + u32 dtd_next; +/* bits 31:5, next transfer element pointer */ +#define DTD_NEXT(d) (((d)>>5)&0x7ffffff) +#define DTD_NEXT_MASK (0x7ffffff << 5) +/* terminate */ +#define DTD_TERM BIT(0) + /* bits 7:0, execution back states */ + u32 dtd_status:8; +#define DTD_STATUS(d) (((d)>>0)&0xff) +#define DTD_STS_ACTIVE BIT(7) /* active */ +#define DTD_STS_HALTED BIT(6) /* halted */ +#define DTD_STS_DBE BIT(5) /* data buffer error */ +#define DTD_STS_TRE BIT(3) /* transaction error */ + /* bits 9:8 */ + u32 dtd_res0:2; + /* bits 11:10, multipier override */ + u32 dtd_multo:2; +#define DTD_MULTO (BIT(11) | BIT(10)) + /* bits 14:12 */ + u32 dtd_res1:3; + /* bit 15, interrupt on complete */ + u32 dtd_ioc:1; +#define DTD_IOC BIT(15) + /* bits 30:16, total bytes */ + u32 dtd_total:15; +#define DTD_TOTAL(d) (((d)>>16)&0x7fff) +#define DTD_MAX_TRANSFER_LENGTH 0x4000 + /* bit 31 */ + u32 dtd_res2:1; + /* dTD buffer pointer page 0 to 4 */ + u32 dtd_buf[5]; +#define DTD_OFFSET_MASK 0xfff +/* bits 31:12, buffer pointer */ +#define DTD_BUFFER(d) (((d)>>12)&0x3ff) +/* bits 11:0, current offset */ +#define DTD_C_OFFSET(d) (((d)>>0)&0xfff) +/* bits 10:0, frame number */ +#define DTD_FRAME(d) (((d)>>0)&0x7ff) + + /* driver-private parts */ + + /* dtd dma address */ + dma_addr_t dtd_dma; + /* next dtd virtual address */ + struct langwell_dtd *next_dtd_virt; +}; + + +/* + * dQH: Device Endpoint Queue Head + * describe where all transfers are managed + * 48-byte data structure, aligned on 64-byte boundary + * + * These are associated with dTD structure + */ +struct langwell_dqh { + /* endpoint capabilities and characteristics */ + u32 dqh_res0:15; /* bits 14:0 */ + u32 dqh_ios:1; /* bit 15, interrupt on setup */ +#define DQH_IOS BIT(15) + u32 dqh_mpl:11; /* bits 26:16, maximum packet length */ +#define DQH_MPL (0x7ff << 16) + u32 dqh_res1:2; /* bits 28:27 */ + u32 dqh_zlt:1; /* bit 29, zero length termination */ +#define DQH_ZLT BIT(29) + u32 dqh_mult:2; /* bits 31:30 */ +#define DQH_MULT (BIT(30) | BIT(31)) + + /* current dTD pointer */ + u32 dqh_current; /* locate the transfer in progress */ +#define DQH_C_DTD(e) \ + (((e)>>5)&0x7ffffff) /* bits 31:5, current dTD pointer */ + + /* transfer overlay, hardware parts of a struct langwell_dtd */ + u32 dtd_next; + u32 dtd_status:8; /* bits 7:0, execution back states */ + u32 dtd_res0:2; /* bits 9:8 */ + u32 dtd_multo:2; /* bits 11:10, multipier override */ + u32 dtd_res1:3; /* bits 14:12 */ + u32 dtd_ioc:1; /* bit 15, interrupt on complete */ + u32 dtd_total:15; /* bits 30:16, total bytes */ + u32 dtd_res2:1; /* bit 31 */ + u32 dtd_buf[5]; /* dTD buffer pointer page 0 to 4 */ + + u32 dqh_res2; + struct usb_ctrlrequest dqh_setup; /* setup packet buffer */ +} __attribute__ ((aligned(64))); + + +/* endpoint data structure */ +struct langwell_ep { + struct usb_ep ep; + dma_addr_t dma; + struct langwell_udc *dev; + unsigned long irqs; + struct list_head queue; + struct langwell_dqh *dqh; + const struct usb_endpoint_descriptor *desc; + char name[14]; + unsigned stopped:1, + ep_type:2, + ep_num:8; +}; + + +/* request data structure */ +struct langwell_request { + struct usb_request req; + struct langwell_dtd *dtd, *head, *tail; + struct langwell_ep *ep; + dma_addr_t dtd_dma; + struct list_head queue; + unsigned dtd_count; + unsigned mapped:1; +}; + + +/* ep0 transfer state */ +enum ep0_state { + WAIT_FOR_SETUP, + DATA_STATE_XMIT, + DATA_STATE_NEED_ZLP, + WAIT_FOR_OUT_STATUS, + DATA_STATE_RECV, +}; + + +/* device suspend state */ +enum lpm_state { + LPM_L0, /* on */ + LPM_L1, /* LPM L1 sleep */ + LPM_L2, /* suspend */ + LPM_L3, /* off */ +}; + + +/* device data structure */ +struct langwell_udc { + /* each pci device provides one gadget, several endpoints */ + struct usb_gadget gadget; + spinlock_t lock; /* device lock */ + struct langwell_ep *ep; + struct usb_gadget_driver *driver; + struct otg_transceiver *transceiver; + u8 dev_addr; + u32 usb_state; + u32 resume_state; + u32 bus_reset; + enum lpm_state lpm_state; + enum ep0_state ep0_state; + u32 ep0_dir; + u16 dciversion; + unsigned ep_max; + unsigned devcap:1, + enabled:1, + region:1, + got_irq:1, + powered:1, + remote_wakeup:1, + rate:1, + is_reset:1, + softconnected:1, + vbus_active:1, + suspended:1, + stopped:1, + lpm:1; /* LPM capability */ + + /* pci state used to access those endpoints */ + struct pci_dev *pdev; + + /* Langwell otg transceiver */ + struct langwell_otg *lotg; + + /* control registers */ + struct langwell_cap_regs __iomem *cap_regs; + struct langwell_op_regs __iomem *op_regs; + + struct usb_ctrlrequest local_setup_buff; + struct langwell_dqh *ep_dqh; + size_t ep_dqh_size; + dma_addr_t ep_dqh_dma; + + /* ep0 status request */ + struct langwell_request *status_req; + + /* dma pool */ + struct dma_pool *dtd_pool; + + /* make sure release() is done */ + struct completion *done; +}; + diff --git a/include/linux/usb/langwell_udc.h b/include/linux/usb/langwell_udc.h new file mode 100644 index 00000000000..c949178a653 --- /dev/null +++ b/include/linux/usb/langwell_udc.h @@ -0,0 +1,310 @@ +/* + * Intel Langwell USB Device Controller driver + * Copyright (C) 2008-2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __LANGWELL_UDC_H +#define __LANGWELL_UDC_H + + +/* MACRO defines */ +#define CAP_REG_OFFSET 0x0 +#define OP_REG_OFFSET 0x28 + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +#define DQH_ALIGNMENT 2048 +#define DTD_ALIGNMENT 64 +#define DMA_BOUNDARY 4096 + +#define EP0_MAX_PKT_SIZE 64 +#define EP_DIR_IN 1 +#define EP_DIR_OUT 0 + +#define FLUSH_TIMEOUT 1000 +#define RESET_TIMEOUT 1000 +#define SETUPSTAT_TIMEOUT 100 +#define PRIME_TIMEOUT 100 + + +/* device memory space registers */ + +/* Capability Registers, BAR0 + CAP_REG_OFFSET */ +struct langwell_cap_regs { + /* offset: 0x0 */ + u8 caplength; /* offset of Operational Register */ + u8 _reserved3; + u16 hciversion; /* H: BCD encoding of host version */ + u32 hcsparams; /* H: host port steering logic capability */ + u32 hccparams; /* H: host multiple mode control capability */ +#define HCC_LEN BIT(17) /* Link power management (LPM) capability */ + u8 _reserved4[0x20-0xc]; + /* offset: 0x20 */ + u16 dciversion; /* BCD encoding of device version */ + u8 _reserved5[0x24-0x22]; + u32 dccparams; /* overall device controller capability */ +#define HOSTCAP BIT(8) /* host capable */ +#define DEVCAP BIT(7) /* device capable */ +#define DEN(d) \ + (((d)>>0)&0x1f) /* bits 4:0, device endpoint number */ +} __attribute__ ((packed)); + + +/* Operational Registers, BAR0 + OP_REG_OFFSET */ +struct langwell_op_regs { + /* offset: 0x28 */ + u32 extsts; +#define EXTS_TI1 BIT(4) /* general purpose timer interrupt 1 */ +#define EXTS_TI1TI0 BIT(3) /* general purpose timer interrupt 0 */ +#define EXTS_TI1UPI BIT(2) /* USB host periodic interrupt */ +#define EXTS_TI1UAI BIT(1) /* USB host asynchronous interrupt */ +#define EXTS_TI1NAKI BIT(0) /* NAK interrupt */ + u32 extintr; +#define EXTI_TIE1 BIT(4) /* general purpose timer interrupt enable 1 */ +#define EXTI_TIE0 BIT(3) /* general purpose timer interrupt enable 0 */ +#define EXTI_UPIE BIT(2) /* USB host periodic interrupt enable */ +#define EXTI_UAIE BIT(1) /* USB host asynchronous interrupt enable */ +#define EXTI_NAKE BIT(0) /* NAK interrupt enable */ + /* offset: 0x30 */ + u32 usbcmd; +#define CMD_HIRD(u) \ + (((u)>>24)&0xf) /* bits 27:24, host init resume duration */ +#define CMD_ITC(u) \ + (((u)>>16)&0xff) /* bits 23:16, interrupt threshold control */ +#define CMD_PPE BIT(15) /* per-port change events enable */ +#define CMD_ATDTW BIT(14) /* add dTD tripwire */ +#define CMD_SUTW BIT(13) /* setup tripwire */ +#define CMD_ASPE BIT(11) /* asynchronous schedule park mode enable */ +#define CMD_FS2 BIT(10) /* frame list size */ +#define CMD_ASP1 BIT(9) /* asynchronous schedule park mode count */ +#define CMD_ASP0 BIT(8) +#define CMD_LR BIT(7) /* light host/device controller reset */ +#define CMD_IAA BIT(6) /* interrupt on async advance doorbell */ +#define CMD_ASE BIT(5) /* asynchronous schedule enable */ +#define CMD_PSE BIT(4) /* periodic schedule enable */ +#define CMD_FS1 BIT(3) +#define CMD_FS0 BIT(2) +#define CMD_RST BIT(1) /* controller reset */ +#define CMD_RUNSTOP BIT(0) /* run/stop */ + u32 usbsts; +#define STS_PPCI(u) \ + (((u)>>16)&0xffff) /* bits 31:16, port-n change detect */ +#define STS_AS BIT(15) /* asynchronous schedule status */ +#define STS_PS BIT(14) /* periodic schedule status */ +#define STS_RCL BIT(13) /* reclamation */ +#define STS_HCH BIT(12) /* HC halted */ +#define STS_ULPII BIT(10) /* ULPI interrupt */ +#define STS_SLI BIT(8) /* DC suspend */ +#define STS_SRI BIT(7) /* SOF received */ +#define STS_URI BIT(6) /* USB reset received */ +#define STS_AAI BIT(5) /* interrupt on async advance */ +#define STS_SEI BIT(4) /* system error */ +#define STS_FRI BIT(3) /* frame list rollover */ +#define STS_PCI BIT(2) /* port change detect */ +#define STS_UEI BIT(1) /* USB error interrupt */ +#define STS_UI BIT(0) /* USB interrupt */ + u32 usbintr; +/* bits 31:16, per-port interrupt enable */ +#define INTR_PPCE(u) (((u)>>16)&0xffff) +#define INTR_ULPIE BIT(10) /* ULPI enable */ +#define INTR_SLE BIT(8) /* DC sleep/suspend enable */ +#define INTR_SRE BIT(7) /* SOF received enable */ +#define INTR_URE BIT(6) /* USB reset enable */ +#define INTR_AAE BIT(5) /* interrupt on async advance enable */ +#define INTR_SEE BIT(4) /* system error enable */ +#define INTR_FRE BIT(3) /* frame list rollover enable */ +#define INTR_PCE BIT(2) /* port change detect enable */ +#define INTR_UEE BIT(1) /* USB error interrupt enable */ +#define INTR_UE BIT(0) /* USB interrupt enable */ + u32 frindex; /* frame index */ +#define FRINDEX_MASK (0x3fff << 0) + u32 ctrldssegment; /* not used */ + u32 deviceaddr; +#define USBADR_SHIFT 25 +#define USBADR(d) \ + (((d)>>25)&0x7f) /* bits 31:25, device address */ +#define USBADR_MASK (0x7f << 25) +#define USBADRA BIT(24) /* device address advance */ + u32 endpointlistaddr;/* endpoint list top memory address */ +/* bits 31:11, endpoint list pointer */ +#define EPBASE(d) (((d)>>11)&0x1fffff) +#define ENDPOINTLISTADDR_MASK (0x1fffff << 11) + u32 ttctrl; /* H: TT operatin, not used */ + /* offset: 0x50 */ + u32 burstsize; /* burst size of data movement */ +#define TXPBURST(b) \ + (((b)>>8)&0xff) /* bits 15:8, TX burst length */ +#define RXPBURST(b) \ + (((b)>>0)&0xff) /* bits 7:0, RX burst length */ + u32 txfilltuning; /* TX tuning */ + u32 txttfilltuning; /* H: TX TT tuning */ + u32 ic_usb; /* control the IC_USB FS/LS transceiver */ + /* offset: 0x60 */ + u32 ulpi_viewport; /* indirect access to ULPI PHY */ +#define ULPIWU BIT(31) /* ULPI wakeup */ +#define ULPIRUN BIT(30) /* ULPI read/write run */ +#define ULPIRW BIT(29) /* ULPI read/write control */ +#define ULPISS BIT(27) /* ULPI sync state */ +#define ULPIPORT(u) \ + (((u)>>24)&7) /* bits 26:24, ULPI port number */ +#define ULPIADDR(u) \ + (((u)>>16)&0xff) /* bits 23:16, ULPI data address */ +#define ULPIDATRD(u) \ + (((u)>>8)&0xff) /* bits 15:8, ULPI data read */ +#define ULPIDATWR(u) \ + (((u)>>0)&0xff) /* bits 7:0, ULPI date write */ + u8 _reserved6[0x70-0x64]; + /* offset: 0x70 */ + u32 configflag; /* H: not used */ + u32 portsc1; /* port status */ +#define DA(p) \ + (((p)>>25)&0x7f) /* bits 31:25, device address */ +#define PORTS_SSTS (BIT(24) | BIT(23)) /* suspend status */ +#define PORTS_WKOC BIT(22) /* wake on over-current enable */ +#define PORTS_WKDS BIT(21) /* wake on disconnect enable */ +#define PORTS_WKCN BIT(20) /* wake on connect enable */ +#define PORTS_PTC(p) (((p)>>16)&0xf) /* bits 19:16, port test control */ +#define PORTS_PIC (BIT(15) | BIT(14)) /* port indicator control */ +#define PORTS_PO BIT(13) /* port owner */ +#define PORTS_PP BIT(12) /* port power */ +#define PORTS_LS (BIT(11) | BIT(10)) /* line status */ +#define PORTS_SLP BIT(9) /* suspend using L1 */ +#define PORTS_PR BIT(8) /* port reset */ +#define PORTS_SUSP BIT(7) /* suspend */ +#define PORTS_FPR BIT(6) /* force port resume */ +#define PORTS_OCC BIT(5) /* over-current change */ +#define PORTS_OCA BIT(4) /* over-current active */ +#define PORTS_PEC BIT(3) /* port enable/disable change */ +#define PORTS_PE BIT(2) /* port enable/disable */ +#define PORTS_CSC BIT(1) /* connect status change */ +#define PORTS_CCS BIT(0) /* current connect status */ + u8 _reserved7[0xb4-0x78]; + /* offset: 0xb4 */ + u32 devlc; /* control LPM and each USB port behavior */ +/* bits 31:29, parallel transceiver select */ +#define LPM_PTS(d) (((d)>>29)&7) +#define LPM_STS BIT(28) /* serial transceiver select */ +#define LPM_PTW BIT(27) /* parallel transceiver width */ +#define LPM_PSPD(d) (((d)>>25)&3) /* bits 26:25, port speed */ +#define LPM_PSPD_MASK (BIT(26) | BIT(25)) +#define LPM_SPEED_FULL 0 +#define LPM_SPEED_LOW 1 +#define LPM_SPEED_HIGH 2 +#define LPM_SRT BIT(24) /* shorten reset time */ +#define LPM_PFSC BIT(23) /* port force full speed connect */ +#define LPM_PHCD BIT(22) /* PHY low power suspend clock disable */ +#define LPM_STL BIT(16) /* STALL reply to LPM token */ +#define LPM_BA(d) \ + (((d)>>1)&0x7ff) /* bits 11:1, BmAttributes */ +#define LPM_NYT_ACK BIT(0) /* NYET/ACK reply to LPM token */ + u8 _reserved8[0xf4-0xb8]; + /* offset: 0xf4 */ + u32 otgsc; /* On-The-Go status and control */ +#define OTGSC_DPIE BIT(30) /* data pulse interrupt enable */ +#define OTGSC_MSE BIT(29) /* 1 ms timer interrupt enable */ +#define OTGSC_BSEIE BIT(28) /* B session end interrupt enable */ +#define OTGSC_BSVIE BIT(27) /* B session valid interrupt enable */ +#define OTGSC_ASVIE BIT(26) /* A session valid interrupt enable */ +#define OTGSC_AVVIE BIT(25) /* A VBUS valid interrupt enable */ +#define OTGSC_IDIE BIT(24) /* USB ID interrupt enable */ +#define OTGSC_DPIS BIT(22) /* data pulse interrupt status */ +#define OTGSC_MSS BIT(21) /* 1 ms timer interrupt status */ +#define OTGSC_BSEIS BIT(20) /* B session end interrupt status */ +#define OTGSC_BSVIS BIT(19) /* B session valid interrupt status */ +#define OTGSC_ASVIS BIT(18) /* A session valid interrupt status */ +#define OTGSC_AVVIS BIT(17) /* A VBUS valid interrupt status */ +#define OTGSC_IDIS BIT(16) /* USB ID interrupt status */ +#define OTGSC_DPS BIT(14) /* data bus pulsing status */ +#define OTGSC_MST BIT(13) /* 1 ms timer toggle */ +#define OTGSC_BSE BIT(12) /* B session end */ +#define OTGSC_BSV BIT(11) /* B session valid */ +#define OTGSC_ASV BIT(10) /* A session valid */ +#define OTGSC_AVV BIT(9) /* A VBUS valid */ +#define OTGSC_USBID BIT(8) /* USB ID */ +#define OTGSC_HABA BIT(7) /* hw assist B-disconnect to A-connect */ +#define OTGSC_HADP BIT(6) /* hw assist data pulse */ +#define OTGSC_IDPU BIT(5) /* ID pullup */ +#define OTGSC_DP BIT(4) /* data pulsing */ +#define OTGSC_OT BIT(3) /* OTG termination */ +#define OTGSC_HAAR BIT(2) /* hw assist auto reset */ +#define OTGSC_VC BIT(1) /* VBUS charge */ +#define OTGSC_VD BIT(0) /* VBUS discharge */ + u32 usbmode; +#define MODE_VBPS BIT(5) /* R/W VBUS power select */ +#define MODE_SDIS BIT(4) /* R/W stream disable mode */ +#define MODE_SLOM BIT(3) /* R/W setup lockout mode */ +#define MODE_ENSE BIT(2) /* endian select */ +#define MODE_CM(u) (((u)>>0)&3) /* bits 1:0, controller mode */ +#define MODE_IDLE 0 +#define MODE_DEVICE 2 +#define MODE_HOST 3 + u8 _reserved9[0x100-0xfc]; + /* offset: 0x100 */ + u32 endptnak; +#define EPTN(e) \ + (((e)>>16)&0xffff) /* bits 31:16, TX endpoint NAK */ +#define EPRN(e) \ + (((e)>>0)&0xffff) /* bits 15:0, RX endpoint NAK */ + u32 endptnaken; +#define EPTNE(e) \ + (((e)>>16)&0xffff) /* bits 31:16, TX endpoint NAK enable */ +#define EPRNE(e) \ + (((e)>>0)&0xffff) /* bits 15:0, RX endpoint NAK enable */ + u32 endptsetupstat; +#define SETUPSTAT_MASK (0xffff << 0) /* bits 15:0 */ +#define EP0SETUPSTAT_MASK 1 + u32 endptprime; +/* bits 31:16, prime endpoint transmit buffer */ +#define PETB(e) (((e)>>16)&0xffff) +/* bits 15:0, prime endpoint receive buffer */ +#define PERB(e) (((e)>>0)&0xffff) + /* offset: 0x110 */ + u32 endptflush; +/* bits 31:16, flush endpoint transmit buffer */ +#define FETB(e) (((e)>>16)&0xffff) +/* bits 15:0, flush endpoint receive buffer */ +#define FERB(e) (((e)>>0)&0xffff) + u32 endptstat; +/* bits 31:16, endpoint transmit buffer ready */ +#define ETBR(e) (((e)>>16)&0xffff) +/* bits 15:0, endpoint receive buffer ready */ +#define ERBR(e) (((e)>>0)&0xffff) + u32 endptcomplete; +/* bits 31:16, endpoint transmit complete event */ +#define ETCE(e) (((e)>>16)&0xffff) +/* bits 15:0, endpoint receive complete event */ +#define ERCE(e) (((e)>>0)&0xffff) + /* offset: 0x11c */ + u32 endptctrl[16]; +#define EPCTRL_TXE BIT(23) /* TX endpoint enable */ +#define EPCTRL_TXR BIT(22) /* TX data toggle reset */ +#define EPCTRL_TXI BIT(21) /* TX data toggle inhibit */ +#define EPCTRL_TXT(e) (((e)>>18)&3) /* bits 19:18, TX endpoint type */ +#define EPCTRL_TXT_SHIFT 18 +#define EPCTRL_TXD BIT(17) /* TX endpoint data source */ +#define EPCTRL_TXS BIT(16) /* TX endpoint STALL */ +#define EPCTRL_RXE BIT(7) /* RX endpoint enable */ +#define EPCTRL_RXR BIT(6) /* RX data toggle reset */ +#define EPCTRL_RXI BIT(5) /* RX data toggle inhibit */ +#define EPCTRL_RXT(e) (((e)>>2)&3) /* bits 3:2, RX endpoint type */ +#define EPCTRL_RXT_SHIFT 2 /* bits 19:18, TX endpoint type */ +#define EPCTRL_RXD BIT(1) /* RX endpoint data sink */ +#define EPCTRL_RXS BIT(0) /* RX endpoint STALL */ +} __attribute__ ((packed)); + +#endif /* __LANGWELL_UDC_H */ + -- cgit v1.2.3-70-g09d2 From 453f77558810ffa669ed5a510a7173ec49def396 Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Thu, 4 Jun 2009 16:06:50 +0800 Subject: USB: Add Intel Langwell USB OTG Transceiver Drive Description: This driver is used for Intel Langwell* USB OTG controller in Intel Moorestown* platform. It tries to implement host/device role switch according to OTG spec. The actual hsot and device functions are accomplished in modified EHCI driver and Intel Langwell USB OTG client controller driver. * Langwell and Moorestown are names used in development. They are not approved official name. Note: This patch is the first version Intel Langwell USB OTG Transceiver driver. The development is not finished, and the bug fixing is on going for some hardware and software issues. The main purpose of this submission is for code view. Supported features: - Data-line Pulsing SRP - Support HNP to switch roles - PCI D0/D3 power management support Known issues: - HNP is only tested with another Moorestown platform. - PCI D0/D3 power management support is not fully tested. - VBus Pulsing SRP is not support in current version. Signed-off-by: Hao Wu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/Kconfig | 14 + drivers/usb/otg/Makefile | 1 + drivers/usb/otg/langwell_otg.c | 1915 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/langwell_otg.h | 177 ++++ 4 files changed, 2107 insertions(+) create mode 100644 drivers/usb/otg/langwell_otg.c create mode 100644 include/linux/usb/langwell_otg.h (limited to 'include/linux/usb') diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index aa884d072f0..69feeec1628 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -59,4 +59,18 @@ config NOP_USB_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. +config USB_LANGWELL_OTG + tristate "Intel Langwell USB OTG dual-role support" + depends on USB && MRST + select USB_OTG + select USB_OTG_UTILS + help + Say Y here if you want to build Intel Langwell USB OTG + transciever driver in kernel. This driver implements role + switch between EHCI host driver and Langwell USB OTG + client driver. + + To compile this driver as a module, choose M here: the + module will be called langwell_otg. + endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 20816785652..6d1abdd3c0a 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o +obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c new file mode 100644 index 00000000000..6f628d0e9f3 --- /dev/null +++ b/drivers/usb/otg/langwell_otg.c @@ -0,0 +1,1915 @@ +/* + * Intel Langwell USB OTG transceiver driver + * Copyright (C) 2008 - 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +/* This driver helps to switch Langwell OTG controller function between host + * and peripheral. It works with EHCI driver and Langwell client controller + * driver together. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" + +#include + +#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" +#define DRIVER_VERSION "3.0.0.32L.0002" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Henry Yuan , Hao Wu "); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +static const char driver_name[] = "langwell_otg"; + +static int langwell_otg_probe(struct pci_dev *pdev, + const struct pci_device_id *id); +static void langwell_otg_remove(struct pci_dev *pdev); +static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message); +static int langwell_otg_resume(struct pci_dev *pdev); + +static int langwell_otg_set_host(struct otg_transceiver *otg, + struct usb_bus *host); +static int langwell_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget); +static int langwell_otg_start_srp(struct otg_transceiver *otg); + +static const struct pci_device_id pci_ids[] = {{ + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = 0x8086, + .device = 0x0811, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +}, { /* end: all zeroes */ } +}; + +static struct pci_driver otg_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_ids, + + .probe = langwell_otg_probe, + .remove = langwell_otg_remove, + + .suspend = langwell_otg_suspend, + .resume = langwell_otg_resume, +}; + +static const char *state_string(enum usb_otg_state state) +{ + switch (state) { + case OTG_STATE_A_IDLE: + return "a_idle"; + case OTG_STATE_A_WAIT_VRISE: + return "a_wait_vrise"; + case OTG_STATE_A_WAIT_BCON: + return "a_wait_bcon"; + case OTG_STATE_A_HOST: + return "a_host"; + case OTG_STATE_A_SUSPEND: + return "a_suspend"; + case OTG_STATE_A_PERIPHERAL: + return "a_peripheral"; + case OTG_STATE_A_WAIT_VFALL: + return "a_wait_vfall"; + case OTG_STATE_A_VBUS_ERR: + return "a_vbus_err"; + case OTG_STATE_B_IDLE: + return "b_idle"; + case OTG_STATE_B_SRP_INIT: + return "b_srp_init"; + case OTG_STATE_B_PERIPHERAL: + return "b_peripheral"; + case OTG_STATE_B_WAIT_ACON: + return "b_wait_acon"; + case OTG_STATE_B_HOST: + return "b_host"; + default: + return "UNDEFINED"; + } +} + +/* HSM timers */ +static inline struct langwell_otg_timer *otg_timer_initializer +(void (*function)(unsigned long), unsigned long expires, unsigned long data) +{ + struct langwell_otg_timer *timer; + timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL); + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +static struct langwell_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, + *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_res_tmr, + *b_bus_suspend_tmr; + +static struct list_head active_timers; + +static struct langwell_otg *the_transceiver; + +/* host/client notify transceiver when event affects HNP state */ +void langwell_update_transceiver() +{ + otg_dbg("transceiver driver is notified\n"); + queue_work(the_transceiver->qwork, &the_transceiver->work); +} +EXPORT_SYMBOL(langwell_update_transceiver); + +static int langwell_otg_set_host(struct otg_transceiver *otg, + struct usb_bus *host) +{ + otg->host = host; + + return 0; +} + +static int langwell_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + otg->gadget = gadget; + + return 0; +} + +static int langwell_otg_set_power(struct otg_transceiver *otg, + unsigned mA) +{ + return 0; +} + +/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/ +static void langwell_otg_drv_vbus(int on) +{ + struct ipc_pmic_reg_data pmic_data = {0}; + struct ipc_pmic_reg_data battery_data; + + /* Check if battery is attached or not */ + battery_data.pmic_reg_data[0].register_address = 0xd2; + battery_data.ioc = 0; + battery_data.num_entries = 1; + if (ipc_pmic_register_read(&battery_data)) { + otg_dbg("Failed to read PMIC register 0xd2.\n"); + return; + } + + if ((battery_data.pmic_reg_data[0].value & 0x20) == 0) { + otg_dbg("no battery attached\n"); + return; + } + + /* Workaround for battery attachment issue */ + if (battery_data.pmic_reg_data[0].value == 0x34) { + otg_dbg("battery \n"); + return; + } + + otg_dbg("battery attached\n"); + + pmic_data.ioc = 0; + pmic_data.pmic_reg_data[0].register_address = 0xD4; + pmic_data.num_entries = 1; + if (on) + pmic_data.pmic_reg_data[0].value = 0x20; + else + pmic_data.pmic_reg_data[0].value = 0xc0; + + if (ipc_pmic_register_write(&pmic_data, TRUE)) + otg_dbg("Failed to write PMIC.\n"); + +} + +/* charge vbus or discharge vbus through a resistor to ground */ +static void langwell_otg_chrg_vbus(int on) +{ + + u32 val; + + val = readl(the_transceiver->regs + CI_OTGSC); + + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC, + the_transceiver->regs + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD, + the_transceiver->regs + CI_OTGSC); + +} + +/* Start SRP */ +static int langwell_otg_start_srp(struct otg_transceiver *otg) +{ + u32 val; + + otg_dbg("Start SRP ->\n"); + + val = readl(the_transceiver->regs + CI_OTGSC); + + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, + the_transceiver->regs + CI_OTGSC); + + /* Check if the data plus is finished or not */ + msleep(8); + val = readl(the_transceiver->regs + CI_OTGSC); + if (val & (OTGSC_HADP | OTGSC_DP)) + otg_dbg("DataLine SRP Error\n"); + + /* FIXME: VBus SRP */ + + return 0; +} + + +/* stop SOF via bus_suspend */ +static void langwell_otg_loc_sof(int on) +{ + struct usb_hcd *hcd; + int err; + + otg_dbg("loc_sof -> %d\n", on); + + hcd = bus_to_hcd(the_transceiver->otg.host); + if (on) + err = hcd->driver->bus_resume(hcd); + else + err = hcd->driver->bus_suspend(hcd); + + if (err) + otg_dbg("Failed to resume/suspend bus - %d\n", err); +} + +static void langwell_otg_phy_low_power(int on) +{ + u32 val; + + otg_dbg("phy low power mode-> %d\n", on); + + val = readl(the_transceiver->regs + CI_HOSTPC1); + if (on) + writel(val | HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1); + else + writel(val & ~HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1); +} + +/* Enable/Disable OTG interrupt */ +static void langwell_otg_intr(int on) +{ + u32 val; + + otg_dbg("interrupt -> %d\n", on); + + val = readl(the_transceiver->regs + CI_OTGSC); + if (on) { + val = val | (OTGSC_INTEN_MASK | OTGSC_IDPU); + writel(val, the_transceiver->regs + CI_OTGSC); + } else { + val = val & ~(OTGSC_INTEN_MASK | OTGSC_IDPU); + writel(val, the_transceiver->regs + CI_OTGSC); + } +} + +/* set HAAR: Hardware Assist Auto-Reset */ +static void langwell_otg_HAAR(int on) +{ + u32 val; + + otg_dbg("HAAR -> %d\n", on); + + val = readl(the_transceiver->regs + CI_OTGSC); + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR, + the_transceiver->regs + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR, + the_transceiver->regs + CI_OTGSC); +} + +/* set HABA: Hardware Assist B-Disconnect to A-Connect */ +static void langwell_otg_HABA(int on) +{ + u32 val; + + otg_dbg("HABA -> %d\n", on); + + val = readl(the_transceiver->regs + CI_OTGSC); + if (on) + writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, + the_transceiver->regs + CI_OTGSC); + else + writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, + the_transceiver->regs + CI_OTGSC); +} + +static int langwell_otg_check_se0_srp(int on) +{ + u32 val; + + int delay_time = TB_SE0_SRP * 10; /* step is 100us */ + + otg_dbg("check_se0_srp -> \n"); + + do { + udelay(100); + if (!delay_time--) + break; + val = readl(the_transceiver->regs + CI_PORTSC1); + val &= PORTSC_LS; + } while (!val); + + otg_dbg("check_se0_srp <- \n"); + return val; +} + +/* The timeout callback function to set time out bit */ +static void set_tmout(unsigned long indicator) +{ + *(int *)indicator = 1; +} + +void langwell_otg_nsf_msg(unsigned long indicator) +{ + switch (indicator) { + case 2: + case 4: + case 6: + case 7: + printk(KERN_ERR "OTG:NSF-%lu - deivce not responding\n", + indicator); + break; + case 3: + printk(KERN_ERR "OTG:NSF-%lu - deivce not supported\n", + indicator); + break; + default: + printk(KERN_ERR "Do not have this kind of NSF\n"); + break; + } +} + +/* Initialize timers */ +static void langwell_otg_init_timers(struct otg_hsm *hsm) +{ + /* HSM used timers */ + a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, + (unsigned long)&hsm->a_wait_vrise_tmout); + a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, + (unsigned long)&hsm->a_wait_bcon_tmout); + a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, + (unsigned long)&hsm->a_aidl_bdis_tmout); + b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, + (unsigned long)&hsm->b_ase0_brst_tmout); + b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, + (unsigned long)&hsm->b_se0_srp); + b_srp_res_tmr = otg_timer_initializer(&set_tmout, TB_SRP_RES, + (unsigned long)&hsm->b_srp_res_tmout); + b_bus_suspend_tmr = otg_timer_initializer(&set_tmout, TB_BUS_SUSPEND, + (unsigned long)&hsm->b_bus_suspend_tmout); +} + +/* Free timers */ +static void langwell_otg_free_timers(void) +{ + kfree(a_wait_vrise_tmr); + kfree(a_wait_bcon_tmr); + kfree(a_aidl_bdis_tmr); + kfree(b_ase0_brst_tmr); + kfree(b_se0_srp_tmr); + kfree(b_srp_res_tmr); + kfree(b_bus_suspend_tmr); +} + +/* Add timer to timer list */ +static void langwell_otg_add_timer(void *gtimer) +{ + struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; + struct langwell_otg_timer *tmp_timer; + u32 val32; + + /* Check if the timer is already in the active list, + * if so update timer count + */ + list_for_each_entry(tmp_timer, &active_timers, list) + if (tmp_timer == timer) { + timer->count = timer->expires; + return; + } + timer->count = timer->expires; + + if (list_empty(&active_timers)) { + val32 = readl(the_transceiver->regs + CI_OTGSC); + writel(val32 | OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); + } + + list_add_tail(&timer->list, &active_timers); +} + +/* Remove timer from the timer list; clear timeout status */ +static void langwell_otg_del_timer(void *gtimer) +{ + struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; + struct langwell_otg_timer *tmp_timer, *del_tmp; + u32 val32; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); + + if (list_empty(&active_timers)) { + val32 = readl(the_transceiver->regs + CI_OTGSC); + writel(val32 & ~OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); + } +} + +/* Reduce timer count by 1, and find timeout conditions.*/ +static int langwell_otg_tick_timer(u32 *int_sts) +{ + struct langwell_otg_timer *tmp_timer, *del_tmp; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(tmp_timer->data); + expired = 1; + } + } + + if (list_empty(&active_timers)) { + otg_dbg("tick timer: disable 1ms int\n"); + *int_sts = *int_sts & ~OTGSC_1MSE; + } + return expired; +} + +static void reset_otg(void) +{ + u32 val; + int delay_time = 1000; + + otg_dbg("reseting OTG controller ...\n"); + val = readl(the_transceiver->regs + CI_USBCMD); + writel(val | USBCMD_RST, the_transceiver->regs + CI_USBCMD); + do { + udelay(100); + if (!delay_time--) + otg_dbg("reset timeout\n"); + val = readl(the_transceiver->regs + CI_USBCMD); + val &= USBCMD_RST; + } while (val != 0); + otg_dbg("reset done.\n"); +} + +static void set_host_mode(void) +{ + u32 val; + + reset_otg(); + val = readl(the_transceiver->regs + CI_USBMODE); + val = (val & (~USBMODE_CM)) | USBMODE_HOST; + writel(val, the_transceiver->regs + CI_USBMODE); +} + +static void set_client_mode(void) +{ + u32 val; + + reset_otg(); + val = readl(the_transceiver->regs + CI_USBMODE); + val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; + writel(val, the_transceiver->regs + CI_USBMODE); +} + +static void init_hsm(void) +{ + struct langwell_otg *langwell = the_transceiver; + u32 val32; + + /* read OTGSC after reset */ + val32 = readl(langwell->regs + CI_OTGSC); + otg_dbg("%s: OTGSC init value = 0x%x\n", __func__, val32); + + /* set init state */ + if (val32 & OTGSC_ID) { + langwell->hsm.id = 1; + langwell->otg.default_a = 0; + set_client_mode(); + langwell->otg.state = OTG_STATE_B_IDLE; + langwell_otg_drv_vbus(0); + } else { + langwell->hsm.id = 0; + langwell->otg.default_a = 1; + set_host_mode(); + langwell->otg.state = OTG_STATE_A_IDLE; + } + + /* set session indicator */ + if (val32 & OTGSC_BSE) + langwell->hsm.b_sess_end = 1; + if (val32 & OTGSC_BSV) + langwell->hsm.b_sess_vld = 1; + if (val32 & OTGSC_ASV) + langwell->hsm.a_sess_vld = 1; + if (val32 & OTGSC_AVV) + langwell->hsm.a_vbus_vld = 1; + + /* defautly power the bus */ + langwell->hsm.a_bus_req = 1; + langwell->hsm.a_bus_drop = 0; + /* defautly don't request bus as B device */ + langwell->hsm.b_bus_req = 0; + /* no system error */ + langwell->hsm.a_clr_err = 0; +} + +static irqreturn_t otg_dummy_irq(int irq, void *_dev) +{ + void __iomem *reg_base = _dev; + u32 val; + u32 int_mask = 0; + + val = readl(reg_base + CI_USBMODE); + if ((val & USBMODE_CM) != USBMODE_DEVICE) + return IRQ_NONE; + + val = readl(reg_base + CI_USBSTS); + int_mask = val & INTR_DUMMY_MASK; + + if (int_mask == 0) + return IRQ_NONE; + + /* clear hsm.b_conn here since host driver can't detect it + * otg_dummy_irq called means B-disconnect happened. + */ + if (the_transceiver->hsm.b_conn) { + the_transceiver->hsm.b_conn = 0; + if (spin_trylock(&the_transceiver->wq_lock)) { + queue_work(the_transceiver->qwork, + &the_transceiver->work); + spin_unlock(&the_transceiver->wq_lock); + } + } + /* Clear interrupts */ + writel(int_mask, reg_base + CI_USBSTS); + return IRQ_HANDLED; +} + +static irqreturn_t otg_irq(int irq, void *_dev) +{ + struct langwell_otg *langwell = _dev; + u32 int_sts, int_en; + u32 int_mask = 0; + int flag = 0; + + int_sts = readl(langwell->regs + CI_OTGSC); + int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; + int_mask = int_sts & int_en; + if (int_mask == 0) + return IRQ_NONE; + + if (int_mask & OTGSC_IDIS) { + otg_dbg("%s: id change int\n", __func__); + langwell->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; + flag = 1; + } + if (int_mask & OTGSC_DPIS) { + otg_dbg("%s: data pulse int\n", __func__); + langwell->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; + flag = 1; + } + if (int_mask & OTGSC_BSEIS) { + otg_dbg("%s: b session end int\n", __func__); + langwell->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; + flag = 1; + } + if (int_mask & OTGSC_BSVIS) { + otg_dbg("%s: b session valid int\n", __func__); + langwell->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; + flag = 1; + } + if (int_mask & OTGSC_ASVIS) { + otg_dbg("%s: a session valid int\n", __func__); + langwell->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; + flag = 1; + } + if (int_mask & OTGSC_AVVIS) { + otg_dbg("%s: a vbus valid int\n", __func__); + langwell->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; + flag = 1; + } + + if (int_mask & OTGSC_1MSS) { + /* need to schedule otg_work if any timer is expired */ + if (langwell_otg_tick_timer(&int_sts)) + flag = 1; + } + + writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, + langwell->regs + CI_OTGSC); + if (flag) + queue_work(langwell->qwork, &langwell->work); + + return IRQ_HANDLED; +} + +static void langwell_otg_work(struct work_struct *work) +{ + struct langwell_otg *langwell = container_of(work, + struct langwell_otg, work); + int retval; + + otg_dbg("%s: old state = %s\n", __func__, + state_string(langwell->otg.state)); + + switch (langwell->otg.state) { + case OTG_STATE_UNDEFINED: + case OTG_STATE_B_IDLE: + if (!langwell->hsm.id) { + langwell_otg_del_timer(b_srp_res_tmr); + langwell->otg.default_a = 1; + langwell->hsm.a_srp_det = 0; + + langwell_otg_chrg_vbus(0); + langwell_otg_drv_vbus(0); + + set_host_mode(); + langwell->otg.state = OTG_STATE_A_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (langwell->hsm.b_srp_res_tmout) { + langwell->hsm.b_srp_res_tmout = 0; + langwell->hsm.b_bus_req = 0; + langwell_otg_nsf_msg(6); + } else if (langwell->hsm.b_sess_vld) { + langwell_otg_del_timer(b_srp_res_tmr); + langwell->hsm.b_sess_end = 0; + langwell->hsm.a_bus_suspend = 0; + + langwell_otg_chrg_vbus(0); + if (langwell->client_ops) { + langwell->client_ops->resume(langwell->pdev); + langwell->otg.state = OTG_STATE_B_PERIPHERAL; + } else + otg_dbg("client driver not loaded.\n"); + + } else if (langwell->hsm.b_bus_req && + (langwell->hsm.b_sess_end)) { + /* workaround for b_se0_srp detection */ + retval = langwell_otg_check_se0_srp(0); + if (retval) { + langwell->hsm.b_bus_req = 0; + otg_dbg("LS is not SE0, try again later\n"); + } else { + /* Start SRP */ + langwell_otg_start_srp(&langwell->otg); + langwell_otg_add_timer(b_srp_res_tmr); + } + } + break; + case OTG_STATE_B_SRP_INIT: + if (!langwell->hsm.id) { + langwell->otg.default_a = 1; + langwell->hsm.a_srp_det = 0; + + langwell_otg_drv_vbus(0); + langwell_otg_chrg_vbus(0); + + langwell->otg.state = OTG_STATE_A_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (langwell->hsm.b_sess_vld) { + langwell_otg_chrg_vbus(0); + if (langwell->client_ops) { + langwell->client_ops->resume(langwell->pdev); + langwell->otg.state = OTG_STATE_B_PERIPHERAL; + } else + otg_dbg("client driver not loaded.\n"); + } + break; + case OTG_STATE_B_PERIPHERAL: + if (!langwell->hsm.id) { + langwell->otg.default_a = 1; + langwell->hsm.a_srp_det = 0; + + langwell_otg_drv_vbus(0); + langwell_otg_chrg_vbus(0); + set_host_mode(); + + if (langwell->client_ops) { + langwell->client_ops->suspend(langwell->pdev, + PMSG_FREEZE); + } else + otg_dbg("client driver has been removed.\n"); + + langwell->otg.state = OTG_STATE_A_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (!langwell->hsm.b_sess_vld) { + langwell->hsm.b_hnp_enable = 0; + + if (langwell->client_ops) { + langwell->client_ops->suspend(langwell->pdev, + PMSG_FREEZE); + } else + otg_dbg("client driver has been removed.\n"); + + langwell->otg.state = OTG_STATE_B_IDLE; + } else if (langwell->hsm.b_bus_req && langwell->hsm.b_hnp_enable + && langwell->hsm.a_bus_suspend) { + + if (langwell->client_ops) { + langwell->client_ops->suspend(langwell->pdev, + PMSG_FREEZE); + } else + otg_dbg("client driver has been removed.\n"); + + langwell_otg_HAAR(1); + langwell->hsm.a_conn = 0; + + if (langwell->host_ops) { + langwell->host_ops->probe(langwell->pdev, + langwell->host_ops->id_table); + langwell->otg.state = OTG_STATE_B_WAIT_ACON; + } else + otg_dbg("host driver not loaded.\n"); + + langwell->hsm.a_bus_resume = 0; + langwell->hsm.b_ase0_brst_tmout = 0; + langwell_otg_add_timer(b_ase0_brst_tmr); + } + break; + + case OTG_STATE_B_WAIT_ACON: + if (!langwell->hsm.id) { + langwell_otg_del_timer(b_ase0_brst_tmr); + langwell->otg.default_a = 1; + langwell->hsm.a_srp_det = 0; + + langwell_otg_drv_vbus(0); + langwell_otg_chrg_vbus(0); + set_host_mode(); + + langwell_otg_HAAR(0); + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell->otg.state = OTG_STATE_A_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (!langwell->hsm.b_sess_vld) { + langwell_otg_del_timer(b_ase0_brst_tmr); + langwell->hsm.b_hnp_enable = 0; + langwell->hsm.b_bus_req = 0; + langwell_otg_chrg_vbus(0); + langwell_otg_HAAR(0); + + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell->otg.state = OTG_STATE_B_IDLE; + } else if (langwell->hsm.a_conn) { + langwell_otg_del_timer(b_ase0_brst_tmr); + langwell_otg_HAAR(0); + langwell->otg.state = OTG_STATE_B_HOST; + queue_work(langwell->qwork, &langwell->work); + } else if (langwell->hsm.a_bus_resume || + langwell->hsm.b_ase0_brst_tmout) { + langwell_otg_del_timer(b_ase0_brst_tmr); + langwell_otg_HAAR(0); + langwell_otg_nsf_msg(7); + + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + + langwell->hsm.a_bus_suspend = 0; + langwell->hsm.b_bus_req = 0; + + if (langwell->client_ops) + langwell->client_ops->resume(langwell->pdev); + else + otg_dbg("client driver not loaded.\n"); + + langwell->otg.state = OTG_STATE_B_PERIPHERAL; + } + break; + + case OTG_STATE_B_HOST: + if (!langwell->hsm.id) { + langwell->otg.default_a = 1; + langwell->hsm.a_srp_det = 0; + + langwell_otg_drv_vbus(0); + langwell_otg_chrg_vbus(0); + set_host_mode(); + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell->otg.state = OTG_STATE_A_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (!langwell->hsm.b_sess_vld) { + langwell->hsm.b_hnp_enable = 0; + langwell->hsm.b_bus_req = 0; + langwell_otg_chrg_vbus(0); + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell->otg.state = OTG_STATE_B_IDLE; + } else if ((!langwell->hsm.b_bus_req) || + (!langwell->hsm.a_conn)) { + langwell->hsm.b_bus_req = 0; + langwell_otg_loc_sof(0); + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + + langwell->hsm.a_bus_suspend = 0; + + if (langwell->client_ops) + langwell->client_ops->resume(langwell->pdev); + else + otg_dbg("client driver not loaded.\n"); + + langwell->otg.state = OTG_STATE_B_PERIPHERAL; + } + break; + + case OTG_STATE_A_IDLE: + langwell->otg.default_a = 1; + if (langwell->hsm.id) { + langwell->otg.default_a = 0; + langwell->hsm.b_bus_req = 0; + langwell_otg_drv_vbus(0); + langwell_otg_chrg_vbus(0); + + langwell->otg.state = OTG_STATE_B_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (langwell->hsm.a_sess_vld) { + langwell_otg_drv_vbus(1); + langwell->hsm.a_srp_det = 1; + langwell->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + langwell->otg.state = OTG_STATE_A_WAIT_VRISE; + queue_work(langwell->qwork, &langwell->work); + } else if (!langwell->hsm.a_bus_drop && + (langwell->hsm.a_srp_det || langwell->hsm.a_bus_req)) { + langwell_otg_drv_vbus(1); + langwell->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + langwell->otg.state = OTG_STATE_A_WAIT_VRISE; + queue_work(langwell->qwork, &langwell->work); + } + break; + case OTG_STATE_A_WAIT_VRISE: + if (langwell->hsm.id) { + langwell_otg_del_timer(a_wait_vrise_tmr); + langwell->hsm.b_bus_req = 0; + langwell->otg.default_a = 0; + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_B_IDLE; + } else if (langwell->hsm.a_vbus_vld) { + langwell_otg_del_timer(a_wait_vrise_tmr); + if (langwell->host_ops) + langwell->host_ops->probe(langwell->pdev, + langwell->host_ops->id_table); + else + otg_dbg("host driver not loaded.\n"); + langwell->hsm.b_conn = 0; + langwell->hsm.a_set_b_hnp_en = 0; + langwell->hsm.a_wait_bcon_tmout = 0; + langwell_otg_add_timer(a_wait_bcon_tmr); + langwell->otg.state = OTG_STATE_A_WAIT_BCON; + } else if (langwell->hsm.a_wait_vrise_tmout) { + if (langwell->hsm.a_vbus_vld) { + if (langwell->host_ops) + langwell->host_ops->probe( + langwell->pdev, + langwell->host_ops->id_table); + else + otg_dbg("host driver not loaded.\n"); + langwell->hsm.b_conn = 0; + langwell->hsm.a_set_b_hnp_en = 0; + langwell->hsm.a_wait_bcon_tmout = 0; + langwell_otg_add_timer(a_wait_bcon_tmr); + langwell->otg.state = OTG_STATE_A_WAIT_BCON; + } else { + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_VBUS_ERR; + } + } + break; + case OTG_STATE_A_WAIT_BCON: + if (langwell->hsm.id) { + langwell_otg_del_timer(a_wait_bcon_tmr); + + langwell->otg.default_a = 0; + langwell->hsm.b_bus_req = 0; + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_B_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (!langwell->hsm.a_vbus_vld) { + langwell_otg_del_timer(a_wait_bcon_tmr); + + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (langwell->hsm.a_bus_drop || + (langwell->hsm.a_wait_bcon_tmout && + !langwell->hsm.a_bus_req)) { + langwell_otg_del_timer(a_wait_bcon_tmr); + + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (langwell->hsm.b_conn) { + langwell_otg_del_timer(a_wait_bcon_tmr); + + langwell->hsm.a_suspend_req = 0; + langwell->otg.state = OTG_STATE_A_HOST; + if (!langwell->hsm.a_bus_req && + langwell->hsm.a_set_b_hnp_en) { + /* It is not safe enough to do a fast + * transistion from A_WAIT_BCON to + * A_SUSPEND */ + msleep(10000); + if (langwell->hsm.a_bus_req) + break; + + if (request_irq(langwell->pdev->irq, + otg_dummy_irq, IRQF_SHARED, + driver_name, langwell->regs) != 0) { + otg_dbg("request interrupt %d fail\n", + langwell->pdev->irq); + } + + langwell_otg_HABA(1); + langwell->hsm.b_bus_resume = 0; + langwell->hsm.a_aidl_bdis_tmout = 0; + langwell_otg_add_timer(a_aidl_bdis_tmr); + + langwell_otg_loc_sof(0); + langwell->otg.state = OTG_STATE_A_SUSPEND; + } else if (!langwell->hsm.a_bus_req && + !langwell->hsm.a_set_b_hnp_en) { + struct pci_dev *pdev = langwell->pdev; + if (langwell->host_ops) + langwell->host_ops->remove(pdev); + else + otg_dbg("host driver removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_WAIT_VFALL; + } + } + break; + case OTG_STATE_A_HOST: + if (langwell->hsm.id) { + langwell->otg.default_a = 0; + langwell->hsm.b_bus_req = 0; + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_B_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (langwell->hsm.a_bus_drop || + (!langwell->hsm.a_set_b_hnp_en && !langwell->hsm.a_bus_req)) { + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (!langwell->hsm.a_vbus_vld) { + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (langwell->hsm.a_set_b_hnp_en + && !langwell->hsm.a_bus_req) { + /* Set HABA to enable hardware assistance to signal + * A-connect after receiver B-disconnect. Hardware + * will then set client mode and enable URE, SLE and + * PCE after the assistance. otg_dummy_irq is used to + * clean these ints when client driver is not resumed. + */ + if (request_irq(langwell->pdev->irq, + otg_dummy_irq, IRQF_SHARED, driver_name, + langwell->regs) != 0) { + otg_dbg("request interrupt %d failed\n", + langwell->pdev->irq); + } + + /* set HABA */ + langwell_otg_HABA(1); + langwell->hsm.b_bus_resume = 0; + langwell->hsm.a_aidl_bdis_tmout = 0; + langwell_otg_add_timer(a_aidl_bdis_tmr); + langwell_otg_loc_sof(0); + langwell->otg.state = OTG_STATE_A_SUSPEND; + } else if (!langwell->hsm.b_conn || !langwell->hsm.a_bus_req) { + langwell->hsm.a_wait_bcon_tmout = 0; + langwell->hsm.a_set_b_hnp_en = 0; + langwell_otg_add_timer(a_wait_bcon_tmr); + langwell->otg.state = OTG_STATE_A_WAIT_BCON; + } + break; + case OTG_STATE_A_SUSPEND: + if (langwell->hsm.id) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(langwell->pdev->irq, langwell->regs); + langwell->otg.default_a = 0; + langwell->hsm.b_bus_req = 0; + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_B_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (langwell->hsm.a_bus_req || + langwell->hsm.b_bus_resume) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(langwell->pdev->irq, langwell->regs); + langwell->hsm.a_suspend_req = 0; + langwell_otg_loc_sof(1); + langwell->otg.state = OTG_STATE_A_HOST; + } else if (langwell->hsm.a_aidl_bdis_tmout || + langwell->hsm.a_bus_drop) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(langwell->pdev->irq, langwell->regs); + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (!langwell->hsm.b_conn && + langwell->hsm.a_set_b_hnp_en) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(langwell->pdev->irq, langwell->regs); + + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + + langwell->hsm.b_bus_suspend = 0; + langwell->hsm.b_bus_suspend_vld = 0; + langwell->hsm.b_bus_suspend_tmout = 0; + + /* msleep(200); */ + if (langwell->client_ops) + langwell->client_ops->resume(langwell->pdev); + else + otg_dbg("client driver not loaded.\n"); + + langwell_otg_add_timer(b_bus_suspend_tmr); + langwell->otg.state = OTG_STATE_A_PERIPHERAL; + break; + } else if (!langwell->hsm.a_vbus_vld) { + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + free_irq(langwell->pdev->irq, langwell->regs); + if (langwell->host_ops) + langwell->host_ops->remove(langwell->pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_VBUS_ERR; + } + break; + case OTG_STATE_A_PERIPHERAL: + if (langwell->hsm.id) { + langwell_otg_del_timer(b_bus_suspend_tmr); + langwell->otg.default_a = 0; + langwell->hsm.b_bus_req = 0; + if (langwell->client_ops) + langwell->client_ops->suspend(langwell->pdev, + PMSG_FREEZE); + else + otg_dbg("client driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_B_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (!langwell->hsm.a_vbus_vld) { + langwell_otg_del_timer(b_bus_suspend_tmr); + if (langwell->client_ops) + langwell->client_ops->suspend(langwell->pdev, + PMSG_FREEZE); + else + otg_dbg("client driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_VBUS_ERR; + } else if (langwell->hsm.a_bus_drop) { + langwell_otg_del_timer(b_bus_suspend_tmr); + if (langwell->client_ops) + langwell->client_ops->suspend(langwell->pdev, + PMSG_FREEZE); + else + otg_dbg("client driver has been removed.\n"); + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_WAIT_VFALL; + } else if (langwell->hsm.b_bus_suspend) { + langwell_otg_del_timer(b_bus_suspend_tmr); + if (langwell->client_ops) + langwell->client_ops->suspend(langwell->pdev, + PMSG_FREEZE); + else + otg_dbg("client driver has been removed.\n"); + + if (langwell->host_ops) + langwell->host_ops->probe(langwell->pdev, + langwell->host_ops->id_table); + else + otg_dbg("host driver not loaded.\n"); + langwell->hsm.a_set_b_hnp_en = 0; + langwell->hsm.a_wait_bcon_tmout = 0; + langwell_otg_add_timer(a_wait_bcon_tmr); + langwell->otg.state = OTG_STATE_A_WAIT_BCON; + } else if (langwell->hsm.b_bus_suspend_tmout) { + u32 val; + val = readl(langwell->regs + CI_PORTSC1); + if (!(val & PORTSC_SUSP)) + break; + if (langwell->client_ops) + langwell->client_ops->suspend(langwell->pdev, + PMSG_FREEZE); + else + otg_dbg("client driver has been removed.\n"); + if (langwell->host_ops) + langwell->host_ops->probe(langwell->pdev, + langwell->host_ops->id_table); + else + otg_dbg("host driver not loaded.\n"); + langwell->hsm.a_set_b_hnp_en = 0; + langwell->hsm.a_wait_bcon_tmout = 0; + langwell_otg_add_timer(a_wait_bcon_tmr); + langwell->otg.state = OTG_STATE_A_WAIT_BCON; + } + break; + case OTG_STATE_A_VBUS_ERR: + if (langwell->hsm.id) { + langwell->otg.default_a = 0; + langwell->hsm.a_clr_err = 0; + langwell->hsm.a_srp_det = 0; + langwell->otg.state = OTG_STATE_B_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (langwell->hsm.a_clr_err) { + langwell->hsm.a_clr_err = 0; + langwell->hsm.a_srp_det = 0; + reset_otg(); + init_hsm(); + if (langwell->otg.state == OTG_STATE_A_IDLE) + queue_work(langwell->qwork, &langwell->work); + } + break; + case OTG_STATE_A_WAIT_VFALL: + if (langwell->hsm.id) { + langwell->otg.default_a = 0; + langwell->otg.state = OTG_STATE_B_IDLE; + queue_work(langwell->qwork, &langwell->work); + } else if (langwell->hsm.a_bus_req) { + langwell_otg_drv_vbus(1); + langwell->hsm.a_wait_vrise_tmout = 0; + langwell_otg_add_timer(a_wait_vrise_tmr); + langwell->otg.state = OTG_STATE_A_WAIT_VRISE; + } else if (!langwell->hsm.a_sess_vld) { + langwell->hsm.a_srp_det = 0; + langwell_otg_drv_vbus(0); + set_host_mode(); + langwell->otg.state = OTG_STATE_A_IDLE; + } + break; + default: + ; + } + + otg_dbg("%s: new state = %s\n", __func__, + state_string(langwell->otg.state)); +} + + static ssize_t +show_registers(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *langwell; + char *next; + unsigned size; + unsigned t; + + langwell = the_transceiver; + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, + "\n" + "USBCMD = 0x%08x \n" + "USBSTS = 0x%08x \n" + "USBINTR = 0x%08x \n" + "ASYNCLISTADDR = 0x%08x \n" + "PORTSC1 = 0x%08x \n" + "HOSTPC1 = 0x%08x \n" + "OTGSC = 0x%08x \n" + "USBMODE = 0x%08x \n", + readl(langwell->regs + 0x30), + readl(langwell->regs + 0x34), + readl(langwell->regs + 0x38), + readl(langwell->regs + 0x48), + readl(langwell->regs + 0x74), + readl(langwell->regs + 0xb4), + readl(langwell->regs + 0xf4), + readl(langwell->regs + 0xf8) + ); + size -= t; + next += t; + + return PAGE_SIZE - size; +} +static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); + +static ssize_t +show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *langwell; + char *next; + unsigned size; + unsigned t; + + langwell = the_transceiver; + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, + "\n" + "current state = %s\n" + "a_bus_resume = \t%d\n" + "a_bus_suspend = \t%d\n" + "a_conn = \t%d\n" + "a_sess_vld = \t%d\n" + "a_srp_det = \t%d\n" + "a_vbus_vld = \t%d\n" + "b_bus_resume = \t%d\n" + "b_bus_suspend = \t%d\n" + "b_conn = \t%d\n" + "b_se0_srp = \t%d\n" + "b_sess_end = \t%d\n" + "b_sess_vld = \t%d\n" + "id = \t%d\n" + "a_set_b_hnp_en = \t%d\n" + "b_srp_done = \t%d\n" + "b_hnp_enable = \t%d\n" + "a_wait_vrise_tmout = \t%d\n" + "a_wait_bcon_tmout = \t%d\n" + "a_aidl_bdis_tmout = \t%d\n" + "b_ase0_brst_tmout = \t%d\n" + "a_bus_drop = \t%d\n" + "a_bus_req = \t%d\n" + "a_clr_err = \t%d\n" + "a_suspend_req = \t%d\n" + "b_bus_req = \t%d\n" + "b_bus_suspend_tmout = \t%d\n" + "b_bus_suspend_vld = \t%d\n", + state_string(langwell->otg.state), + langwell->hsm.a_bus_resume, + langwell->hsm.a_bus_suspend, + langwell->hsm.a_conn, + langwell->hsm.a_sess_vld, + langwell->hsm.a_srp_det, + langwell->hsm.a_vbus_vld, + langwell->hsm.b_bus_resume, + langwell->hsm.b_bus_suspend, + langwell->hsm.b_conn, + langwell->hsm.b_se0_srp, + langwell->hsm.b_sess_end, + langwell->hsm.b_sess_vld, + langwell->hsm.id, + langwell->hsm.a_set_b_hnp_en, + langwell->hsm.b_srp_done, + langwell->hsm.b_hnp_enable, + langwell->hsm.a_wait_vrise_tmout, + langwell->hsm.a_wait_bcon_tmout, + langwell->hsm.a_aidl_bdis_tmout, + langwell->hsm.b_ase0_brst_tmout, + langwell->hsm.a_bus_drop, + langwell->hsm.a_bus_req, + langwell->hsm.a_clr_err, + langwell->hsm.a_suspend_req, + langwell->hsm.b_bus_req, + langwell->hsm.b_bus_suspend_tmout, + langwell->hsm.b_bus_suspend_vld + ); + size -= t; + next += t; + + return PAGE_SIZE - size; +} +static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); + +static ssize_t +get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *langwell; + char *next; + unsigned size; + unsigned t; + + langwell = the_transceiver; + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", langwell->hsm.a_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *langwell; + langwell = the_transceiver; + if (!langwell->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '0') { + langwell->hsm.a_bus_req = 0; + otg_dbg("a_bus_req = 0\n"); + } else if (buf[0] == '1') { + /* If a_bus_drop is TRUE, a_bus_req can't be set */ + if (langwell->hsm.a_bus_drop) + return -1; + langwell->hsm.a_bus_req = 1; + otg_dbg("a_bus_req = 1\n"); + } + if (spin_trylock(&langwell->wq_lock)) { + queue_work(langwell->qwork, &langwell->work); + spin_unlock(&langwell->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req); + +static ssize_t +get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *langwell; + char *next; + unsigned size; + unsigned t; + + langwell = the_transceiver; + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", langwell->hsm.a_bus_drop); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_drop(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *langwell; + langwell = the_transceiver; + if (!langwell->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '0') { + langwell->hsm.a_bus_drop = 0; + otg_dbg("a_bus_drop = 0\n"); + } else if (buf[0] == '1') { + langwell->hsm.a_bus_drop = 1; + langwell->hsm.a_bus_req = 0; + otg_dbg("a_bus_drop = 1, then a_bus_req = 0\n"); + } + if (spin_trylock(&langwell->wq_lock)) { + queue_work(langwell->qwork, &langwell->work); + spin_unlock(&langwell->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO, + get_a_bus_drop, set_a_bus_drop); + +static ssize_t +get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct langwell_otg *langwell; + char *next; + unsigned size; + unsigned t; + + langwell = the_transceiver; + next = buf; + size = PAGE_SIZE; + + t = scnprintf(next, size, "%d", langwell->hsm.b_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_b_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *langwell; + langwell = the_transceiver; + + if (langwell->otg.default_a) + return -1; + + if (count > 2) + return -1; + + if (buf[0] == '0') { + langwell->hsm.b_bus_req = 0; + otg_dbg("b_bus_req = 0\n"); + } else if (buf[0] == '1') { + langwell->hsm.b_bus_req = 1; + otg_dbg("b_bus_req = 1\n"); + } + if (spin_trylock(&langwell->wq_lock)) { + queue_work(langwell->qwork, &langwell->work); + spin_unlock(&langwell->wq_lock); + } + return count; +} +static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req); + +static ssize_t +set_a_clr_err(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct langwell_otg *langwell; + langwell = the_transceiver; + + if (!langwell->otg.default_a) + return -1; + if (count > 2) + return -1; + + if (buf[0] == '1') { + langwell->hsm.a_clr_err = 1; + otg_dbg("a_clr_err = 1\n"); + } + if (spin_trylock(&langwell->wq_lock)) { + queue_work(langwell->qwork, &langwell->work); + spin_unlock(&langwell->wq_lock); + } + return count; +} +static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err); + +static struct attribute *inputs_attrs[] = { + &dev_attr_a_bus_req.attr, + &dev_attr_a_bus_drop.attr, + &dev_attr_b_bus_req.attr, + &dev_attr_a_clr_err.attr, + NULL, +}; + +static struct attribute_group debug_dev_attr_group = { + .name = "inputs", + .attrs = inputs_attrs, +}; + +int langwell_register_host(struct pci_driver *host_driver) +{ + int ret = 0; + + the_transceiver->host_ops = host_driver; + queue_work(the_transceiver->qwork, &the_transceiver->work); + otg_dbg("host controller driver is registered\n"); + + return ret; +} +EXPORT_SYMBOL(langwell_register_host); + +void langwell_unregister_host(struct pci_driver *host_driver) +{ + if (the_transceiver->host_ops) + the_transceiver->host_ops->remove(the_transceiver->pdev); + the_transceiver->host_ops = NULL; + the_transceiver->hsm.a_bus_drop = 1; + queue_work(the_transceiver->qwork, &the_transceiver->work); + otg_dbg("host controller driver is unregistered\n"); +} +EXPORT_SYMBOL(langwell_unregister_host); + +int langwell_register_peripheral(struct pci_driver *client_driver) +{ + int ret = 0; + + if (client_driver) + ret = client_driver->probe(the_transceiver->pdev, + client_driver->id_table); + if (!ret) { + the_transceiver->client_ops = client_driver; + queue_work(the_transceiver->qwork, &the_transceiver->work); + otg_dbg("client controller driver is registered\n"); + } + + return ret; +} +EXPORT_SYMBOL(langwell_register_peripheral); + +void langwell_unregister_peripheral(struct pci_driver *client_driver) +{ + if (the_transceiver->client_ops) + the_transceiver->client_ops->remove(the_transceiver->pdev); + the_transceiver->client_ops = NULL; + the_transceiver->hsm.b_bus_req = 0; + queue_work(the_transceiver->qwork, &the_transceiver->work); + otg_dbg("client controller driver is unregistered\n"); +} +EXPORT_SYMBOL(langwell_unregister_peripheral); + +static int langwell_otg_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long resource, len; + void __iomem *base = NULL; + int retval; + u32 val32; + struct langwell_otg *langwell; + char qname[] = "langwell_otg_queue"; + + retval = 0; + otg_dbg("\notg controller is detected.\n"); + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto done; + } + + langwell = kzalloc(sizeof *langwell, GFP_KERNEL); + if (langwell == NULL) { + retval = -ENOMEM; + goto done; + } + the_transceiver = langwell; + + /* control register: BAR 0 */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!request_mem_region(resource, len, driver_name)) { + retval = -EBUSY; + goto err; + } + langwell->region = 1; + + base = ioremap_nocache(resource, len); + if (base == NULL) { + retval = -EFAULT; + goto err; + } + langwell->regs = base; + + if (!pdev->irq) { + otg_dbg("No IRQ.\n"); + retval = -ENODEV; + goto err; + } + + langwell->qwork = create_workqueue(qname); + if (!langwell->qwork) { + otg_dbg("cannot create workqueue %s\n", qname); + retval = -ENOMEM; + goto err; + } + INIT_WORK(&langwell->work, langwell_otg_work); + + /* OTG common part */ + langwell->pdev = pdev; + langwell->otg.dev = &pdev->dev; + langwell->otg.label = driver_name; + langwell->otg.set_host = langwell_otg_set_host; + langwell->otg.set_peripheral = langwell_otg_set_peripheral; + langwell->otg.set_power = langwell_otg_set_power; + langwell->otg.start_srp = langwell_otg_start_srp; + langwell->otg.state = OTG_STATE_UNDEFINED; + if (otg_set_transceiver(&langwell->otg)) { + otg_dbg("can't set transceiver\n"); + retval = -EBUSY; + goto err; + } + + reset_otg(); + init_hsm(); + + spin_lock_init(&langwell->lock); + spin_lock_init(&langwell->wq_lock); + INIT_LIST_HEAD(&active_timers); + langwell_otg_init_timers(&langwell->hsm); + + if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, + driver_name, langwell) != 0) { + otg_dbg("request interrupt %d failed\n", pdev->irq); + retval = -EBUSY; + goto err; + } + + /* enable OTGSC int */ + val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | + OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; + writel(val32, langwell->regs + CI_OTGSC); + + retval = device_create_file(&pdev->dev, &dev_attr_registers); + if (retval < 0) { + otg_dbg("Can't register sysfs attribute: %d\n", retval); + goto err; + } + + retval = device_create_file(&pdev->dev, &dev_attr_hsm); + if (retval < 0) { + otg_dbg("Can't hsm sysfs attribute: %d\n", retval); + goto err; + } + + retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); + if (retval < 0) { + otg_dbg("Can't register sysfs attr group: %d\n", retval); + goto err; + } + + if (langwell->otg.state == OTG_STATE_A_IDLE) + queue_work(langwell->qwork, &langwell->work); + + return 0; + +err: + if (the_transceiver) + langwell_otg_remove(pdev); +done: + return retval; +} + +static void langwell_otg_remove(struct pci_dev *pdev) +{ + struct langwell_otg *langwell; + + langwell = the_transceiver; + + if (langwell->qwork) { + flush_workqueue(langwell->qwork); + destroy_workqueue(langwell->qwork); + } + langwell_otg_free_timers(); + + /* disable OTGSC interrupt as OTGSC doesn't change in reset */ + writel(0, langwell->regs + CI_OTGSC); + + if (pdev->irq) + free_irq(pdev->irq, langwell); + if (langwell->regs) + iounmap(langwell->regs); + if (langwell->region) + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + + otg_set_transceiver(NULL); + pci_disable_device(pdev); + sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); + device_remove_file(&pdev->dev, &dev_attr_hsm); + device_remove_file(&pdev->dev, &dev_attr_registers); + kfree(langwell); + langwell = NULL; +} + +static void transceiver_suspend(struct pci_dev *pdev) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, PCI_D3hot); + langwell_otg_phy_low_power(1); +} + +static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) +{ + int ret = 0; + struct langwell_otg *langwell; + + langwell = the_transceiver; + + /* Disbale OTG interrupts */ + langwell_otg_intr(0); + + if (pdev->irq) + free_irq(pdev->irq, langwell); + + /* Prevent more otg_work */ + flush_workqueue(langwell->qwork); + spin_lock(&langwell->wq_lock); + + /* start actions */ + switch (langwell->otg.state) { + case OTG_STATE_A_IDLE: + case OTG_STATE_B_IDLE: + case OTG_STATE_A_WAIT_VFALL: + case OTG_STATE_A_VBUS_ERR: + transceiver_suspend(pdev); + break; + case OTG_STATE_A_WAIT_VRISE: + langwell_otg_del_timer(a_wait_vrise_tmr); + langwell->hsm.a_srp_det = 0; + langwell_otg_drv_vbus(0); + langwell->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); + break; + case OTG_STATE_A_WAIT_BCON: + langwell_otg_del_timer(a_wait_bcon_tmr); + if (langwell->host_ops) + ret = langwell->host_ops->suspend(pdev, message); + langwell_otg_drv_vbus(0); + break; + case OTG_STATE_A_HOST: + if (langwell->host_ops) + ret = langwell->host_ops->suspend(pdev, message); + langwell_otg_drv_vbus(0); + langwell_otg_phy_low_power(1); + break; + case OTG_STATE_A_SUSPEND: + langwell_otg_del_timer(a_aidl_bdis_tmr); + langwell_otg_HABA(0); + if (langwell->host_ops) + langwell->host_ops->remove(pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell_otg_drv_vbus(0); + transceiver_suspend(pdev); + langwell->otg.state = OTG_STATE_A_WAIT_VFALL; + break; + case OTG_STATE_A_PERIPHERAL: + if (langwell->client_ops) + ret = langwell->client_ops->suspend(pdev, message); + else + otg_dbg("client driver has been removed.\n"); + langwell_otg_drv_vbus(0); + transceiver_suspend(pdev); + langwell->otg.state = OTG_STATE_A_WAIT_VFALL; + break; + case OTG_STATE_B_HOST: + if (langwell->host_ops) + langwell->host_ops->remove(pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell->hsm.b_bus_req = 0; + transceiver_suspend(pdev); + langwell->otg.state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_B_PERIPHERAL: + if (langwell->client_ops) + ret = langwell->client_ops->suspend(pdev, message); + else + otg_dbg("client driver has been removed.\n"); + break; + case OTG_STATE_B_WAIT_ACON: + langwell_otg_del_timer(b_ase0_brst_tmr); + langwell_otg_HAAR(0); + if (langwell->host_ops) + langwell->host_ops->remove(pdev); + else + otg_dbg("host driver has been removed.\n"); + langwell->hsm.b_bus_req = 0; + langwell->otg.state = OTG_STATE_B_IDLE; + transceiver_suspend(pdev); + break; + default: + otg_dbg("error state before suspend\n "); + break; + } + spin_unlock(&langwell->wq_lock); + + return ret; +} + +static void transceiver_resume(struct pci_dev *pdev) +{ + pci_restore_state(pdev); + pci_set_power_state(pdev, PCI_D0); + langwell_otg_phy_low_power(0); +} + +static int langwell_otg_resume(struct pci_dev *pdev) +{ + int ret = 0; + struct langwell_otg *langwell; + + langwell = the_transceiver; + + spin_lock(&langwell->wq_lock); + + switch (langwell->otg.state) { + case OTG_STATE_A_IDLE: + case OTG_STATE_B_IDLE: + case OTG_STATE_A_WAIT_VFALL: + case OTG_STATE_A_VBUS_ERR: + transceiver_resume(pdev); + break; + case OTG_STATE_A_WAIT_BCON: + langwell_otg_add_timer(a_wait_bcon_tmr); + langwell_otg_drv_vbus(1); + if (langwell->host_ops) + ret = langwell->host_ops->resume(pdev); + break; + case OTG_STATE_A_HOST: + langwell_otg_drv_vbus(1); + langwell_otg_phy_low_power(0); + if (langwell->host_ops) + ret = langwell->host_ops->resume(pdev); + break; + case OTG_STATE_B_PERIPHERAL: + if (langwell->client_ops) + ret = langwell->client_ops->resume(pdev); + else + otg_dbg("client driver not loaded.\n"); + break; + default: + otg_dbg("error state before suspend\n "); + break; + } + + if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, + driver_name, the_transceiver) != 0) { + otg_dbg("request interrupt %d failed\n", pdev->irq); + ret = -EBUSY; + } + + /* enable OTG interrupts */ + langwell_otg_intr(1); + + spin_unlock(&langwell->wq_lock); + + queue_work(langwell->qwork, &langwell->work); + + + return ret; +} + +static int __init langwell_otg_init(void) +{ + return pci_register_driver(&otg_pci_driver); +} +module_init(langwell_otg_init); + +static void __exit langwell_otg_cleanup(void) +{ + pci_unregister_driver(&otg_pci_driver); +} +module_exit(langwell_otg_cleanup); diff --git a/include/linux/usb/langwell_otg.h b/include/linux/usb/langwell_otg.h new file mode 100644 index 00000000000..e115ae6df1d --- /dev/null +++ b/include/linux/usb/langwell_otg.h @@ -0,0 +1,177 @@ +/* + * Intel Langwell USB OTG transceiver driver + * Copyright (C) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __LANGWELL_OTG_H__ +#define __LANGWELL_OTG_H__ + +/* notify transceiver driver about OTG events */ +extern void langwell_update_transceiver(void); +/* HCD register bus driver */ +extern int langwell_register_host(struct pci_driver *host_driver); +/* HCD unregister bus driver */ +extern void langwell_unregister_host(struct pci_driver *host_driver); +/* DCD register bus driver */ +extern int langwell_register_peripheral(struct pci_driver *client_driver); +/* DCD unregister bus driver */ +extern void langwell_unregister_peripheral(struct pci_driver *client_driver); +/* No silent failure, output warning message */ +extern void langwell_otg_nsf_msg(unsigned long message); + +#define CI_USBCMD 0x30 +# define USBCMD_RST BIT(1) +# define USBCMD_RS BIT(0) +#define CI_USBSTS 0x34 +# define USBSTS_SLI BIT(8) +# define USBSTS_URI BIT(6) +# define USBSTS_PCI BIT(2) +#define CI_PORTSC1 0x74 +# define PORTSC_PP BIT(12) +# define PORTSC_LS (BIT(11) | BIT(10)) +# define PORTSC_SUSP BIT(7) +# define PORTSC_CCS BIT(0) +#define CI_HOSTPC1 0xb4 +# define HOSTPC1_PHCD BIT(22) +#define CI_OTGSC 0xf4 +# define OTGSC_DPIE BIT(30) +# define OTGSC_1MSE BIT(29) +# define OTGSC_BSEIE BIT(28) +# define OTGSC_BSVIE BIT(27) +# define OTGSC_ASVIE BIT(26) +# define OTGSC_AVVIE BIT(25) +# define OTGSC_IDIE BIT(24) +# define OTGSC_DPIS BIT(22) +# define OTGSC_1MSS BIT(21) +# define OTGSC_BSEIS BIT(20) +# define OTGSC_BSVIS BIT(19) +# define OTGSC_ASVIS BIT(18) +# define OTGSC_AVVIS BIT(17) +# define OTGSC_IDIS BIT(16) +# define OTGSC_DPS BIT(14) +# define OTGSC_1MST BIT(13) +# define OTGSC_BSE BIT(12) +# define OTGSC_BSV BIT(11) +# define OTGSC_ASV BIT(10) +# define OTGSC_AVV BIT(9) +# define OTGSC_ID BIT(8) +# define OTGSC_HABA BIT(7) +# define OTGSC_HADP BIT(6) +# define OTGSC_IDPU BIT(5) +# define OTGSC_DP BIT(4) +# define OTGSC_OT BIT(3) +# define OTGSC_HAAR BIT(2) +# define OTGSC_VC BIT(1) +# define OTGSC_VD BIT(0) +# define OTGSC_INTEN_MASK (0x7f << 24) +# define OTGSC_INTSTS_MASK (0x7f << 16) +#define CI_USBMODE 0xf8 +# define USBMODE_CM (BIT(1) | BIT(0)) +# define USBMODE_IDLE 0 +# define USBMODE_DEVICE 0x2 +# define USBMODE_HOST 0x3 + +#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI) + +struct otg_hsm { + /* Input */ + int a_bus_resume; + int a_bus_suspend; + int a_conn; + int a_sess_vld; + int a_srp_det; + int a_vbus_vld; + int b_bus_resume; + int b_bus_suspend; + int b_conn; + int b_se0_srp; + int b_sess_end; + int b_sess_vld; + int id; + + /* Internal variables */ + int a_set_b_hnp_en; + int b_srp_done; + int b_hnp_enable; + + /* Timeout indicator for timers */ + int a_wait_vrise_tmout; + int a_wait_bcon_tmout; + int a_aidl_bdis_tmout; + int b_ase0_brst_tmout; + int b_bus_suspend_tmout; + int b_srp_res_tmout; + + /* Informative variables */ + int a_bus_drop; + int a_bus_req; + int a_clr_err; + int a_suspend_req; + int b_bus_req; + + /* Output */ + int drv_vbus; + int loc_conn; + int loc_sof; + + /* Others */ + int b_bus_suspend_vld; +}; + +#define TA_WAIT_VRISE 100 +#define TA_WAIT_BCON 30000 +#define TA_AIDL_BDIS 15000 +#define TB_ASE0_BRST 5000 +#define TB_SE0_SRP 2 +#define TB_SRP_RES 100 +#define TB_BUS_SUSPEND 500 + +struct langwell_otg_timer { + unsigned long expires; /* Number of count increase to timeout */ + unsigned long count; /* Tick counter */ + void (*function)(unsigned long); /* Timeout function */ + unsigned long data; /* Data passed to function */ + struct list_head list; +}; + +struct langwell_otg { + struct otg_transceiver otg; + struct otg_hsm hsm; + void __iomem *regs; + unsigned region; + struct pci_driver *host_ops; + struct pci_driver *client_ops; + struct pci_dev *pdev; + struct work_struct work; + struct workqueue_struct *qwork; + spinlock_t lock; + spinlock_t wq_lock; +}; + +static inline struct langwell_otg *otg_to_langwell(struct otg_transceiver *otg) +{ + return container_of(otg, struct langwell_otg, otg); +} + +#ifdef DEBUG +#define otg_dbg(fmt, args...) \ + printk(KERN_DEBUG fmt , ## args) +#else +#define otg_dbg(fmt, args...) \ + do { } while (0) +#endif /* DEBUG */ +#endif /* __LANGWELL_OTG_H__ */ -- cgit v1.2.3-70-g09d2 From 6b403b020c1f42180b14d28d832da61167cff822 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:54:10 -0700 Subject: USB: Add SuperSpeed to the list of USB device speeds. Modify the USB core to handle the new USB 3.0 speed, "SuperSpeed". This is 5.0 Gbps (wire speed). There are probably more places that check for speed that I've missed. SuperSpeed devices have a 512 byte endpoint 0 max packet size. This shows up as a bMaxPacketSize0 set to 0x09 (see table 9-8 of the USB 3.0 bus spec). xHCI spec says that the xHC can handle intervals up to 2^15 microframes. That might change when real silicon becomes available. Add FIXME note for SuperSpeed isochronous endpoints. They can transmit up to 16 packets in one "burst" before they wait for an acknowledgment of the packets. They can do up to 3 bursts per microframe (determined by the mult value in the endpoint companion descriptor). The xHCI driver doesn't have support for isoc yet, so fix this later. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 1 + drivers/usb/core/hcd.c | 16 ++++++++++++++-- drivers/usb/core/hcd.h | 1 + drivers/usb/core/hub.c | 11 +++++++++-- drivers/usb/core/urb.c | 6 ++++++ include/linux/usb/ch9.h | 1 + 6 files changed, 32 insertions(+), 4 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 568244c99bd..e9426acf568 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -92,6 +92,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (usb_endpoint_xfer_int(d)) { i = 1; switch (to_usb_device(ddev)->speed) { + case USB_SPEED_SUPER: case USB_SPEED_HIGH: /* Many device manufacturers are using full-speed * bInterval values in high-speed interrupt endpoint diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index c65d9560402..120bb56c4b8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1870,8 +1870,20 @@ int usb_add_hcd(struct usb_hcd *hcd, retval = -ENOMEM; goto err_allocate_root_hub; } - rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : - USB_SPEED_FULL; + + switch (hcd->driver->flags & HCD_MASK) { + case HCD_USB11: + rhdev->speed = USB_SPEED_FULL; + break; + case HCD_USB2: + rhdev->speed = USB_SPEED_HIGH; + break; + case HCD_USB3: + rhdev->speed = USB_SPEED_SUPER; + break; + default: + goto err_allocate_root_hub; + } hcd->self.root_hub = rhdev; /* wakeup flag init defaults to "everything works" for root hubs, diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 7a47498b0aa..4f6ee60d97c 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -174,6 +174,7 @@ struct hc_driver { #define HCD_USB11 0x0010 /* USB 1.1 */ #define HCD_USB2 0x0020 /* USB 2.0 */ #define HCD_USB3 0x0040 /* USB 3.0 */ +#define HCD_MASK 0x0070 /* called to init HCD and root hub */ int (*reset) (struct usb_hcd *hcd); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c7a1a1d4bcb..fa0208e0da6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2471,6 +2471,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. */ switch (udev->speed) { + case USB_SPEED_SUPER: case USB_SPEED_VARIABLE: /* fixed at 512 */ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); break; @@ -2496,6 +2497,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; + case USB_SPEED_SUPER: + speed = "super"; + break; case USB_SPEED_VARIABLE: speed = "variable"; type = "Wireless "; @@ -2634,8 +2638,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (retval) goto fail; - i = udev->descriptor.bMaxPacketSize0 == 0xff? /* wusb device? */ - 512 : udev->descriptor.bMaxPacketSize0; + if (udev->descriptor.bMaxPacketSize0 == 0xff || + udev->speed == USB_SPEED_SUPER) + i = 512; + else + i = udev->descriptor.bMaxPacketSize0; if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { if (udev->speed != USB_SPEED_FULL || !(i == 8 || i == 16 || i == 32 || i == 64)) { diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3376055f36e..02eb0ef7a4c 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -351,6 +351,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (xfertype == USB_ENDPOINT_XFER_ISOC) { int n, len; + /* FIXME SuperSpeed isoc endpoints have up to 16 bursts */ /* "high bandwidth" mode, 1-3 packets/uframe? */ if (dev->speed == USB_SPEED_HIGH) { int mult = 1 + ((max >> 11) & 0x03); @@ -426,6 +427,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) return -EINVAL; /* too big? */ switch (dev->speed) { + case USB_SPEED_SUPER: /* units are 125us */ + /* Handle up to 2^(16-1) microframes */ + if (urb->interval > (1 << 15)) + return -EINVAL; + max = 1 << 15; case USB_SPEED_HIGH: /* units are microframes */ /* NOTE usb handles 2^15 */ if (urb->interval > (1024 * 8)) diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index b145119a90d..93bfe635234 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -752,6 +752,7 @@ enum usb_device_speed { USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ USB_SPEED_HIGH, /* usb 2.0 */ USB_SPEED_VARIABLE, /* wireless (usb 2.5) */ + USB_SPEED_SUPER, /* usb 3.0 */ }; enum usb_device_state { -- cgit v1.2.3-70-g09d2 From 663c30d0829d556efabd5fbd98fb8473da7fe694 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 27 Apr 2009 19:58:14 -0700 Subject: USB: Parse and store the SuperSpeed endpoint companion descriptors. The USB 3.0 bus specification added an "Endpoint Companion" descriptor that is supposed to follow all SuperSpeed Endpoint descriptors. This descriptor is used to extend the bus protocol to allow more packets to be sent to an endpoint per "microframe". The word microframe was removed from the USB 3.0 specification because the host controller does not send Start Of Frame (SOF) symbols down the USB 3.0 wires. The descriptor defines a bMaxBurst field, which indicates the number of packets of wMaxPacketSize that a SuperSpeed device can send or recieve in a service interval. All non-control endpoints may set this value as high as 16 packets (bMaxBurst = 15). The descriptor also allows isochronous endpoints to further specify that they can send and receive multiple bursts per service interval. The bmAttributes allows them to specify a "Mult" of up to 3 (bmAttributes = 2). Bulk endpoints use bmAttributes to report the number of "Streams" they support. This was an extension of the endpoint pipe concept to allow multiple mass storage device commands to be outstanding for one bulk endpoint at a time. This should allow USB 3.0 mass storage devices to support SCSI command queueing. Bulk endpoints can say they support up to 2^16 (65,536) streams. The information in the endpoint companion descriptor must be stored with the other device, config, interface, and endpoint descriptors because the host controller needs to access them quickly, and we need to install some default values if a SuperSpeed device doesn't provide an endpoint companion descriptor. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 189 +++++++++++++++++++++++++++++++++++++++++++--- include/linux/usb.h | 16 ++++ include/linux/usb/ch9.h | 16 ++++ 3 files changed, 212 insertions(+), 9 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index e9426acf568..7103758bb48 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -19,6 +19,32 @@ static inline const char *plural(int n) return (n == 1 ? "" : "s"); } +/* FIXME: this is a kludge */ +static int find_next_descriptor_more(unsigned char *buffer, int size, + int dt1, int dt2, int dt3, int *num_skipped) +{ + struct usb_descriptor_header *h; + int n = 0; + unsigned char *buffer0 = buffer; + + /* Find the next descriptor of type dt1 or dt2 or dt3 */ + while (size > 0) { + h = (struct usb_descriptor_header *) buffer; + if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2 || + h->bDescriptorType == dt3) + break; + buffer += h->bLength; + size -= h->bLength; + ++n; + } + + /* Store the number of descriptors skipped and return the + * number of bytes skipped */ + if (num_skipped) + *num_skipped = n; + return buffer - buffer0; +} + static int find_next_descriptor(unsigned char *buffer, int size, int dt1, int dt2, int *num_skipped) { @@ -43,6 +69,128 @@ static int find_next_descriptor(unsigned char *buffer, int size, return buffer - buffer0; } +static int usb_parse_endpoint_companion(struct device *ddev, int cfgno, + int inum, int asnum, struct usb_host_endpoint *ep, + int num_ep, unsigned char *buffer, int size) +{ + unsigned char *buffer_start = buffer; + struct usb_ep_comp_descriptor *desc; + int retval; + int num_skipped; + int max_tx; + int i; + + /* Allocate space for the companion descriptor */ + ep->ep_comp = kzalloc(sizeof(struct usb_host_ep_comp), GFP_KERNEL); + if (!ep->ep_comp) + return -ENOMEM; + desc = (struct usb_ep_comp_descriptor *) buffer; + if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { + dev_warn(ddev, "No SuperSpeed endpoint companion for config %d " + " interface %d altsetting %d ep %d: " + "using minimum values\n", + cfgno, inum, asnum, ep->desc.bEndpointAddress); + ep->ep_comp->desc.bLength = USB_DT_EP_COMP_SIZE; + ep->ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; + ep->ep_comp->desc.bMaxBurst = 0; + /* + * Leave bmAttributes as zero, which will mean no streams for + * bulk, and isoc won't support multiple bursts of packets. + * With bursts of only one packet, and a Mult of 1, the max + * amount of data moved per endpoint service interval is one + * packet. + */ + if (usb_endpoint_xfer_isoc(&ep->desc) || + usb_endpoint_xfer_int(&ep->desc)) + ep->ep_comp->desc.wBytesPerInterval = + ep->desc.wMaxPacketSize; + /* + * The next descriptor is for an Endpoint or Interface, + * no extra descriptors to copy into the companion structure, + * and we didn't eat up any of the buffer. + */ + retval = 0; + goto valid; + } + memcpy(&ep->ep_comp->desc, desc, USB_DT_EP_COMP_SIZE); + desc = &ep->ep_comp->desc; + buffer += desc->bLength; + size -= desc->bLength; + + /* Eat up the other descriptors we don't care about */ + ep->ep_comp->extra = buffer; + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, &num_skipped); + ep->ep_comp->extralen = i; + buffer += i; + size -= i; + retval = buffer - buffer_start + i; + if (num_skipped > 0) + dev_dbg(ddev, "skipped %d descriptor%s after %s\n", + num_skipped, plural(num_skipped), + "SuperSpeed endpoint companion"); + + /* Check the various values */ + if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) { + dev_warn(ddev, "Control endpoint with bMaxBurst = %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to zero\n", desc->bMaxBurst, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bMaxBurst = 0; + } + if (desc->bMaxBurst > 15) { + dev_warn(ddev, "Endpoint with bMaxBurst = %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to 15\n", desc->bMaxBurst, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bMaxBurst = 15; + } + if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) + && desc->bmAttributes != 0) { + dev_warn(ddev, "%s endpoint with bmAttributes = %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to zero\n", + usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk", + desc->bmAttributes, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bmAttributes = 0; + } + if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) { + dev_warn(ddev, "Bulk endpoint with more than 65536 streams in " + "config %d interface %d altsetting %d ep %d: " + "setting to max\n", + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bmAttributes = 16; + } + if (usb_endpoint_xfer_isoc(&ep->desc) && desc->bmAttributes > 2) { + dev_warn(ddev, "Isoc endpoint has Mult of %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to 3\n", desc->bmAttributes + 1, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + desc->bmAttributes = 2; + } + if (usb_endpoint_xfer_isoc(&ep->desc)) { + max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) * + (desc->bmAttributes + 1); + } else if (usb_endpoint_xfer_int(&ep->desc)) { + max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1); + } else { + goto valid; + } + if (desc->wBytesPerInterval > max_tx) { + dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to %d\n", + usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int", + desc->wBytesPerInterval, + cfgno, inum, asnum, ep->desc.bEndpointAddress, + max_tx); + desc->wBytesPerInterval = max_tx; + } +valid: + return retval; +} + static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_interface *ifp, int num_ep, unsigned char *buffer, int size) @@ -50,7 +198,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, unsigned char *buffer0 = buffer; struct usb_endpoint_descriptor *d; struct usb_host_endpoint *endpoint; - int n, i, j; + int n, i, j, retval; d = (struct usb_endpoint_descriptor *) buffer; buffer += d->bLength; @@ -162,17 +310,38 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, cfgno, inum, asnum, d->bEndpointAddress, maxp); } - - /* Skip over any Class Specific or Vendor Specific descriptors; - * find the next endpoint or interface descriptor */ - endpoint->extra = buffer; - i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, - USB_DT_INTERFACE, &n); - endpoint->extralen = i; + /* Allocate room for and parse any endpoint companion descriptors */ + if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) { + endpoint->extra = buffer; + i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP, + USB_DT_ENDPOINT, USB_DT_INTERFACE, &n); + endpoint->extralen = i; + buffer += i; + size -= i; + + if (size > 0) { + retval = usb_parse_endpoint_companion(ddev, cfgno, inum, asnum, + endpoint, num_ep, buffer, size); + if (retval >= 0) { + buffer += retval; + retval = buffer - buffer0; + } + } else { + retval = buffer - buffer0; + } + } else { + /* Skip over any Class Specific or Vendor Specific descriptors; + * find the next endpoint or interface descriptor */ + endpoint->extra = buffer; + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, &n); + endpoint->extralen = i; + retval = buffer - buffer0 + i; + } if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", n, plural(n), "endpoint"); - return buffer - buffer0 + i; + return retval; skip_to_next_endpoint_or_interface_descriptor: i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, @@ -453,6 +622,8 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, kref_init(&intfc->ref); } + /* FIXME: parse the BOS descriptor */ + /* Skip over any Class Specific or Vendor Specific descriptors; * find the first interface descriptor */ config->extra = buffer; diff --git a/include/linux/usb.h b/include/linux/usb.h index 112a2d6e922..13bced521b8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -36,6 +36,7 @@ struct wusb_dev; * - configs have one (often) or more interfaces; * - interfaces have one (usually) or more settings; * - each interface setting has zero or (usually) more endpoints. + * - a SuperSpeed endpoint has a companion descriptor * * And there might be other descriptors mixed in with those. * @@ -44,6 +45,19 @@ struct wusb_dev; struct ep_device; +/* For SS devices */ +/** + * struct usb_host_ep_comp - Valid for SuperSpeed devices only + * @desc: endpoint companion descriptor, wMaxPacketSize in native byteorder + * @extra: descriptors following this endpoint companion descriptor + * @extralen: how many bytes of "extra" are valid + */ +struct usb_host_ep_comp { + struct usb_ep_comp_descriptor desc; + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + /** * struct usb_host_endpoint - host-side endpoint descriptor and queue * @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder @@ -51,6 +65,7 @@ struct ep_device; * @hcpriv: for use by HCD; typically holds hardware dma queue head (QH) * with one or more transfer descriptors (TDs) per urb * @ep_dev: ep_device for sysfs info + * @ep_comp: companion descriptor information for this endpoint * @extra: descriptors following this endpoint in the configuration * @extralen: how many bytes of "extra" are valid * @enabled: URBs may be submitted to this endpoint @@ -63,6 +78,7 @@ struct usb_host_endpoint { struct list_head urb_list; void *hcpriv; struct ep_device *ep_dev; /* For sysfs info */ + struct usb_host_ep_comp *ep_comp; /* For SS devices */ unsigned char *extra; /* Extra descriptors */ int extralen; diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 93bfe635234..9e9c5c0a3d7 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -191,6 +191,8 @@ struct usb_ctrlrequest { #define USB_DT_WIRE_ADAPTER 0x21 #define USB_DT_RPIPE 0x22 #define USB_DT_CS_RADIO_CONTROL 0x23 +/* From the USB 3.0 spec */ +#define USB_DT_SS_ENDPOINT_COMP 0x30 /* Conventional codes for class-specific descriptors. The convention is * defined in the USB "Common Class" Spec (3.11). Individual class specs @@ -535,6 +537,20 @@ static inline int usb_endpoint_is_isoc_out( /*-------------------------------------------------------------------------*/ +/* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */ +struct usb_ep_comp_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bMaxBurst; + __u8 bmAttributes; + __u16 wBytesPerInterval; +} __attribute__ ((packed)); + +#define USB_DT_EP_COMP_SIZE 6 + +/*-------------------------------------------------------------------------*/ + /* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */ struct usb_qualifier_descriptor { __u8 bLength; -- cgit v1.2.3-70-g09d2 From f0058c627855ecb3b6c7185b7ad1910463c24c42 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 29 Apr 2009 19:06:20 -0700 Subject: USB: Change names of SuperSpeed ep companion descriptor structs. Differentiate between SuperSpeed endpoint companion descriptor and the wireless USB endpoint companion descriptor. Make all structure names for this descriptor have "ss" (SuperSpeed) in them. David Vrabel asked for this change in http://marc.info/?l=linux-usb&m=124091465109367&w=2 Reported-by: David Vrabel Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 36 +++++++++++++++++++----------------- drivers/usb/host/xhci-mem.c | 2 +- include/linux/usb.h | 14 +++++++------- include/linux/usb/ch9.h | 4 ++-- 4 files changed, 29 insertions(+), 27 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 7103758bb48..24dfb33f90c 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -69,30 +69,31 @@ static int find_next_descriptor(unsigned char *buffer, int size, return buffer - buffer0; } -static int usb_parse_endpoint_companion(struct device *ddev, int cfgno, +static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_endpoint *ep, int num_ep, unsigned char *buffer, int size) { unsigned char *buffer_start = buffer; - struct usb_ep_comp_descriptor *desc; + struct usb_ss_ep_comp_descriptor *desc; int retval; int num_skipped; int max_tx; int i; - /* Allocate space for the companion descriptor */ - ep->ep_comp = kzalloc(sizeof(struct usb_host_ep_comp), GFP_KERNEL); - if (!ep->ep_comp) + /* Allocate space for the SS endpoint companion descriptor */ + ep->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp), + GFP_KERNEL); + if (!ep->ss_ep_comp) return -ENOMEM; - desc = (struct usb_ep_comp_descriptor *) buffer; + desc = (struct usb_ss_ep_comp_descriptor *) buffer; if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { dev_warn(ddev, "No SuperSpeed endpoint companion for config %d " " interface %d altsetting %d ep %d: " "using minimum values\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); - ep->ep_comp->desc.bLength = USB_DT_EP_COMP_SIZE; - ep->ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; - ep->ep_comp->desc.bMaxBurst = 0; + ep->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE; + ep->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; + ep->ss_ep_comp->desc.bMaxBurst = 0; /* * Leave bmAttributes as zero, which will mean no streams for * bulk, and isoc won't support multiple bursts of packets. @@ -102,7 +103,7 @@ static int usb_parse_endpoint_companion(struct device *ddev, int cfgno, */ if (usb_endpoint_xfer_isoc(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) - ep->ep_comp->desc.wBytesPerInterval = + ep->ss_ep_comp->desc.wBytesPerInterval = ep->desc.wMaxPacketSize; /* * The next descriptor is for an Endpoint or Interface, @@ -112,16 +113,16 @@ static int usb_parse_endpoint_companion(struct device *ddev, int cfgno, retval = 0; goto valid; } - memcpy(&ep->ep_comp->desc, desc, USB_DT_EP_COMP_SIZE); - desc = &ep->ep_comp->desc; + memcpy(&ep->ss_ep_comp->desc, desc, USB_DT_SS_EP_COMP_SIZE); + desc = &ep->ss_ep_comp->desc; buffer += desc->bLength; size -= desc->bLength; /* Eat up the other descriptors we don't care about */ - ep->ep_comp->extra = buffer; + ep->ss_ep_comp->extra = buffer; i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, &num_skipped); - ep->ep_comp->extralen = i; + ep->ss_ep_comp->extralen = i; buffer += i; size -= i; retval = buffer - buffer_start + i; @@ -310,7 +311,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, cfgno, inum, asnum, d->bEndpointAddress, maxp); } - /* Allocate room for and parse any endpoint companion descriptors */ + /* Allocate room for and parse any SS endpoint companion descriptors */ if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) { endpoint->extra = buffer; i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP, @@ -320,8 +321,9 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, size -= i; if (size > 0) { - retval = usb_parse_endpoint_companion(ddev, cfgno, inum, asnum, - endpoint, num_ep, buffer, size); + retval = usb_parse_ss_endpoint_companion(ddev, cfgno, + inum, asnum, endpoint, num_ep, buffer, + size); if (retval >= 0) { buffer += retval; retval = buffer - buffer0; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index f49f280cfd4..a7fbd6c10ad 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -496,7 +496,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, max_packet = ep->desc.wMaxPacketSize; ep_ctx->ep_info2 |= MAX_PACKET(max_packet); /* dig out max burst from ep companion desc */ - max_packet = ep->ep_comp->desc.bMaxBurst; + max_packet = ep->ss_ep_comp->desc.bMaxBurst; ep_ctx->ep_info2 |= MAX_BURST(max_packet); break; case USB_SPEED_HIGH: diff --git a/include/linux/usb.h b/include/linux/usb.h index 0a1819a6497..7e6b5259ea3 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -47,15 +47,15 @@ struct ep_device; /* For SS devices */ /** - * struct usb_host_ep_comp - Valid for SuperSpeed devices only + * struct usb_host_ss_ep_comp - Valid for SuperSpeed devices only * @desc: endpoint companion descriptor, wMaxPacketSize in native byteorder * @extra: descriptors following this endpoint companion descriptor * @extralen: how many bytes of "extra" are valid */ -struct usb_host_ep_comp { - struct usb_ep_comp_descriptor desc; - unsigned char *extra; /* Extra descriptors */ - int extralen; +struct usb_host_ss_ep_comp { + struct usb_ss_ep_comp_descriptor desc; + unsigned char *extra; /* Extra descriptors */ + int extralen; }; /** @@ -65,7 +65,7 @@ struct usb_host_ep_comp { * @hcpriv: for use by HCD; typically holds hardware dma queue head (QH) * with one or more transfer descriptors (TDs) per urb * @ep_dev: ep_device for sysfs info - * @ep_comp: companion descriptor information for this endpoint + * @ss_ep_comp: companion descriptor information for this endpoint * @extra: descriptors following this endpoint in the configuration * @extralen: how many bytes of "extra" are valid * @enabled: URBs may be submitted to this endpoint @@ -78,7 +78,7 @@ struct usb_host_endpoint { struct list_head urb_list; void *hcpriv; struct ep_device *ep_dev; /* For sysfs info */ - struct usb_host_ep_comp *ep_comp; /* For SS devices */ + struct usb_host_ss_ep_comp *ss_ep_comp; /* For SS devices */ unsigned char *extra; /* Extra descriptors */ int extralen; diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 9e9c5c0a3d7..93223638f70 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -538,7 +538,7 @@ static inline int usb_endpoint_is_isoc_out( /*-------------------------------------------------------------------------*/ /* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */ -struct usb_ep_comp_descriptor { +struct usb_ss_ep_comp_descriptor { __u8 bLength; __u8 bDescriptorType; @@ -547,7 +547,7 @@ struct usb_ep_comp_descriptor { __u16 wBytesPerInterval; } __attribute__ ((packed)); -#define USB_DT_EP_COMP_SIZE 6 +#define USB_DT_SS_EP_COMP_SIZE 6 /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-70-g09d2