diff options
Diffstat (limited to 'drivers/usb/serial')
36 files changed, 2022 insertions, 792 deletions
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 4a86696e6c7..c1e65dfd935 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -2,10 +2,7 @@ # USB Serial device configuration # -menu "USB Serial Converter support" - depends on USB!=n - -config USB_SERIAL +menuconfig USB_SERIAL tristate "USB Serial Converter support" depends on USB ---help--- @@ -20,6 +17,8 @@ config USB_SERIAL To compile this driver as a module, choose M here: the module will be called usbserial. +if USB_SERIAL + config USB_SERIAL_CONSOLE bool "USB Serial Console device support (EXPERIMENTAL)" depends on USB_SERIAL=y && EXPERIMENTAL @@ -43,6 +42,12 @@ config USB_SERIAL_CONSOLE If unsure, say N. +config USB_EZUSB + bool "Functions for loading firmware on EZUSB chips" + depends on USB_SERIAL + help + Say Y here if you need EZUSB device support. + config USB_SERIAL_GENERIC bool "USB Generic Serial Driver" depends on USB_SERIAL @@ -105,6 +110,7 @@ config USB_SERIAL_CH341 config USB_SERIAL_WHITEHEAT tristate "USB ConnectTech WhiteHEAT Serial Driver" depends on USB_SERIAL + select USB_EZUSB help Say Y here if you want to use a ConnectTech WhiteHEAT 4 port USB to serial converter device. @@ -282,9 +288,21 @@ config USB_SERIAL_IPW To compile this driver as a module, choose M here: the module will be called ipw. +config USB_SERIAL_IUU + tristate "USB Infinity USB Unlimited Phoenix Driver (Experimental)" + depends on USB_SERIAL && EXPERIMENTAL + help + Say Y here if you want to use a IUU in phoenix mode and get + an extra ttyUSBx device. More information available on + http://eczema.ecze.com/iuu_phoenix.html + + To compile this driver as a module, choose M here: the + module will be called iuu_phoenix.o + config USB_SERIAL_KEYSPAN_PDA tristate "USB Keyspan PDA Single Port Serial Driver" depends on USB_SERIAL + select USB_EZUSB help Say Y here if you want to use a Keyspan PDA single port USB to serial converter device. This driver makes use of firmware @@ -296,6 +314,7 @@ config USB_SERIAL_KEYSPAN_PDA config USB_SERIAL_KEYSPAN tristate "USB Keyspan USA-xxx Serial Driver" depends on USB_SERIAL + select USB_EZUSB ---help--- Say Y here if you want to use Keyspan USB to serial converter devices. This driver makes use of Keyspan's official firmware @@ -538,6 +557,7 @@ config USB_SERIAL_CYBERJACK config USB_SERIAL_XIRCOM tristate "USB Xircom / Entregra Single Port Serial Driver" depends on USB_SERIAL + select USB_EZUSB help Say Y here if you want to use a Xircom or Entregra single port USB to serial converter device. This driver makes use of firmware @@ -585,11 +605,4 @@ config USB_SERIAL_DEBUG To compile this driver as a module, choose M here: the module will be called usb-debug. -config USB_EZUSB - bool - depends on USB_SERIAL_KEYSPAN_PDA || USB_SERIAL_XIRCOM || USB_SERIAL_KEYSPAN || USB_SERIAL_WHITEHEAT - default y - - -endmenu - +endif # USB_SERIAL diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index d6fb384e52b..0db109a54d1 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o obj-$(CONFIG_USB_SERIAL_HP4X) += hp4x.o obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o +obj-$(CONFIG_USB_SERIAL_IUU) += iuu_phoenix.o obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index 77bb893bf2e..f156dba0300 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -217,7 +217,10 @@ static void airprime_close(struct usb_serial_port *port, struct file * filp) priv->rts_state = 0; priv->dtr_state = 0; - airprime_send_setup(port); + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) + airprime_send_setup(port); + mutex_lock(&port->serial->disc_mutex); for (i = 0; i < NUM_READ_URBS; ++i) { usb_kill_urb (priv->read_urbp[i]); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index ddfee918000..fe2bfd67ba8 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -151,8 +151,10 @@ static int ark3116_attach(struct usb_serial *serial) return 0; cleanup: - for (--i; i >= 0; --i) + for (--i; i >= 0; --i) { + kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); + } return -ENOMEM; } diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 86724e88570..df0a2b3b029 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -350,14 +350,12 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios unsigned long control_state; int bad_flow_control; speed_t baud; + struct ktermios *termios = port->tty->termios; - if ((!port->tty) || (!port->tty->termios)) { - dbg ("%s - no tty or termios structure", __FUNCTION__); - return; - } + iflag = termios->c_iflag; + cflag = termios->c_cflag; - iflag = port->tty->termios->c_iflag; - cflag = port->tty->termios->c_cflag; + termios->c_cflag &= ~CMSPAR; /* get a local copy of the current port settings */ spin_lock_irqsave(&priv->lock, flags); @@ -369,33 +367,30 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios old_cflag = old_termios->c_cflag; /* Set the baud rate */ - if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { + if ((cflag & CBAUD) != (old_cflag & CBAUD)) { /* reassert DTR and (maybe) RTS on transition from B0 */ if( (old_cflag&CBAUD) == B0 ) { control_state |= (TIOCM_DTR|TIOCM_RTS); if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 1) < 0) err("Set DTR error"); /* don't set RTS if using hardware flow control */ - if (!(old_cflag&CRTSCTS) ) + if (!(old_cflag & CRTSCTS)) if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 1) < 0) err("Set RTS error"); } } baud = tty_get_baud_rate(port->tty); - if (baud == 0) { - dbg("%s - tty_get_baud_rate says 0 baud", __FUNCTION__); - return; - } - urb_value = BELKIN_SA_BAUD(baud); - /* Clip to maximum speed */ - if (urb_value == 0) - urb_value = 1; - /* Turn it back into a resulting real baud rate */ - baud = BELKIN_SA_BAUD(urb_value); - /* FIXME: Once the tty updates are done then push this back to the tty */ - - if ((cflag & CBAUD) != B0 ) { + if (baud) { + urb_value = BELKIN_SA_BAUD(baud); + /* Clip to maximum speed */ + if (urb_value == 0) + urb_value = 1; + /* Turn it back into a resulting real baud rate */ + baud = BELKIN_SA_BAUD(urb_value); + + /* Report the actual baud rate back to the caller */ + tty_encode_baud_rate(port->tty, baud, baud); if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) err("Set baudrate error"); } else { diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 0362654d3b5..66ce30c1b75 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -64,8 +64,8 @@ static int usb_console_setup(struct console *co, char *options) struct usb_serial *serial; struct usb_serial_port *port; int retval = 0; - struct tty_struct *tty; - struct ktermios *termios; + struct tty_struct *tty = NULL; + struct ktermios *termios = NULL, dummy; dbg ("%s", __FUNCTION__); @@ -133,11 +133,14 @@ static int usb_console_setup(struct console *co, char *options) } co->cflag = cflag; - /* grab the first serial port that happens to be connected */ - serial = usb_serial_get_by_index(0); + /* + * no need to check the index here: if the index is wrong, console + * code won't call us + */ + serial = usb_serial_get_by_index(co->index); if (serial == NULL) { /* no device is connected yet, sorry :( */ - err ("No USB device connected to ttyUSB0"); + err ("No USB device connected to ttyUSB%i", co->index); return -ENODEV; } @@ -148,49 +151,64 @@ static int usb_console_setup(struct console *co, char *options) ++port->open_count; if (port->open_count == 1) { + if (serial->type->set_termios) { + /* + * allocate a fake tty so the driver can initialize + * the termios structure, then later call set_termios to + * configure according to command line arguments + */ + tty = kzalloc(sizeof(*tty), GFP_KERNEL); + if (!tty) { + retval = -ENOMEM; + err("no more memory"); + goto reset_open_count; + } + termios = kzalloc(sizeof(*termios), GFP_KERNEL); + if (!termios) { + retval = -ENOMEM; + err("no more memory"); + goto free_tty; + } + memset(&dummy, 0, sizeof(struct ktermios)); + tty->termios = termios; + port->tty = tty; + } + /* only call the device specific open if this * is the first time the port is opened */ if (serial->type->open) retval = serial->type->open(port, NULL); else retval = usb_serial_generic_open(port, NULL); - if (retval) - port->open_count = 0; - } - if (retval) { - err ("could not open USB console port"); - return retval; - } - - if (serial->type->set_termios) { - struct ktermios dummy; - /* build up a fake tty structure so that the open call has something - * to look at to get the cflag value */ - tty = kzalloc(sizeof(*tty), GFP_KERNEL); - if (!tty) { - err ("no more memory"); - return -ENOMEM; + if (retval) { + err("could not open USB console port"); + goto free_termios; } - termios = kzalloc(sizeof(*termios), GFP_KERNEL); - if (!termios) { - err ("no more memory"); - kfree (tty); - return -ENOMEM; - } - memset(&dummy, 0, sizeof(struct ktermios)); - termios->c_cflag = cflag; - tty->termios = termios; - port->tty = tty; - /* set up the initial termios settings */ - serial->type->set_termios(port, &dummy); - port->tty = NULL; - kfree (termios); - kfree (tty); + if (serial->type->set_termios) { + termios->c_cflag = cflag; + serial->type->set_termios(port, &dummy); + + port->tty = NULL; + kfree(termios); + kfree(tty); + } } + port->console = 1; + retval = 0; + +out: return retval; +free_termios: + kfree(termios); + port->tty = NULL; +free_tty: + kfree(tty); +reset_open_count: + port->open_count = 0; +goto out; } static void usb_console_write(struct console *co, const char *buf, unsigned count) diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 3a83cb4c4bc..f3ca66017a0 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -55,9 +55,11 @@ static int debug; static struct usb_device_id id_table [] = { { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */ { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ + { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ + { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ @@ -71,11 +73,17 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */ { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ + { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ + { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */ + { USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */ + { USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */ { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */ { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ + { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ { } /* Terminating Entry */ }; @@ -98,8 +106,8 @@ static struct usb_serial_driver cp2101_device = { .usb_driver = &cp2101_driver, .id_table = id_table, .num_interrupt_in = 0, - .num_bulk_in = 0, - .num_bulk_out = 0, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, .open = cp2101_open, .close = cp2101_close, @@ -340,7 +348,10 @@ static void cp2101_close (struct usb_serial_port *port, struct file * filp) usb_kill_urb(port->write_urb); usb_kill_urb(port->read_urb); - cp2101_set_config_single(port, CP2101_UART, UART_DISABLE); + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) + cp2101_set_config_single(port, CP2101_UART, UART_DISABLE); + mutex_unlock(&port->serial->disc_mutex); } /* diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 4353df92487..8d9b045aa7e 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -319,7 +319,6 @@ static void cyberjack_read_int_callback( struct urb *urb ) /* React only to interrupts signaling a bulk_in transfer */ if( (urb->actual_length==4) && (data[0]==0x01) ) { short old_rdtodo; - int result; /* This is a announcement of coming bulk_ins. */ unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 163386336a5..08c65c1a377 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -682,7 +682,6 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) { struct cypress_private *priv = usb_get_serial_port_data(port); unsigned int c_cflag; - unsigned long flags; int bps; long timeout; wait_queue_t wait; @@ -690,7 +689,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); /* wait for data to drain from buffer */ - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); timeout = CYPRESS_CLOSING_WAIT; init_waitqueue_entry(&wait, current); add_wait_queue(&port->tty->write_wait, &wait); @@ -698,18 +697,25 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) set_current_state(TASK_INTERRUPTIBLE); if (cypress_buf_data_avail(priv->buf) == 0 || timeout == 0 || signal_pending(current) - || !usb_get_intfdata(port->serial->interface)) + /* without mutex, allowed due to harmless failure mode */ + || port->serial->disconnected) break; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); timeout = schedule_timeout(timeout); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); } set_current_state(TASK_RUNNING); remove_wait_queue(&port->tty->write_wait, &wait); /* clear out any remaining data in the buffer */ cypress_buf_clear(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); - + 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 */ bps = tty_get_baud_rate(port->tty); if (bps > 1200) @@ -727,10 +733,10 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) if (c_cflag & HUPCL) { /* drop dtr and rts */ priv = usb_get_serial_port_data(port); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->line_control = 0; priv->cmd_ctrl = 1; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); cypress_write(port, NULL, 0); } } @@ -738,6 +744,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) if (stats) dev_info (&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", priv->bytes_in, priv->bytes_out, priv->cmd_count); + mutex_unlock(&port->serial->disc_mutex); } /* cypress_close */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index ae410c4678e..5f9c6e46bee 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1405,19 +1405,19 @@ static void digi_close(struct usb_serial_port *port, struct file *filp) unsigned char buf[32]; struct tty_struct *tty = port->tty; struct digi_port *priv = usb_get_serial_port_data(port); - unsigned long flags = 0; dbg("digi_close: TOP: port=%d, open_count=%d", priv->dp_port_num, port->open_count); + mutex_lock(&port->serial->disc_mutex); /* if disconnected, just clear flags */ - if (!usb_get_intfdata(port->serial->interface)) + if (port->serial->disconnected) goto exit; /* do cleanup only after final close on this port */ - spin_lock_irqsave(&priv->dp_port_lock, flags); + spin_lock_irq(&priv->dp_port_lock); priv->dp_in_close = 1; - spin_unlock_irqrestore(&priv->dp_port_lock, flags); + spin_unlock_irq(&priv->dp_port_lock); /* tell line discipline to process only XON/XOFF */ tty->closing = 1; @@ -1482,11 +1482,12 @@ static void digi_close(struct usb_serial_port *port, struct file *filp) } tty->closing = 0; exit: - spin_lock_irqsave(&priv->dp_port_lock, flags); + 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_irqrestore(&priv->dp_port_lock, flags); + spin_unlock_irq(&priv->dp_port_lock); + mutex_unlock(&port->serial->disc_mutex); dbg("digi_close: done"); } diff --git a/drivers/usb/serial/ezusb.c b/drivers/usb/serial/ezusb.c index 97ee718b1da..3f698baa0ab 100644 --- a/drivers/usb/serial/ezusb.c +++ b/drivers/usb/serial/ezusb.c @@ -53,6 +53,6 @@ int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit) } -EXPORT_SYMBOL(ezusb_writememory); -EXPORT_SYMBOL(ezusb_set_reset); +EXPORT_SYMBOL_GPL(ezusb_writememory); +EXPORT_SYMBOL_GPL(ezusb_set_reset); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index c40e77dccf8..90dcc625f70 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -17,226 +17,8 @@ * See http://ftdi-usb-sio.sourceforge.net for upto date testing info * and extra documentation * - * (21/Jul/2004) Ian Abbott - * Incorporated Steven Turner's code to add support for the FT2232C chip. - * The prelimilary port to the 2.6 kernel was by Rus V. Brushkoff. I have - * fixed a couple of things. - * - * (27/May/2004) Ian Abbott - * Improved throttling code, mostly stolen from the WhiteHEAT driver. - * - * (26/Mar/2004) Jan Capek - * Added PID's for ICD-U20/ICD-U40 - incircuit PIC debuggers from CCS Inc. - * - * (09/Feb/2004) Ian Abbott - * Changed full name of USB-UIRT device to avoid "/" character. - * Added FTDI's alternate PID (0x6006) for FT232/245 devices. - * Added PID for "ELV USB Module UO100" from Stefan Frings. - * - * (21/Oct/2003) Ian Abbott - * Renamed some VID/PID macros for Matrix Orbital and Perle Systems - * devices. Removed Matrix Orbital and Perle Systems devices from the - * 8U232AM device table, but left them in the FT232BM table, as they are - * known to use only FT232BM. - * - * (17/Oct/2003) Scott Allen - * Added vid/pid for Perle Systems UltraPort USB serial converters - * - * (21/Sep/2003) Ian Abbott - * Added VID/PID for Omnidirectional Control Technology US101 USB to - * RS-232 adapter (also rebadged as Dick Smith Electronics XH6381). - * VID/PID supplied by Donald Gordon. - * - * (19/Aug/2003) Ian Abbott - * Freed urb's transfer buffer in write bulk callback. - * Omitted some paranoid checks in write bulk callback that don't matter. - * Scheduled work in write bulk callback regardless of port's open count. - * - * (05/Aug/2003) Ian Abbott - * Added VID/PID for ID TECH IDT1221U USB to RS-232 adapter. - * VID/PID provided by Steve Briggs. - * - * (23/Jul/2003) Ian Abbott - * Added PIDs for CrystalFontz 547, 633, 631, 635, 640 and 640 from - * Wayne Wylupski. - * - * (10/Jul/2003) David Glance - * Added PID for DSS-20 SyncStation cradle for Sony-Ericsson P800. - * - * (27/Jun/2003) Ian Abbott - * Reworked the urb handling logic. We have no more pool, but dynamically - * allocate the urb and the transfer buffer on the fly. In testing this - * does not incure any measurable overhead. This also relies on the fact - * that we have proper reference counting logic for urbs. I nicked this - * from Greg KH's Visor driver. - * - * (23/Jun/2003) Ian Abbott - * Reduced flip buffer pushes and corrected a data length test in - * ftdi_read_bulk_callback. - * Defererence pointers after any paranoid checks, not before. - * - * (21/Jun/2003) Erik Nygren - * Added support for Home Electronics Tira-1 IR transceiver using FT232BM chip. - * See <http://www.home-electro.com/tira1.htm>. Only operates properly - * at 100000 and RTS-CTS, so set custom divisor mode on startup. - * Also force the Tira-1 and USB-UIRT to only use their custom baud rates. - * - * (18/Jun/2003) Ian Abbott - * Added Device ID of the USB relais from Rudolf Gugler (backported from - * Philipp Gühring's patch for 2.5.x kernel). - * Moved read transfer buffer reallocation into startup function. - * Free existing write urb and transfer buffer in startup function. - * Only use urbs in write urb pool that were successfully allocated. - * Moved some constant macros out of functions. - * Minor whitespace and comment changes. - * - * (12/Jun/2003) David Norwood - * Added support for USB-UIRT IR transceiver using 8U232AM chip. - * See <http://home.earthlink.net/~jrhees/USBUIRT/index.htm>. Only - * operates properly at 312500, so set custom divisor mode on startup. - * - * (12/Jun/2003) Ian Abbott - * Added Sealevel SeaLINK+ 210x, 220x, 240x, 280x vid/pids from Tuan Hoang - * - I've eliminated some that don't seem to exist! - * Added Home Electronics Tira-1 IR transceiver pid from Chris Horn - * Some whitespace/coding-style cleanups - * - * (11/Jun/2003) Ian Abbott - * Fixed unsafe spinlock usage in ftdi_write - * - * (24/Feb/2003) Richard Shooter - * Increase read buffer size to improve read speeds at higher baud rates - * (specifically tested with up to 1Mb/sec at 1.5M baud) - * - * (23/Feb/2003) John Wilkins - * Added Xon/xoff flow control (activating support in the ftdi device) - * Added vid/pid for Videonetworks/Homechoice (UK ISP) - * - * (23/Feb/2003) Bill Ryder - * Added matrix orb device vid/pids from Wayne Wylupski - * - * (19/Feb/2003) Ian Abbott - * For TIOCSSERIAL, set alt_speed to 0 when ASYNC_SPD_MASK value has - * changed to something other than ASYNC_SPD_HI, ASYNC_SPD_VHI, - * ASYNC_SPD_SHI or ASYNC_SPD_WARP. Also, unless ASYNC_SPD_CUST is in - * force, don't bother changing baud rate when custom_divisor has changed. - * - * (18/Feb/2003) Ian Abbott - * Fixed TIOCMGET handling to include state of DTR and RTS, the state - * of which are now saved by set_dtr() and set_rts(). - * Fixed improper storage class for buf in set_dtr() and set_rts(). - * Added FT232BM chip type and support for its extra baud rates (compared - * to FT8U232AM). - * Took account of special case divisor values for highest baud rates of - * FT8U232AM and FT232BM. - * For TIOCSSERIAL, forced alt_speed to 0 when ASYNC_SPD_CUST kludge used, - * as previous alt_speed setting is now stale. - * Moved startup code common between the startup routines for the - * different chip types into a common subroutine. - * - * (17/Feb/2003) Bill Ryder - * Added write urb buffer pool on a per device basis - * Added more checking for open file on callbacks (fixed OOPS) - * Added CrystalFontz 632 and 634 PIDs - * (thanx to CrystalFontz for the sample devices - they flushed out - * some driver bugs) - * Minor debugging message changes - * Added throttle, unthrottle and chars_in_buffer functions - * Fixed FTDI_SIO (the original device) bug - * Fixed some shutdown handling - * - * - * - * - * (07/Jun/2002) Kuba Ober - * Changed FTDI_SIO_BASE_BAUD_TO_DIVISOR macro into ftdi_baud_to_divisor - * function. It was getting too complex. - * Fix the divisor calculation logic which was setting divisor of 0.125 - * instead of 0.5 for fractional parts of divisor equal to 5/8, 6/8, 7/8. - * Also make it bump up the divisor to next integer in case of 7/8 - it's - * a better approximation. - * - * (25/Jul/2002) Bill Ryder inserted Dmitri's TIOCMIWAIT patch - * Not tested by me but it doesn't break anything I use. - * - * (04/Jan/2002) Kuba Ober - * Implemented 38400 baudrate kludge, where it can be substituted with other - * values. That's the only way to set custom baudrates. - * Implemented TIOCSSERIAL, TIOCGSERIAL ioctl's so that setserial is happy. - * FIXME: both baudrate things should eventually go to usbserial.c as other - * devices may need that functionality too. Actually, it can probably be - * merged in serial.c somehow - too many drivers repeat this code over - * and over. - * Fixed baudrate forgetfulness - open() used to reset baudrate to 9600 every time. - * Divisors for baudrates are calculated by a macro. - * Small code cleanups. Ugly whitespace changes for Plato's sake only ;-]. - * - * (04/Nov/2001) Bill Ryder - * Fixed bug in read_bulk_callback where incorrect urb buffer was used. - * Cleaned up write offset calculation - * Added write_room since default values can be incorrect for sio - * Changed write_bulk_callback to use same queue_task as other drivers - * (the previous version caused panics) - * Removed port iteration code since the device only has one I/O port and it - * was wrong anyway. - * - * (31/May/2001) gkh - * Switched from using spinlock to a semaphore, which fixes lots of problems. - * - * (23/May/2001) Bill Ryder - * Added runtime debug patch (thanx Tyson D Sawyer). - * Cleaned up comments for 8U232 - * Added parity, framing and overrun error handling - * Added receive break handling. - * - * (04/08/2001) gb - * Identify version on module load. - * - * (18/March/2001) Bill Ryder - * (Not released) - * Added send break handling. (requires kernel patch too) - * Fixed 8U232AM hardware RTS/CTS etc status reporting. - * Added flipbuf fix copied from generic device - * - * (12/3/2000) Bill Ryder - * Added support for 8U232AM device. - * Moved PID and VIDs into header file only. - * Turned on low-latency for the tty (device will do high baudrates) - * Added shutdown routine to close files when device removed. - * More debug and error message cleanups. - * - * (11/13/2000) Bill Ryder - * Added spinlock protected open code and close code. - * Multiple opens work (sort of - see webpage mentioned above). - * Cleaned up comments. Removed multiple PID/VID definitions. - * Factorised cts/dtr code - * Made use of __FUNCTION__ in dbg's - * - * (11/01/2000) Adam J. Richter - * usb_device_id table support - * - * (10/05/2000) gkh - * Fixed bug with urb->dev not being set properly, now that the usb - * core needs it. - * - * (09/11/2000) gkh - * Removed DEBUG #ifdefs with call to usb_serial_debug_data - * - * (07/19/2000) gkh - * Added module_init and module_exit functions to handle the fact that this - * driver is a loadable module now. - * - * (04/04/2000) Bill Ryder - * Fixed bugs in TCGET/TCSET ioctls (by removing them - they are - * handled elsewhere in the tty io driver chain). - * - * (03/30/2000) Bill Ryder - * Implemented lots of ioctls - * Fixed a race condition in write - * Changed some dbg's to errs - * - * (03/26/2000) gkh - * Split driver up into device specific pieces. + * Change entries from 2004 and earlier can be found in versions of this + * file in kernel versions prior to the 2.6.24 release. * */ @@ -309,12 +91,12 @@ struct ftdi_sio_quirk { void (*port_probe)(struct ftdi_private *); /* Special settings for probed ports. */ }; -static int ftdi_olimex_probe (struct usb_serial *serial); +static int ftdi_jtag_probe (struct usb_serial *serial); static void ftdi_USB_UIRT_setup (struct ftdi_private *priv); static void ftdi_HE_TIRA1_setup (struct ftdi_private *priv); -static struct ftdi_sio_quirk ftdi_olimex_quirk = { - .probe = ftdi_olimex_probe, +static struct ftdi_sio_quirk ftdi_jtag_quirk = { + .probe = ftdi_jtag_probe, }; static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = { @@ -471,30 +253,28 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, /* - * These will probably use user-space drivers. Uncomment them if - * you need them or use the user-specified vendor/product module - * parameters (see ftdi_sio.h for the numbers). Make a fuss if - * you think the driver should recognize any of them by default. + * Due to many user requests for multiple ELV devices we enable + * them by default. */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_UDF77_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_UIO88_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_UAD8_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_UDA7_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_USI2_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_T1100_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_PCD200_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_ULA200_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_CSI8_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1000DL_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, */ - /* { USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) }, */ + { USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UDF77_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UIO88_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UAD8_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UDA7_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_USI2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_T1100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_PCD200_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_ULA200_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_CSI8_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1000DL_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) }, { USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) }, { USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) }, { USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) }, @@ -545,6 +325,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16C_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HR_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HRC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16IC_PID) }, { USB_DEVICE(KOBIL_VID, KOBIL_CONV_B1_PID) }, { USB_DEVICE(KOBIL_VID, KOBIL_CONV_KAAN_PID) }, { USB_DEVICE(POSIFLEX_VID, POSIFLEX_PP7000_PID) }, @@ -569,8 +350,13 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) }, { USB_DEVICE(TML_VID, TML_USB_SERIAL_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID), - .driver_info = (kernel_ulong_t)&ftdi_olimex_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; @@ -1283,10 +1069,11 @@ static void ftdi_HE_TIRA1_setup (struct ftdi_private *priv) } /* ftdi_HE_TIRA1_setup */ /* - * First port on Olimex arm-usb-ocd is reserved for JTAG interface - * and can be accessed from userspace using openocd. + * First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko + * Neo1973 Debug Board is reserved for JTAG interface and can be accessed from + * userspace using openocd. */ -static int ftdi_olimex_probe(struct usb_serial *serial) +static int ftdi_jtag_probe(struct usb_serial *serial) { struct usb_device *udev = serial->dev; struct usb_interface *interface = serial->interface; @@ -1294,7 +1081,7 @@ static int ftdi_olimex_probe(struct usb_serial *serial) dbg("%s",__FUNCTION__); if (interface == udev->actconfig->interface[0]) { - info("Ignoring reserved serial port on Olimex arm-usb-ocd\n"); + info("Ignoring serial port reserved for JTAG"); return -ENODEV; } @@ -1411,7 +1198,8 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) dbg("%s", __FUNCTION__); - if (c_cflag & HUPCL){ + 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), @@ -1425,6 +1213,7 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) /* 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(&priv->rx_work); diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index b51cbb0eaa0..6eee2ab914e 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -98,6 +98,10 @@ #define FTDI_MTXORB_5_PID 0xFA05 /* Matrix Orbital Product Id */ #define FTDI_MTXORB_6_PID 0xFA06 /* Matrix Orbital Product Id */ +/* OOCDlink by Joern Kaipf <joernk@web.de> + * (http://www.joernonline.de/dw/doku.php?id=start&idx=projects:oocdlink) */ +#define FTDI_OOCDLINK_PID 0xbaf8 /* Amontec JTAGkey */ + /* Interbiometrics USB I/O Board */ /* Developed for Interbiometrics by Rudolf Gugler */ #define INTERBIOMETRICS_VID 0x1209 @@ -245,6 +249,7 @@ #define FTDI_ELV_WS300PC_PID 0xE0F6 /* PC-Wetterstation (WS 300 PC) */ #define FTDI_ELV_FHZ1300PC_PID 0xE0E8 /* FHZ 1300 PC */ #define FTDI_ELV_WS500_PID 0xE0E9 /* PC-Wetterstation (WS 500) */ +#define FTDI_ELV_EM1010PC_PID 0xE0EF /* Engery monitor EM 1010 PC */ /* * Definitions for ID TECH (www.idt-net.com) devices @@ -278,6 +283,7 @@ #define FTDI_ATIK_ATK16C_PID 0xDF32 /* ATIK ATK-16C Colour Camera */ #define FTDI_ATIK_ATK16HR_PID 0xDF31 /* ATIK ATK-16HR Grayscale Camera */ #define FTDI_ATIK_ATK16HRC_PID 0xDF33 /* ATIK ATK-16HRC Colour Camera */ +#define FTDI_ATIK_ATK16IC_PID 0xDF35 /* ATIK ATK-16IC Grayscale Camera */ /* * Protego product ids @@ -534,6 +540,8 @@ #define OLIMEX_VID 0x15BA #define OLIMEX_ARM_USB_OCD_PID 0x0003 +/* www.elsterelectricity.com Elster Unicom III Optical Probe */ +#define FTDI_ELSTER_UNICOM_PID 0xE700 /* Product Id */ /* * The Mobility Lab (TML) @@ -556,6 +564,13 @@ /* + * FIC / OpenMoko, Inc. http://wiki.openmoko.org/wiki/Neo1973_Debug_Board_v3 + * Submitted by Harald Welte <laforge@openmoko.org> + */ +#define FIC_VID 0x1457 +#define FIC_NEO1973_DEBUG_PID 0x5118 + +/* * BmRequestType: 1100 0000b * bRequest: FTDI_E2_READ * wValue: 0 diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index f1c90cfe725..d74e43d6923 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1020,19 +1020,26 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp) if (!serial) return; - garmin_clear(garmin_data_p); + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) + garmin_clear(garmin_data_p); /* shutdown our urbs */ usb_kill_urb (port->read_urb); usb_kill_urb (port->write_urb); - if (noResponseFromAppLayer(garmin_data_p) || - ((garmin_data_p->flags & CLEAR_HALT_REQUIRED) != 0)) { - process_resetdev_request(port); - garmin_data_p->state = STATE_RESET; + if (!port->serial->disconnected) { + if (noResponseFromAppLayer(garmin_data_p) || + ((garmin_data_p->flags & CLEAR_HALT_REQUIRED) != 0)) { + process_resetdev_request(port); + garmin_data_p->state = STATE_RESET; + } else { + garmin_data_p->state = STATE_DISCONNECTED; + } } else { garmin_data_p->state = STATE_DISCONNECTED; } + mutex_unlock(&port->serial->disc_mutex); } diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 9eb4a65ee4d..97fa3c42843 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -175,6 +175,14 @@ int usb_serial_generic_resume(struct usb_serial *serial) struct usb_serial_port *port; int i, c = 0, r; +#ifdef CONFIG_PM + /* + * If this is an autoresume, don't submit URBs. + * They will be submitted in the open function instead. + */ + if (serial->dev->auto_pm) + return 0; +#endif for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; if (port->open_count && port->read_urb) { @@ -327,6 +335,7 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; unsigned char *data = urb->transfer_buffer; int status = urb->status; + unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); @@ -339,11 +348,11 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb) usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); /* Throttle the device if requested by tty */ - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); if (!(port->throttled = port->throttle_req)) /* Handle data and continue reading from device */ flush_and_resubmit_read_urb(port); - spin_unlock(&port->lock); + spin_unlock_irqrestore(&port->lock, flags); } EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index a5d2e115e16..3428ccc28da 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -959,7 +959,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) * * This function will block the close until one of the following: * 1. Response to our Chase comes from Edgeport - * 2. A timout of 10 seconds without activity has expired + * 2. A timeout of 10 seconds without activity has expired * (1K of Edgeport data @ 2400 baud ==> 4 sec to empty) * ************************************************************************/ @@ -999,7 +999,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port) return; } } else { - // Reset timout value back to 10 seconds + // Reset timeout value back to 10 seconds dbg("%s - Last %d, Current %d", __FUNCTION__, lastCredits, edge_port->txCredits); loop = 10; } @@ -1014,7 +1014,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port) * This function will block the close until one of the following: * 1. TX count are 0 * 2. The edgeport has stopped - * 3. A timout of 3 seconds without activity has expired + * 3. A timeout of 3 seconds without activity has expired * ************************************************************************/ static void block_until_tx_empty (struct edgeport_port *edge_port) @@ -1050,7 +1050,7 @@ static void block_until_tx_empty (struct edgeport_port *edge_port) return; } } else { - // Reset timout value back to seconds + // Reset timeout value back to seconds loop = 30; } } diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index b8670905bc3..cd3405953f7 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -34,6 +34,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> +#include <linux/mutex.h> #include <linux/serial.h> #include <linux/ioctl.h> #include <asm/uaccess.h> @@ -133,7 +134,7 @@ struct edgeport_serial { struct product_info product_info; u8 TI_I2C_Type; // Type of I2C in UMP u8 TiReadI2C; // Set to TRUE if we have read the I2c in Boot Mode - struct semaphore es_sem; + struct mutex es_lock; int num_ports_open; struct usb_serial *serial; }; @@ -1978,7 +1979,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) } /* set up the port settings */ - edge_set_termios (port, NULL); + edge_set_termios (port, port->tty->termios); /* open up the port */ @@ -2044,7 +2045,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) dbg ("ShadowMCR 0x%X", edge_port->shadow_mcr); edge_serial = edge_port->edge_serial; - if (down_interruptible(&edge_serial->es_sem)) + if (mutex_lock_interruptible(&edge_serial->es_lock)) return -ERESTARTSYS; if (edge_serial->num_ports_open == 0) { /* we are the first port to be opened, let's post the interrupt urb */ @@ -2052,7 +2053,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (!urb) { dev_err (&port->dev, "%s - no interrupt urb present, exiting\n", __FUNCTION__); status = -EINVAL; - goto up_es_sem; + goto release_es_lock; } urb->complete = edge_interrupt_callback; urb->context = edge_serial; @@ -2060,7 +2061,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) status = usb_submit_urb (urb, GFP_KERNEL); if (status) { dev_err (&port->dev, "%s - usb_submit_urb failed with value %d\n", __FUNCTION__, status); - goto up_es_sem; + goto release_es_lock; } } @@ -2092,13 +2093,13 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) dbg("%s - exited", __FUNCTION__); - goto up_es_sem; + goto release_es_lock; unlink_int_urb: if (edge_port->edge_serial->num_ports_open == 0) usb_kill_urb(port->serial->port[0]->interrupt_in_urb); -up_es_sem: - up(&edge_serial->es_sem); +release_es_lock: + mutex_unlock(&edge_serial->es_lock); return status; } @@ -2137,14 +2138,14 @@ static void edge_close (struct usb_serial_port *port, struct file *filp) 0, NULL, 0); - down(&edge_serial->es_sem); + mutex_lock(&edge_serial->es_lock); --edge_port->edge_serial->num_ports_open; if (edge_port->edge_serial->num_ports_open <= 0) { /* last port is now closed, let's shut down our interrupt urb */ usb_kill_urb(port->serial->port[0]->interrupt_in_urb); edge_port->edge_serial->num_ports_open = 0; } - up(&edge_serial->es_sem); + mutex_unlock(&edge_serial->es_lock); edge_port->close_pending = 0; dbg("%s - exited", __FUNCTION__); @@ -2393,11 +2394,6 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi dbg("%s - port %d", __FUNCTION__, edge_port->port->number); tty = edge_port->port->tty; - if ((!tty) || - (!tty->termios)) { - dbg("%s - no tty structures", __FUNCTION__); - return; - } config = kmalloc (sizeof (*config), GFP_KERNEL); if (!config) { @@ -2492,15 +2488,21 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi } } + tty->termios->c_cflag &= ~CMSPAR; + /* Round the baud rate */ baud = tty_get_baud_rate(tty); if (!baud) { /* pick a default, any default... */ baud = 9600; - } + } else + tty_encode_baud_rate(tty, baud, baud); + edge_port->baud_rate = baud; config->wBaudRate = (__u16)((461550L + baud/2) / baud); + /* FIXME: Recompute actual baud from divisor here */ + dbg ("%s - baud rate = %d, wBaudRate = %d", __FUNCTION__, baud, config->wBaudRate); dbg ("wBaudRate: %d", (int)(461550L / config->wBaudRate)); @@ -2538,19 +2540,12 @@ static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old struct tty_struct *tty = port->tty; unsigned int cflag; - if (!port->tty || !port->tty->termios) { - dbg ("%s - no tty or termios", __FUNCTION__); - return; - } - cflag = tty->termios->c_cflag; dbg("%s - clfag %08x iflag %08x", __FUNCTION__, tty->termios->c_cflag, tty->termios->c_iflag); - if (old_termios) { - dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__, - old_termios->c_cflag, old_termios->c_iflag); - } + dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__, + old_termios->c_cflag, old_termios->c_iflag); dbg("%s - port %d", __FUNCTION__, port->number); @@ -2743,7 +2738,7 @@ static int edge_startup (struct usb_serial *serial) dev_err(&serial->dev->dev, "%s - Out of memory\n", __FUNCTION__); return -ENOMEM; } - sema_init(&edge_serial->es_sem, 1); + mutex_init(&edge_serial->es_lock); edge_serial->serial = serial; usb_set_serial_data(serial, edge_serial); diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c new file mode 100644 index 00000000000..fde188e23ce --- /dev/null +++ b/drivers/usb/serial/iuu_phoenix.c @@ -0,0 +1,1217 @@ +/* + * Infinity Unlimited USB Phoenix driver + * + * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com) + * + * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * And tested with help of WB Electronics + * + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/spinlock.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include "iuu_phoenix.h" +#include <linux/random.h> + + +#ifdef CONFIG_USB_SERIAL_DEBUG +static int debug = 1; +#else +static int debug; +#endif + +/* + * Version Information + */ +#define DRIVER_VERSION "v0.5" +#define DRIVER_DESC "Infinity USB Unlimited Phoenix driver" + +static struct usb_device_id id_table[] = { + {USB_DEVICE(IUU_USB_VENDOR_ID, IUU_USB_PRODUCT_ID)}, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_driver iuu_driver = { + .name = "iuu_phoenix", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + +/* turbo parameter */ +static int boost = 100; +static int clockmode = 1; +static int cdmode = 1; +static int iuu_cardin; +static int iuu_cardout; +static int xmas; + +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 */ + u8 reset; /* if 1 reset is needed */ + int poll; /* number of poll */ + u8 *writebuf; /* buffer for writing to device */ + int writelen; /* num of byte to write to device */ + u8 *buf; /* used for initialize speed */ + u8 *dbgbuf; /* debug buffer */ + u8 len; +}; + + +static void iuu_free_buf(struct iuu_private *priv) +{ + kfree(priv->buf); + kfree(priv->dbgbuf); + kfree(priv->writebuf); +} + +static int iuu_alloc_buf(struct iuu_private *priv) +{ + priv->buf = kzalloc(256, GFP_KERNEL); + priv->dbgbuf = kzalloc(256, GFP_KERNEL); + priv->writebuf = kzalloc(256, GFP_KERNEL); + if (!priv->buf || !priv->dbgbuf || !priv->writebuf) { + iuu_free_buf(priv); + dbg("%s problem allocation buffer", __FUNCTION__); + return -ENOMEM; + } + dbg("%s - Privates buffers allocation success", __FUNCTION__); + return 0; +} + +static int iuu_startup(struct usb_serial *serial) +{ + struct iuu_private *priv; + priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL); + dbg("%s- priv allocation success", __FUNCTION__); + if (!priv) + return -ENOMEM; + if (iuu_alloc_buf(priv)) { + kfree(priv); + return -ENOMEM; + } + spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->delta_msr_wait); + usb_set_serial_port_data(serial->port[0], priv); + return 0; +} + +/* Shutdown function */ +static void iuu_shutdown(struct usb_serial *serial) +{ + struct usb_serial_port *port = serial->port[0]; + struct iuu_private *priv = usb_get_serial_port_data(port); + if (!port) + return; + + dbg("%s", __FUNCTION__); + + if (priv) { + iuu_free_buf(priv); + dbg("%s - I will free all", __FUNCTION__); + usb_set_serial_port_data(port, NULL); + + dbg("%s - priv is not anymore in port structure", __FUNCTION__); + kfree(priv); + + dbg("%s priv is now kfree", __FUNCTION__); + } +} + +static int iuu_tiocmset(struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear) +{ + struct iuu_private *priv = usb_get_serial_port_data(port); + struct tty_struct *tty; + tty = port->tty; + + dbg("%s (%d) msg : SET = 0x%04x, CLEAR = 0x%04x ", __FUNCTION__, + port->number, set, clear); + if (set & TIOCM_RTS) + priv->tiostatus = TIOCM_RTS; + + if (!(set & TIOCM_RTS) && priv->tiostatus == TIOCM_RTS) { + dbg("%s TIOCMSET RESET called !!!", __FUNCTION__); + priv->reset = 1; + return 0; + } + + return 0; +} + +/* This is used to provide a carrier detect mechanism + * When a card is present, the response is 0x00 + * When no card , the reader respond with TIOCM_CD + * This is known as CD autodetect mechanism + */ +static int iuu_tiocmget(struct usb_serial_port *port, struct file *file) +{ + struct iuu_private *priv = usb_get_serial_port_data(port); + return priv->tiostatus; +} + +static void iuu_rxcmd(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int result; + dbg("%s - enter", __FUNCTION__); + + if (urb->status) { + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + /* error stop all */ + return; + } + + + memset(port->write_urb->transfer_buffer, IUU_UART_RX, 1); + usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, 1, + read_rxcmd_callback, port); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); +} + +static int iuu_reset(struct usb_serial_port *port, u8 wt) +{ + struct iuu_private *priv = usb_get_serial_port_data(port); + int result; + char *buf_ptr = port->write_urb->transfer_buffer; + dbg("%s - enter", __FUNCTION__); + + /* Prepare the reset sequence */ + + *buf_ptr++ = IUU_RST_SET; + *buf_ptr++ = IUU_DELAY_MS; + *buf_ptr++ = wt; + *buf_ptr = IUU_RST_CLEAR; + + /* send the sequence */ + + usb_fill_bulk_urb(port->write_urb, + port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, 4, iuu_rxcmd, port); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + priv->reset = 0; + return result; +} + +/* Status Function + * Return value is + * 0x00 = no card + * 0x01 = smartcard + * 0x02 = sim card + */ +static void iuu_update_status_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct iuu_private *priv = usb_get_serial_port_data(port); + u8 *st; + dbg("%s - enter", __FUNCTION__); + + if (urb->status) { + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + /* error stop all */ + return; + } + + st = urb->transfer_buffer; + dbg("%s - enter", __FUNCTION__); + if (urb->actual_length == 1) { + switch (st[0]) { + case 0x1: + priv->tiostatus = iuu_cardout; + break; + case 0x0: + priv->tiostatus = iuu_cardin; + break; + default: + priv->tiostatus = iuu_cardin; + } + } + iuu_rxcmd(urb); +} + +static void iuu_status_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int result; + dbg("%s - enter", __FUNCTION__); + + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, 256, + iuu_update_status_callback, port); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); +} + +static int iuu_status(struct usb_serial_port *port) +{ + int result; + + dbg("%s - enter", __FUNCTION__); + + memset(port->write_urb->transfer_buffer, IUU_GET_STATE_REGISTER, 1); + usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, 1, + iuu_status_callback, port); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + return result; + +} + +static int bulk_immediate(struct usb_serial_port *port, u8 *buf, u8 count) +{ + int status; + struct usb_serial *serial = port->serial; + int actual = 0; + + dbg("%s - enter", __FUNCTION__); + + /* send the data out the bulk port */ + + status = + usb_bulk_msg(serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), buf, + count, &actual, HZ * 1); + + if (status != IUU_OPERATION_OK) { + dbg("%s - error = %2x", __FUNCTION__, status); + } else { + dbg("%s - write OK !", __FUNCTION__); + } + return status; +} + +static int read_immediate(struct usb_serial_port *port, u8 *buf, u8 count) +{ + int status; + struct usb_serial *serial = port->serial; + int actual = 0; + + dbg("%s - enter", __FUNCTION__); + + /* send the data out the bulk port */ + + status = + usb_bulk_msg(serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), buf, + count, &actual, HZ * 1); + + if (status != IUU_OPERATION_OK) { + dbg("%s - error = %2x", __FUNCTION__, status); + } else { + dbg("%s - read OK !", __FUNCTION__); + } + + return status; +} + +static int iuu_led(struct usb_serial_port *port, unsigned int R, + unsigned int G, unsigned int B, u8 f) +{ + int status; + u8 *buf; + buf = kmalloc(8, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + dbg("%s - enter", __FUNCTION__); + + buf[0] = IUU_SET_LED; + buf[1] = R & 0xFF; + buf[2] = (R >> 8) & 0xFF; + buf[3] = G & 0xFF; + buf[4] = (G >> 8) & 0xFF; + buf[5] = B & 0xFF; + buf[6] = (B >> 8) & 0xFF; + buf[7] = f; + status = bulk_immediate(port, buf, 8); + kfree(buf); + if (status != IUU_OPERATION_OK) + dbg("%s - led error status = %2x", __FUNCTION__, status); + else + dbg("%s - led OK !", __FUNCTION__); + return IUU_OPERATION_OK; +} + +static void iuu_rgbf_fill_buffer(u8 *buf, u8 r1, u8 r2, u8 g1, u8 g2, u8 b1, + u8 b2, u8 freq) +{ + *buf++ = IUU_SET_LED; + *buf++ = r1; + *buf++ = r2; + *buf++ = g1; + *buf++ = g2; + *buf++ = b1; + *buf++ = b2; + *buf = freq; +} + +static void iuu_led_activity_on(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int result; + char *buf_ptr = port->write_urb->transfer_buffer; + *buf_ptr++ = IUU_SET_LED; + if (xmas == 1) { + get_random_bytes(buf_ptr, 6); + *(buf_ptr+7) = 1; + } else { + iuu_rgbf_fill_buffer(buf_ptr, 255, 255, 0, 0, 0, 0, 255); + } + + usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, 8 , + iuu_rxcmd, port); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); +} + +static void iuu_led_activity_off(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int result; + char *buf_ptr = port->write_urb->transfer_buffer; + if (xmas == 1) { + iuu_rxcmd(urb); + return; + } else { + *buf_ptr++ = IUU_SET_LED; + iuu_rgbf_fill_buffer(buf_ptr, 0, 0, 255, 255, 0, 0, 255); + } + usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, 8 , + iuu_rxcmd, port); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); +} + + + +static int iuu_clk(struct usb_serial_port *port, int dwFrq) +{ + int status; + struct iuu_private *priv = usb_get_serial_port_data(port); + int Count = 0; + u8 FrqGenAdr = 0x69; + u8 DIV = 0; /* 8bit */ + u8 XDRV = 0; /* 8bit */ + u8 PUMP = 0; /* 3bit */ + u8 PBmsb = 0; /* 2bit */ + u8 PBlsb = 0; /* 8bit */ + u8 PO = 0; /* 1bit */ + u8 Q = 0; /* 7bit */ + /* 24bit = 3bytes */ + unsigned int P = 0; + unsigned int P2 = 0; + int frq = (int)dwFrq; + + dbg("%s - enter", __FUNCTION__); + + if (frq == 0) { + priv->buf[Count++] = IUU_UART_WRITE_I2C; + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x09; + priv->buf[Count++] = 0x00; + + status = bulk_immediate(port, (u8 *) priv->buf, Count); + if (status != 0) { + dbg("%s - write error ", __FUNCTION__); + return status; + } + } else if (frq == 3579000) { + DIV = 100; + P = 1193; + Q = 40; + XDRV = 0; + } else if (frq == 3680000) { + DIV = 105; + P = 161; + Q = 5; + XDRV = 0; + } else if (frq == 6000000) { + DIV = 66; + P = 66; + Q = 2; + XDRV = 0x28; + } else { + unsigned int result = 0; + unsigned int tmp = 0; + unsigned int check; + unsigned int check2; + char found = 0x00; + unsigned int lQ = 2; + unsigned int lP = 2055; + unsigned int lDiv = 4; + + for (lQ = 2; lQ <= 47 && !found; lQ++) + for (lP = 2055; lP >= 8 && !found; lP--) + for (lDiv = 4; lDiv <= 127 && !found; lDiv++) { + tmp = (12000000 / lDiv) * (lP / lQ); + if (abs((int)(tmp - frq)) < + abs((int)(frq - result))) { + check2 = (12000000 / lQ); + if (check2 < 250000) + continue; + check = (12000000 / lQ) * lP; + if (check > 400000000) + continue; + if (check < 100000000) + continue; + if (lDiv < 4 || lDiv > 127) + continue; + result = tmp; + P = lP; + DIV = lDiv; + Q = lQ; + if (result == frq) + found = 0x01; + } + } + } + P2 = ((P - PO) / 2) - 4; + DIV = DIV; + PUMP = 0x04; + PBmsb = (P2 >> 8 & 0x03); + PBlsb = P2 & 0xFF; + PO = (P >> 10) & 0x01; + Q = Q - 2; + + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x09; + priv->buf[Count++] = 0x20; /* Adr = 0x09 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x0C; + priv->buf[Count++] = DIV; /* Adr = 0x0C */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x12; + priv->buf[Count++] = XDRV; /* Adr = 0x12 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x13; + priv->buf[Count++] = 0x6B; /* Adr = 0x13 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x40; + priv->buf[Count++] = (0xC0 | ((PUMP & 0x07) << 2)) | + (PBmsb & 0x03); /* Adr = 0x40 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x41; + priv->buf[Count++] = PBlsb; /* Adr = 0x41 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x42; + priv->buf[Count++] = Q | (((PO & 0x01) << 7)); /* Adr = 0x42 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x44; + priv->buf[Count++] = (char)0xFF; /* Adr = 0x44 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x45; + priv->buf[Count++] = (char)0xFE; /* Adr = 0x45 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x46; + priv->buf[Count++] = 0x7F; /* Adr = 0x46 */ + priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */ + priv->buf[Count++] = FrqGenAdr << 1; + priv->buf[Count++] = 0x47; + priv->buf[Count++] = (char)0x84; /* Adr = 0x47 */ + + status = bulk_immediate(port, (u8 *) priv->buf, Count); + if (status != IUU_OPERATION_OK) + dbg("%s - write error ", __FUNCTION__); + return status; +} + +static int iuu_uart_flush(struct usb_serial_port *port) +{ + int i; + int status; + u8 rxcmd = IUU_UART_RX; + struct iuu_private *priv = usb_get_serial_port_data(port); + + dbg("%s - enter", __FUNCTION__); + + if (iuu_led(port, 0xF000, 0, 0, 0xFF) < 0) + return -EIO; + + for (i = 0; i < 2; i++) { + status = bulk_immediate(port, &rxcmd, 1); + if (status != IUU_OPERATION_OK) { + dbg("%s - uart_flush_write error", __FUNCTION__); + return status; + } + + status = read_immediate(port, &priv->len, 1); + if (status != IUU_OPERATION_OK) { + dbg("%s - uart_flush_read error", __FUNCTION__); + return status; + } + + if (priv->len > 0) { + dbg("%s - uart_flush datalen is : %i ", __FUNCTION__, + priv->len); + status = read_immediate(port, priv->buf, priv->len); + if (status != IUU_OPERATION_OK) { + dbg("%s - uart_flush_read error", __FUNCTION__); + return status; + } + } + } + dbg("%s - uart_flush_read OK!", __FUNCTION__); + iuu_led(port, 0, 0xF000, 0, 0xFF); + return status; +} + +static void read_buf_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + + if (urb->status) { + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + if (urb->status == -EPROTO) { + /* reschedule needed */ + } + return; + } + + dbg("%s - %i chars to write", __FUNCTION__, urb->actual_length); + tty = port->tty; + if (data == NULL) + dbg("%s - data is NULL !!!", __FUNCTION__); + if (tty && urb->actual_length && data) { + tty_insert_flip_string(tty, data, urb->actual_length); + tty_flip_buffer_push(tty); + } + iuu_led_activity_on(urb); +} + +static int iuu_bulk_write(struct usb_serial_port *port) +{ + struct iuu_private *priv = usb_get_serial_port_data(port); + unsigned int flags; + int result; + int i; + char *buf_ptr = port->write_urb->transfer_buffer; + dbg("%s - enter", __FUNCTION__); + + *buf_ptr++ = IUU_UART_ESC; + *buf_ptr++ = IUU_UART_TX; + *buf_ptr++ = priv->writelen; + + memcpy(buf_ptr, priv->writebuf, + priv->writelen); + if (debug == 1) { + for (i = 0; i < priv->writelen; i++) + sprintf(priv->dbgbuf + i*2 , + "%02X", priv->writebuf[i]); + priv->dbgbuf[priv->writelen+i*2] = 0; + dbg("%s - writing %i chars : %s", __FUNCTION__, + priv->writelen, priv->dbgbuf); + } + usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, priv->writelen + 3, + iuu_rxcmd, port); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + spin_lock_irqsave(&priv->lock, flags); + priv->writelen = 0; + spin_unlock_irqrestore(&priv->lock, flags); + usb_serial_port_softint(port); + return result; +} + +static int iuu_read_buf(struct usb_serial_port *port, int len) +{ + int result; + dbg("%s - enter", __FUNCTION__); + + usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, len, + read_buf_callback, port); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + return result; +} + +static void iuu_uart_read_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct iuu_private *priv = usb_get_serial_port_data(port); + unsigned int flags; + int status; + int error = 0; + int len = 0; + unsigned char *data = urb->transfer_buffer; + priv->poll++; + + dbg("%s - enter", __FUNCTION__); + + if (urb->status) { + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + /* error stop all */ + return; + } + if (data == NULL) + dbg("%s - data is NULL !!!", __FUNCTION__); + + if (urb->actual_length == 1 && data != NULL) + len = (int) data[0]; + + if (urb->actual_length > 1) { + dbg("%s - urb->actual_length = %i", __FUNCTION__, + urb->actual_length); + error = 1; + return; + } + /* if len > 0 call readbuf */ + + if (len > 0 && error == 0) { + dbg("%s - call read buf - len to read is %i ", + __FUNCTION__, len); + status = iuu_read_buf(port, len); + return; + } + /* need to update status ? */ + if (priv->poll > 99) { + status = iuu_status(port); + priv->poll = 0; + return; + } + + /* reset waiting ? */ + + if (priv->reset == 1) { + status = iuu_reset(port, 0xC); + return; + } + /* Writebuf is waiting */ + spin_lock_irqsave(&priv->lock, flags); + if (priv->writelen > 0) { + spin_unlock_irqrestore(&priv->lock, flags); + status = iuu_bulk_write(port); + return; + } + spin_unlock_irqrestore(&priv->lock, flags); + /* if nothing to write call again rxcmd */ + dbg("%s - rxcmd recall", __FUNCTION__); + iuu_led_activity_off(urb); + return; +} + +static int iuu_uart_write(struct usb_serial_port *port, const u8 *buf, + int count) +{ + struct iuu_private *priv = usb_get_serial_port_data(port); + unsigned int flags; + dbg("%s - enter", __FUNCTION__); + + if (count > 256) + return -ENOMEM; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->writelen > 0) { + /* buffer already filled but not commited */ + spin_unlock_irqrestore(&priv->lock, flags); + return (0); + } + /* fill the buffer */ + memcpy(priv->writebuf, buf, count); + priv->writelen = count; + spin_unlock_irqrestore(&priv->lock, flags); + + return (count); +} + +static void read_rxcmd_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int result; + dbg("%s - enter", __FUNCTION__); + + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + + if (urb->status) { + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + /* error stop all */ + return; + } + + usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, 256, + iuu_uart_read_callback, port); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + dbg("%s - submit result = %d", __FUNCTION__, result); + return; +} + +static int iuu_uart_on(struct usb_serial_port *port) +{ + int status; + u8 *buf; + + buf = kmalloc(sizeof(u8) * 4, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + buf[0] = IUU_UART_ENABLE; + buf[1] = (u8) ((IUU_BAUD_9600 >> 8) & 0x00FF); + buf[2] = (u8) (0x00FF & IUU_BAUD_9600); + buf[3] = (u8) (0x0F0 & IUU_TWO_STOP_BITS) | (0x07 & IUU_PARITY_EVEN); + + status = bulk_immediate(port, buf, 4); + if (status != IUU_OPERATION_OK) { + dbg("%s - uart_on error", __FUNCTION__); + goto uart_enable_failed; + } + /* iuu_reset() the card after iuu_uart_on() */ + status = iuu_uart_flush(port); + if (status != IUU_OPERATION_OK) + dbg("%s - uart_flush error", __FUNCTION__); +uart_enable_failed: + kfree(buf); + return status; +} + +/* Diables the IUU UART (a.k.a. the Phoenix voiderface) */ +static int iuu_uart_off(struct usb_serial_port *port) +{ + int status; + u8 *buf; + buf = kmalloc(1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + buf[0] = IUU_UART_DISABLE; + + status = bulk_immediate(port, buf, 1); + if (status != IUU_OPERATION_OK) + dbg("%s - uart_off error", __FUNCTION__); + + kfree(buf); + return status; +} + +static int iuu_uart_baud(struct usb_serial_port *port, u32 baud, + u32 *actual, u8 parity) +{ + int status; + u8 *dataout; + u8 DataCount = 0; + u8 T1Frekvens = 0; + u8 T1reload = 0; + unsigned int T1FrekvensHZ = 0; + + dataout = kmalloc(sizeof(u8) * 5, GFP_KERNEL); + + if (!dataout) + return -ENOMEM; + + if (baud < 1200 || baud > 230400) { + kfree(dataout); + return IUU_INVALID_PARAMETER; + } + if (baud > 977) { + T1Frekvens = 3; + T1FrekvensHZ = 500000; + } + + if (baud > 3906) { + T1Frekvens = 2; + T1FrekvensHZ = 2000000; + } + + if (baud > 11718) { + T1Frekvens = 1; + T1FrekvensHZ = 6000000; + } + + if (baud > 46875) { + T1Frekvens = 0; + T1FrekvensHZ = 24000000; + } + + T1reload = 256 - (u8) (T1FrekvensHZ / (baud * 2)); + + /* magic number here: ENTER_FIRMWARE_UPDATE; */ + dataout[DataCount++] = IUU_UART_ESC; + /* magic number here: CHANGE_BAUD; */ + dataout[DataCount++] = IUU_UART_CHANGE; + dataout[DataCount++] = T1Frekvens; + dataout[DataCount++] = T1reload; + + *actual = (T1FrekvensHZ / (256 - T1reload)) / 2; + + switch (parity & 0x0F) { + case IUU_PARITY_NONE: + dataout[DataCount++] = 0x00; + break; + case IUU_PARITY_EVEN: + dataout[DataCount++] = 0x01; + break; + case IUU_PARITY_ODD: + dataout[DataCount++] = 0x02; + break; + case IUU_PARITY_MARK: + dataout[DataCount++] = 0x03; + break; + case IUU_PARITY_SPACE: + dataout[DataCount++] = 0x04; + break; + default: + kfree(dataout); + return IUU_INVALID_PARAMETER; + break; + } + + switch (parity & 0xF0) { + case IUU_ONE_STOP_BIT: + dataout[DataCount - 1] |= IUU_ONE_STOP_BIT; + break; + + case IUU_TWO_STOP_BITS: + dataout[DataCount - 1] |= IUU_TWO_STOP_BITS; + break; + default: + kfree(dataout); + return IUU_INVALID_PARAMETER; + break; + } + + status = bulk_immediate(port, dataout, DataCount); + if (status != IUU_OPERATION_OK) + dbg("%s - uart_off error", __FUNCTION__); + kfree(dataout); + return status; +} + +static int set_control_lines(struct usb_device *dev, u8 value) +{ + return 0; +} + +static void iuu_close(struct usb_serial_port *port, struct file *filp) +{ + /* 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) + return; + + dbg("%s - port %d", __FUNCTION__, port->number); + + iuu_uart_off(port); + if (serial->dev) { + if (port->tty) { + c_cflag = port->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", __FUNCTION__); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); + msleep(1000); + /* wait one second to free all buffers */ + iuu_led(port, 0, 0, 0xF000, 0xFF); + msleep(1000); + usb_reset_device(port->serial->dev); + } +} + +static int iuu_open(struct usb_serial_port *port, struct file *filp) +{ + struct usb_serial *serial = port->serial; + u8 *buf; + int result; + u32 actual; + unsigned long flags; + struct iuu_private *priv = usb_get_serial_port_data(port); + + dbg("%s - port %d", __FUNCTION__, port->number); + usb_clear_halt(serial->dev, port->write_urb->pipe); + usb_clear_halt(serial->dev, port->read_urb->pipe); + + buf = kmalloc(10, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + /* fixup the endpoint buffer size */ + kfree(port->bulk_out_buffer); + port->bulk_out_buffer = kmalloc(512, GFP_KERNEL); + port->bulk_out_size = 512; + kfree(port->bulk_in_buffer); + port->bulk_in_buffer = kmalloc(512, GFP_KERNEL); + port->bulk_in_size = 512; + + if (!port->bulk_out_buffer || !port->bulk_in_buffer) { + kfree(port->bulk_out_buffer); + kfree(port->bulk_in_buffer); + kfree(buf); + return -ENOMEM; + } + + usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + port->bulk_out_buffer, 512, + NULL, NULL); + + + usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->bulk_in_buffer, 512, + NULL, NULL); + + /* set the termios structure */ + spin_lock_irqsave(&priv->lock, flags); + if (!priv->termios_initialized) { + *(port->tty->termios) = tty_std_termios; + port->tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 + | TIOCM_CTS | CSTOPB | PARENB; + port->tty->termios->c_lflag = 0; + port->tty->termios->c_oflag = 0; + port->tty->termios->c_iflag = 0; + priv->termios_initialized = 1; + port->tty->low_latency = 1; + priv->poll = 0; + } + spin_unlock_irqrestore(&priv->lock, flags); + + /* initialize writebuf */ +#define FISH(a, b, c, d) do { \ + result = usb_control_msg(port->serial->dev, \ + usb_rcvctrlpipe(port->serial->dev, 0), \ + b, a, c, d, buf, 1, 1000); \ + dbg("0x%x:0x%x:0x%x:0x%x %d - %x", a, b, c, d, result, \ + buf[0]); } while (0); + +#define SOUP(a, b, c, d) do { \ + result = usb_control_msg(port->serial->dev, \ + usb_sndctrlpipe(port->serial->dev, 0), \ + b, a, c, d, NULL, 0, 1000); \ + dbg("0x%x:0x%x:0x%x:0x%x %d", a, b, c, d, result); } while (0) + + /* This is not UART related but IUU USB driver related or something */ + /* like that. Basically no IUU will accept any commands from the USB */ + /* host unless it has received the following message */ + /* sprintf(buf ,"%c%c%c%c",0x03,0x02,0x02,0x0); */ + + SOUP(0x03, 0x02, 0x02, 0x0); + kfree(buf); + iuu_led(port, 0xF000, 0xF000, 0, 0xFF); + iuu_uart_on(port); + if (boost < 100) + boost = 100; + switch (clockmode) { + case 2: /* 3.680 Mhz */ + iuu_clk(port, IUU_CLK_3680000 * boost / 100); + result = + iuu_uart_baud(port, 9600 * boost / 100, &actual, + IUU_PARITY_EVEN); + break; + case 3: /* 6.00 Mhz */ + iuu_clk(port, IUU_CLK_6000000 * boost / 100); + result = + iuu_uart_baud(port, 16457 * boost / 100, &actual, + IUU_PARITY_EVEN); + break; + default: /* 3.579 Mhz */ + iuu_clk(port, IUU_CLK_3579000 * boost / 100); + result = + iuu_uart_baud(port, 9600 * boost / 100, &actual, + IUU_PARITY_EVEN); + } + + /* set the cardin cardout signals */ + switch (cdmode) { + case 0: + iuu_cardin = 0; + iuu_cardout = 0; + break; + case 1: + iuu_cardin = TIOCM_CD; + iuu_cardout = 0; + break; + case 2: + iuu_cardin = 0; + iuu_cardout = TIOCM_CD; + break; + case 3: + iuu_cardin = TIOCM_DSR; + iuu_cardout = 0; + break; + case 4: + iuu_cardin = 0; + iuu_cardout = TIOCM_DSR; + break; + case 5: + iuu_cardin = TIOCM_CTS; + iuu_cardout = 0; + break; + case 6: + iuu_cardin = 0; + iuu_cardout = TIOCM_CTS; + break; + case 7: + iuu_cardin = TIOCM_RNG; + iuu_cardout = 0; + break; + case 8: + iuu_cardin = 0; + iuu_cardout = TIOCM_RNG; + } + + iuu_uart_flush(port); + + dbg("%s - initialization done", __FUNCTION__); + + memset(port->write_urb->transfer_buffer, IUU_UART_RX, 1); + usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, 1, + read_rxcmd_callback, port); + result = usb_submit_urb(port->write_urb, GFP_KERNEL); + + if (result) { + dev_err(&port->dev, "%s - failed submitting read urb," + " error %d\n", __FUNCTION__, result); + iuu_close(port, NULL); + return -EPROTO; + } else { + dbg("%s - rxcmd OK", __FUNCTION__); + } + return result; +} + +static struct usb_serial_driver iuu_device = { + .driver = { + .owner = THIS_MODULE, + .name = "iuu_phoenix", + }, + .id_table = id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = iuu_open, + .close = iuu_close, + .write = iuu_uart_write, + .read_bulk_callback = iuu_uart_read_callback, + .tiocmget = iuu_tiocmget, + .tiocmset = iuu_tiocmset, + .attach = iuu_startup, + .shutdown = iuu_shutdown, +}; + +static int __init iuu_init(void) +{ + int retval; + retval = usb_serial_register(&iuu_device); + if (retval) + goto failed_usb_serial_register; + retval = usb_register(&iuu_driver); + if (retval) + goto failed_usb_register; + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; +failed_usb_register: + usb_serial_deregister(&iuu_device); +failed_usb_serial_register: + return retval; +} + +static void __exit iuu_exit(void) +{ + usb_deregister(&iuu_driver); + usb_serial_deregister(&iuu_device); +} + +module_init(iuu_init); +module_exit(iuu_exit); + +MODULE_AUTHOR("Alain Degreffe eczema@ecze.com"); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +MODULE_VERSION(DRIVER_VERSION); +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + +module_param(xmas, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(xmas, "xmas color enabled or not"); + +module_param(boost, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(boost, "overclock boost percent 100 to 500"); + +module_param(clockmode, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(clockmode, "1=3Mhz579,2=3Mhz680,3=6Mhz"); + +module_param(cdmode, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(cdmode, "Card detect mode 0=none, 1=CD, 2=!CD, 3=DSR, " + "4=!DSR, 5=CTS, 6=!CTS, 7=RING, 8=!RING"); diff --git a/drivers/usb/serial/iuu_phoenix.h b/drivers/usb/serial/iuu_phoenix.h new file mode 100644 index 00000000000..b82630a3b8f --- /dev/null +++ b/drivers/usb/serial/iuu_phoenix.h @@ -0,0 +1,122 @@ +/* + * Infinity Unlimited USB Phoenix driver + * + * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com) + * + * + * Original code taken from iuutool ( Copyright (C) 2006 Juan Carlos Borrás ) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * And tested with help of WB Electronics + * + */ + +#define IUU_USB_VENDOR_ID 0x104f +#define IUU_USB_PRODUCT_ID 0x0004 +#define IUU_USB_OP_TIMEOUT 0x0200 + +/* Programmer commands */ + +#define IUU_NO_OPERATION 0x00 +#define IUU_GET_FIRMWARE_VERSION 0x01 +#define IUU_GET_PRODUCT_NAME 0x02 +#define IUU_GET_STATE_REGISTER 0x03 +#define IUU_SET_LED 0x04 +#define IUU_WAIT_MUS 0x05 +#define IUU_WAIT_MS 0x06 +#define IUU_GET_LOADER_VERSION 0x50 +#define IUU_RST_SET 0x52 +#define IUU_RST_CLEAR 0x53 +#define IUU_SET_VCC 0x59 +#define IUU_UART_ENABLE 0x49 +#define IUU_UART_DISABLE 0x4A +#define IUU_UART_WRITE_I2C 0x4C +#define IUU_UART_ESC 0x5E +#define IUU_UART_TRAP 0x54 +#define IUU_UART_TRAP_BREAK 0x5B +#define IUU_UART_RX 0x56 +#define IUU_AVR_ON 0x21 +#define IUU_AVR_OFF 0x22 +#define IUU_AVR_1CLK 0x23 +#define IUU_AVR_RESET 0x24 +#define IUU_AVR_RESET_PC 0x25 +#define IUU_AVR_INC_PC 0x26 +#define IUU_AVR_INCN_PC 0x27 +#define IUU_AVR_PREAD 0x29 +#define IUU_AVR_PREADN 0x2A +#define IUU_AVR_PWRITE 0x28 +#define IUU_AVR_DREAD 0x2C +#define IUU_AVR_DREADN 0x2D +#define IUU_AVR_DWRITE 0x2B +#define IUU_AVR_PWRITEN 0x2E +#define IUU_EEPROM_ON 0x37 +#define IUU_EEPROM_OFF 0x38 +#define IUU_EEPROM_WRITE 0x39 +#define IUU_EEPROM_WRITEX 0x3A +#define IUU_EEPROM_WRITE8 0x3B +#define IUU_EEPROM_WRITE16 0x3C +#define IUU_EEPROM_WRITEX32 0x3D +#define IUU_EEPROM_WRITEX64 0x3E +#define IUU_EEPROM_READ 0x3F +#define IUU_EEPROM_READX 0x40 +#define IUU_EEPROM_BREAD 0x41 +#define IUU_EEPROM_BREADX 0x42 +#define IUU_PIC_CMD 0x0A +#define IUU_PIC_CMD_LOAD 0x0B +#define IUU_PIC_CMD_READ 0x0C +#define IUU_PIC_ON 0x0D +#define IUU_PIC_OFF 0x0E +#define IUU_PIC_RESET 0x16 +#define IUU_PIC_INC_PC 0x0F +#define IUU_PIC_INCN_PC 0x10 +#define IUU_PIC_PWRITE 0x11 +#define IUU_PIC_PREAD 0x12 +#define IUU_PIC_PREADN 0x13 +#define IUU_PIC_DWRITE 0x14 +#define IUU_PIC_DREAD 0x15 +#define IUU_UART_NOP 0x00 +#define IUU_UART_CHANGE 0x02 +#define IUU_UART_TX 0x04 +#define IUU_DELAY_MS 0x06 + +#define IUU_OPERATION_OK 0x00 +#define IUU_DEVICE_NOT_FOUND 0x01 +#define IUU_INVALID_HANDLE 0x02 +#define IUU_INVALID_PARAMETER 0x03 +#define IUU_INVALID_voidERFACE 0x04 +#define IUU_INVALID_REQUEST_LENGTH 0x05 +#define IUU_UART_NOT_ENABLED 0x06 +#define IUU_WRITE_ERROR 0x07 +#define IUU_READ_ERROR 0x08 +#define IUU_TX_ERROR 0x09 +#define IUU_RX_ERROR 0x0A + +#define IUU_PARITY_NONE 0x00 +#define IUU_PARITY_EVEN 0x01 +#define IUU_PARITY_ODD 0x02 +#define IUU_PARITY_MARK 0x03 +#define IUU_PARITY_SPACE 0x04 +#define IUU_SC_INSERTED 0x01 +#define IUU_VERIFY_ERROR 0x02 +#define IUU_SIM_INSERTED 0x04 +#define IUU_TWO_STOP_BITS 0x00 +#define IUU_ONE_STOP_BIT 0x20 +#define IUU_BAUD_2400 0x0398 +#define IUU_BAUD_9600 0x0298 +#define IUU_BAUD_19200 0x0164 +#define IUU_BAUD_28800 0x0198 +#define IUU_BAUD_38400 0x01B2 +#define IUU_BAUD_57600 0x0030 +#define IUU_BAUD_115200 0x0098 +#define IUU_CLK_3579000 3579000 +#define IUU_CLK_3680000 3680000 +#define IUU_CLK_6000000 6000000 +#define IUU_FULLCARD_IN 0x01 +#define IUU_DEV_ERROR 0x02 +#define IUU_MINICARD_IN 0x04 +#define IUU_VCC_5V 0x00 +#define IUU_VCC_3V 0x01 diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 6bfdba6a213..ea7bba69f4d 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -447,7 +447,7 @@ static void usa26_indat_callback(struct urb *urb) port = (struct usb_serial_port *) urb->context; tty = port->tty; - if (urb->actual_length) { + if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { /* no errors on individual bytes, only possible overrun err*/ @@ -838,7 +838,7 @@ static void usa49_indat_callback(struct urb *urb) port = (struct usb_serial_port *) urb->context; tty = port->tty; - if (urb->actual_length) { + if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { /* no error on any byte */ @@ -1227,8 +1227,8 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); d_details = p_priv->device_details; - - dbg("%s - port%d.", __FUNCTION__, port->number); + + dbg("%s - port%d.", __FUNCTION__, port->number); /* Set some sane defaults */ p_priv->rts_state = 1; @@ -1249,7 +1249,7 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) urb->dev = serial->dev; /* make sure endpoint data toggle is synchronized with the device */ - + usb_clear_halt(urb->dev, urb->pipe); if ((err = usb_submit_urb(urb, GFP_KERNEL)) != 0) { @@ -1265,7 +1265,7 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */ } - /* get the terminal config for the setup message now so we don't + /* get the terminal config for the setup message now so we don't * need to send 2 of them */ cflag = port->tty->termios->c_cflag; @@ -1274,7 +1274,7 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) /* Baud rate calculation takes baud rate as an integer so other rates can be generated if desired. */ baud_rate = tty_get_baud_rate(port->tty); - /* If no match or invalid, leave as default */ + /* If no match or invalid, leave as default */ if (baud_rate >= 0 && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index be9ac20a8f1..b1fa5a376e9 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -303,7 +303,7 @@ static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port) } -static int keyspan_pda_setbaud (struct usb_serial *serial, int baud) +static speed_t keyspan_pda_setbaud (struct usb_serial *serial, speed_t baud) { int rc; int bindex; @@ -319,7 +319,9 @@ static int keyspan_pda_setbaud (struct usb_serial *serial, int baud) case 38400: bindex = 7; break; case 57600: bindex = 8; break; case 115200: bindex = 9; break; - default: return -EINVAL; + default: + bindex = 5; /* Default to 9600 */ + baud = 9600; } /* rather than figure out how to sleep while waiting for this @@ -334,7 +336,9 @@ static int keyspan_pda_setbaud (struct usb_serial *serial, int baud) NULL, /* &data */ 0, /* size */ 2000); /* timeout */ - return(rc); + if (rc < 0) + return 0; + return baud; } @@ -366,7 +370,7 @@ static void keyspan_pda_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; - unsigned int cflag = port->tty->termios->c_cflag; + speed_t speed; /* cflag specifies lots of stuff: number of stop bits, parity, number of data bits, baud. What can the device actually handle?: @@ -388,22 +392,18 @@ static void keyspan_pda_set_termios (struct usb_serial_port *port, For now, just do baud. */ - switch (cflag & CBAUD) { - /* we could support more values here, just need to calculate - the necessary divisors in the firmware. <asm/termbits.h> - has the Bnnn constants. */ - case B110: keyspan_pda_setbaud(serial, 110); break; - case B300: keyspan_pda_setbaud(serial, 300); break; - case B1200: keyspan_pda_setbaud(serial, 1200); break; - case B2400: keyspan_pda_setbaud(serial, 2400); break; - case B4800: keyspan_pda_setbaud(serial, 4800); break; - case B9600: keyspan_pda_setbaud(serial, 9600); break; - case B19200: keyspan_pda_setbaud(serial, 19200); break; - case B38400: keyspan_pda_setbaud(serial, 38400); break; - case B57600: keyspan_pda_setbaud(serial, 57600); break; - case B115200: keyspan_pda_setbaud(serial, 115200); break; - default: dbg("can't handle requested baud rate"); break; + speed = tty_get_baud_rate(port->tty); + speed = keyspan_pda_setbaud(serial, speed); + + if (speed == 0) { + dbg("can't handle requested baud rate"); + /* It hasn't changed so.. */ + speed = tty_termios_baud_rate(old_termios); } + /* Only speed can change so copy the old h/w parameters + then encode the new speed */ + tty_termios_copy_hw(port->tty->termios, old_termios); + tty_encode_baud_rate(port->tty, speed, speed); } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 90e3216abd1..55736df7d2f 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -461,17 +461,21 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp) dbg("%s port %d", __FUNCTION__, port->number); - /* send READ_OFF */ - rc = usb_control_msg (port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - KL5KUSB105A_SIO_CONFIGURE, - USB_TYPE_VENDOR | USB_DIR_OUT, - KL5KUSB105A_SIO_CONFIGURE_READ_OFF, - 0, /* index */ - NULL, 0, - KLSI_TIMEOUT); - if (rc < 0) - err("Disabling read failed (error = %d)", rc); + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) { + /* send READ_OFF */ + rc = usb_control_msg (port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + KL5KUSB105A_SIO_CONFIGURE, + USB_TYPE_VENDOR | USB_DIR_OUT, + KL5KUSB105A_SIO_CONFIGURE_READ_OFF, + 0, /* index */ + NULL, 0, + KLSI_TIMEOUT); + if (rc < 0) + err("Disabling read failed (error = %d)", rc); + } + mutex_unlock(&port->serial->disc_mutex); /* shutdown our bulk reads and writes */ usb_kill_urb(port->write_urb); diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index aee450246bf..17b3baead4a 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -114,6 +114,7 @@ static struct usb_serial_driver kobil_device = { .usb_driver = &kobil_driver, .id_table = id_table, .num_interrupt_in = NUM_DONT_CARE, + .num_interrupt_out = NUM_DONT_CARE, .num_bulk_in = 0, .num_bulk_out = 0, .num_ports = 1, diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 0dc99f75bb0..fc1cea4aba1 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -182,10 +182,11 @@ struct mct_u232_private { /* * Later day 2.6.0-test kernels have new baud rates like B230400 which * we do not know how to support. We ignore them for the moment. - * XXX Rate-limit the error message, it's user triggerable. */ -static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value) +static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value, speed_t *result) { + *result = value; + if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID || le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) { switch (value) { @@ -200,11 +201,13 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value case 57600: return 0x0b; case 115200: return 0x0c; default: - err("MCT USB-RS232: unsupported baudrate request 0x%x," - " using default of B9600", value); + *result = 9600; return 0x08; } } else { + /* FIXME: Can we use any divider - should we do + divider = 115200/value; + real baud = 115200/divider */ switch (value) { case 300: break; case 600: break; @@ -217,9 +220,8 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value case 57600: break; case 115200: break; default: - err("MCT USB-RS232: unsupported baudrate request 0x%x," - " using default of B9600", value); value = 9600; + *result = 9600; } return 115200/value; } @@ -232,16 +234,19 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_p int rc; unsigned char zero_byte = 0; unsigned char cts_enable_byte = 0; + speed_t speed; - divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value)); + divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value, &speed)); rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCT_U232_SET_BAUD_RATE_REQUEST, MCT_U232_SET_REQUEST_TYPE, 0, 0, &divisor, MCT_U232_SET_BAUD_RATE_SIZE, WDR_TIMEOUT); - if (rc < 0) + if (rc < 0) /*FIXME: What value speed results */ err("Set BAUD RATE %d failed (error = %d)", value, rc); + else + tty_encode_baud_rate(port->tty, speed, speed); dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", value, divisor); /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which @@ -482,21 +487,22 @@ error: static void mct_u232_close (struct usb_serial_port *port, struct file *filp) { unsigned int c_cflag; - unsigned long flags; unsigned int control_state; struct mct_u232_private *priv = usb_get_serial_port_data(port); dbg("%s port %d", __FUNCTION__, port->number); if (port->tty) { c_cflag = port->tty->termios->c_cflag; - if (c_cflag & HUPCL) { - /* drop DTR and RTS */ - spin_lock_irqsave(&priv->lock, flags); - priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); - mct_u232_set_modem_ctrl(port->serial, control_state); + mutex_lock(&port->serial->disc_mutex); + if (c_cflag & HUPCL && !port->serial->disconnected) { + /* drop DTR and RTS */ + spin_lock_irq(&priv->lock); + 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); } @@ -608,7 +614,8 @@ static void mct_u232_set_termios (struct usb_serial_port *port, { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); - unsigned int cflag = port->tty->termios->c_cflag; + struct ktermios *termios = port->tty->termios; + unsigned int cflag = termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned long flags; unsigned int control_state; @@ -670,6 +677,8 @@ static void mct_u232_set_termios (struct usb_serial_port *port, break; } + termios->c_cflag &= ~CMSPAR; + /* set the number of stop bits */ last_lcr |= (cflag & CSTOPB) ? MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h index aae10c8174d..07b6bec31dc 100644 --- a/drivers/usb/serial/mct_u232.h +++ b/drivers/usb/serial/mct_u232.h @@ -79,7 +79,7 @@ * and "Intel solution". They are the regular MCT and "Sitecom" for us. * This is pointless to document in the header, see the code for the bits. */ -static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value); +static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value, speed_t *result); /* * Line Control Register (LCR) diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index e02c198016b..40f3a018880 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -564,22 +564,25 @@ static void mos7720_close(struct usb_serial_port *port, struct file *filp) } /* While closing port, shutdown all bulk read, write * - * and interrupt read if they exists */ - if (serial->dev) { - dbg("Shutdown bulk write"); - usb_kill_urb(port->write_urb); - dbg("Shutdown bulk read"); - usb_kill_urb(port->read_urb); + * and interrupt read if they exists, otherwise nop */ + dbg("Shutdown bulk write"); + usb_kill_urb(port->write_urb); + dbg("Shutdown bulk read"); + usb_kill_urb(port->read_urb); + + mutex_lock(&serial->disc_mutex); + /* these commands must not be issued if the device has + * been disconnected */ + if (!serial->disconnected) { + data = 0x00; + send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, + 0x04, &data); + + data = 0x00; + send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, + 0x01, &data); } - - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, - 0x04, &data); - - data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, - 0x01, &data); - + mutex_unlock(&serial->disc_mutex); mos7720_port->open = 0; dbg("Leaving %s", __FUNCTION__); @@ -1040,11 +1043,6 @@ static void change_port_settings(struct moschip_port *mos7720_port, tty = mos7720_port->port->tty; - if ((!tty) || (!tty->termios)) { - dbg("%s - no tty structures", __FUNCTION__); - return; - } - dbg("%s: Entering ..........", __FUNCTION__); lData = UART_LCR_WLEN8; @@ -1175,7 +1173,10 @@ static void change_port_settings(struct moschip_port *mos7720_port, dbg("%s - baud rate = %d", __FUNCTION__, baud); status = send_cmd_write_baud_rate(mos7720_port, baud); - + /* FIXME: needs to write actual resulting baud back not just + blindly do so */ + if (cflag & CBAUD) + tty_encode_baud_rate(tty, baud, baud); /* Enable Interrupts */ data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, UART_IER, &data); @@ -1214,10 +1215,6 @@ static void mos7720_set_termios(struct usb_serial_port *port, tty = port->tty; - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty or termios", __FUNCTION__); - return; - } if (!mos7720_port->open) { dbg("%s - port not opened", __FUNCTION__); @@ -1228,19 +1225,13 @@ static void mos7720_set_termios(struct usb_serial_port *port, cflag = tty->termios->c_cflag; - if (!cflag) { - printk("%s %s\n",__FUNCTION__,"cflag is NULL"); - return; - } - - dbg("%s - clfag %08x iflag %08x", __FUNCTION__, + dbg("%s - cflag %08x iflag %08x", __FUNCTION__, tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag)); - if (old_termios) - dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__, - old_termios->c_cflag, - RELEVANT_IFLAG(old_termios->c_iflag)); + dbg("%s - old cflag %08x old iflag %08x", __FUNCTION__, + old_termios->c_cflag, + RELEVANT_IFLAG(old_termios->c_iflag)); dbg("%s - port %d", __FUNCTION__, port->number); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index a5ced7e08cb..869ecd374cb 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1133,7 +1133,7 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) * This function will block the close until one of the following: * 1. TX count are 0 * 2. The mos7840 has stopped - * 3. A timout of 3 seconds without activity has expired + * 3. A timeout of 3 seconds without activity has expired * ************************************************************************/ static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) @@ -1161,7 +1161,7 @@ static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) dbg("%s - TIMEOUT", __FUNCTION__); return; } else { - /* Reset timout value back to seconds */ + /* Reset timeout value back to seconds */ wait = 30; } } @@ -1275,7 +1275,7 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) * * This function will block the close until one of the following: * 1. Response to our Chase comes from mos7840 - * 2. A timout of 10 seconds without activity has expired + * 2. A timeout of 10 seconds without activity has expired * (1K of mos7840 data @ 2400 baud ==> 4 sec to empty) * ************************************************************************/ @@ -1304,7 +1304,7 @@ static void mos7840_block_until_chase_response(struct moschip_port dbg("%s - TIMEOUT", __FUNCTION__); return; } else { - /* Reset timout value back to seconds */ + /* Reset timeout value back to seconds */ wait = 10; } } @@ -2711,7 +2711,7 @@ static int mos7840_startup(struct usb_serial *serial) status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data); if (status < 0) { dbg("Writing ZLP_REG5 failed status-0x%x\n", status); - return -1; + goto error; } else dbg("ZLP_REG5 Writing success status%d\n", status); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 4590124cf88..5e8bf1bc1e5 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -158,8 +158,8 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_NETWORK) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, - { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) }, - { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1100) }, /* Novatel Merlin XS620/S640 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1110) }, /* Novatel Merlin S620 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1120) }, /* Novatel Merlin EX720 */ @@ -180,6 +180,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(DELL_VENDOR_ID, 0x8117) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO ExpressCard == Novatel Merlin XV620 CDMA/EV-DO */ { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard == Novatel Merlin XU870 HSDPA/3G */ { USB_DEVICE(DELL_VENDOR_ID, 0x8128) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite E720 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, 0x8136) }, /* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */ { USB_DEVICE(DELL_VENDOR_ID, 0x8137) }, /* Dell Wireless HSDPA 5520 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) }, @@ -640,7 +641,10 @@ static void option_close(struct usb_serial_port *port, struct file *filp) portdata->dtr_state = 0; if (serial->dev) { - option_send_setup(port); + mutex_lock(&serial->disc_mutex); + if (!serial->disconnected) + option_send_setup(port); + mutex_unlock(&serial->disc_mutex); /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index eea226ae37b..a3847d6c946 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -79,7 +79,7 @@ static int debug; #define PL2303_BUF_SIZE 1024 #define PL2303_TMP_BUF_SIZE 1024 -struct pl2303_buf { +struct oti6858_buf { unsigned int buf_size; char *buf_buf; char *buf_get; @@ -161,14 +161,14 @@ static int oti6858_startup(struct usb_serial *serial); static void oti6858_shutdown(struct usb_serial *serial); /* functions operating on buffers */ -static struct pl2303_buf *pl2303_buf_alloc(unsigned int size); -static void pl2303_buf_free(struct pl2303_buf *pb); -static void pl2303_buf_clear(struct pl2303_buf *pb); -static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb); -static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb); -static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, +static struct oti6858_buf *oti6858_buf_alloc(unsigned int size); +static void oti6858_buf_free(struct oti6858_buf *pb); +static void oti6858_buf_clear(struct oti6858_buf *pb); +static unsigned int oti6858_buf_data_avail(struct oti6858_buf *pb); +static unsigned int oti6858_buf_space_avail(struct oti6858_buf *pb); +static unsigned int oti6858_buf_put(struct oti6858_buf *pb, const char *buf, unsigned int count); -static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, +static unsigned int oti6858_buf_get(struct oti6858_buf *pb, char *buf, unsigned int count); @@ -203,7 +203,7 @@ static struct usb_serial_driver oti6858_device = { struct oti6858_private { spinlock_t lock; - struct pl2303_buf *buf; + struct oti6858_buf *buf; struct oti6858_control_pkt status; struct { @@ -316,7 +316,7 @@ void send_data(struct work_struct *work) } priv->flags.write_urb_in_use = 1; - count = pl2303_buf_data_avail(priv->buf); + count = oti6858_buf_data_avail(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); if (count > port->bulk_out_size) count = port->bulk_out_size; @@ -345,7 +345,7 @@ void send_data(struct work_struct *work) } spin_lock_irqsave(&priv->lock, flags); - pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, count); + oti6858_buf_get(priv->buf, port->write_urb->transfer_buffer, count); spin_unlock_irqrestore(&priv->lock, flags); port->write_urb->transfer_buffer_length = count; @@ -370,7 +370,7 @@ static int oti6858_startup(struct usb_serial *serial) priv = kzalloc(sizeof(struct oti6858_private), GFP_KERNEL); if (!priv) break; - priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); + priv->buf = oti6858_buf_alloc(PL2303_BUF_SIZE); if (priv->buf == NULL) { kfree(priv); break; @@ -391,7 +391,7 @@ static int oti6858_startup(struct usb_serial *serial) for (--i; i >= 0; --i) { priv = usb_get_serial_port_data(serial->port[i]); - pl2303_buf_free(priv->buf); + oti6858_buf_free(priv->buf); kfree(priv); usb_set_serial_port_data(serial->port[i], NULL); } @@ -410,7 +410,7 @@ static int oti6858_write(struct usb_serial_port *port, return count; spin_lock_irqsave(&priv->lock, flags); - count = pl2303_buf_put(priv->buf, buf, count); + count = oti6858_buf_put(priv->buf, buf, count); spin_unlock_irqrestore(&priv->lock, flags); return count; @@ -425,7 +425,7 @@ static int oti6858_write_room(struct usb_serial_port *port) dbg("%s(port = %d)", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags); - room = pl2303_buf_space_avail(priv->buf); + room = oti6858_buf_space_avail(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); return room; @@ -440,7 +440,7 @@ static int oti6858_chars_in_buffer(struct usb_serial_port *port) dbg("%s(port = %d)", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags); - chars = pl2303_buf_data_avail(priv->buf); + chars = oti6858_buf_data_avail(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); return chars; @@ -458,7 +458,7 @@ static void oti6858_set_termios(struct usb_serial_port *port, dbg("%s(port = %d)", __FUNCTION__, port->number); - if ((!port->tty) || (!port->tty->termios)) { + if (!port->tty || !port->tty->termios) { dbg("%s(): no tty structures", __FUNCTION__); return; } @@ -468,6 +468,8 @@ static void oti6858_set_termios(struct usb_serial_port *port, *(port->tty->termios) = tty_std_termios; port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; priv->flags.termios_initialized = 1; + port->tty->termios->c_ispeed = 38400; + port->tty->termios->c_ospeed = 38400; } spin_unlock_irqrestore(&priv->lock, flags); @@ -504,19 +506,14 @@ static void oti6858_set_termios(struct usb_serial_port *port, br = tty_get_baud_rate(port->tty); if (br == 0) { divisor = 0; - } else if (br <= OTI6858_MAX_BAUD_RATE) { + } else { int real_br; + br = min(br, OTI6858_MAX_BAUD_RATE); divisor = (96000000 + 8 * br) / (16 * br); real_br = 96000000 / (16 * divisor); - if ((((real_br - br) * 100 + br - 1) / br) > 2) { - dbg("%s(): baud rate %d is invalid", __FUNCTION__, br); - return; - } divisor = cpu_to_le16(divisor); - } else { - dbg("%s(): baud rate %d is too high", __FUNCTION__, br); - return; + tty_encode_baud_rate(port->tty, real_br, real_br); } frame_fmt &= ~FMT_STOP_BITS_MASK; @@ -650,9 +647,9 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) dbg("%s(): entering wait loop", __FUNCTION__); for (;;) { set_current_state(TASK_INTERRUPTIBLE); - if (pl2303_buf_data_avail(priv->buf) == 0 + if (oti6858_buf_data_avail(priv->buf) == 0 || timeout == 0 || signal_pending(current) - || !usb_get_intfdata(port->serial->interface)) /* disconnect */ + || port->serial->disconnected) break; spin_unlock_irqrestore(&priv->lock, flags); timeout = schedule_timeout(timeout); @@ -663,7 +660,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) dbg("%s(): after wait loop", __FUNCTION__); /* clear out any remaining data in the buffer */ - pl2303_buf_clear(priv->buf); + oti6858_buf_clear(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); /* wait for characters to drain from the device */ @@ -831,21 +828,6 @@ static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, return -EFAULT; return oti6858_tiocmset(port, NULL, 0, x); - case TIOCGSERIAL: - if (copy_to_user(user_arg, port->tty->termios, - sizeof(struct ktermios))) { - return -EFAULT; - } - return 0; - - case TIOCSSERIAL: - if (copy_from_user(port->tty->termios, user_arg, - sizeof(struct ktermios))) { - return -EFAULT; - } - oti6858_set_termios(port, NULL); - return 0; - case TIOCMIWAIT: dbg("%s(): TIOCMIWAIT", __FUNCTION__); return wait_modem_info(port, arg); @@ -887,7 +869,7 @@ static void oti6858_shutdown(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { priv = usb_get_serial_port_data(serial->port[i]); if (priv) { - pl2303_buf_free(priv->buf); + oti6858_buf_free(priv->buf); kfree(priv); usb_set_serial_port_data(serial->port[i], NULL); } @@ -987,7 +969,7 @@ static void oti6858_read_int_callback(struct urb *urb) spin_lock_irqsave(&priv->lock, flags); if (priv->flags.write_urb_in_use == 0 - && pl2303_buf_data_avail(priv->buf) != 0) { + && oti6858_buf_data_avail(priv->buf) != 0) { schedule_delayed_work(&priv->delayed_write_work,0); resubmit = 0; } @@ -1015,9 +997,8 @@ static void oti6858_read_bulk_callback(struct urb *urb) struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; unsigned long flags; - int i, result; int status = urb->status; - char tty_flag; + int result; dbg("%s(port = %d, status = %d)", __FUNCTION__, port->number, status); @@ -1045,27 +1026,9 @@ static void oti6858_read_bulk_callback(struct urb *urb) return; } - // get tty_flag from status - tty_flag = TTY_NORMAL; - -/* FIXME: probably, errors will be signalled using interrupt pipe! */ -/* - // break takes precedence over parity, - // which takes precedence over framing errors - if (status & UART_BREAK_ERROR ) - tty_flag = TTY_BREAK; - else if (status & UART_PARITY_ERROR) - tty_flag = TTY_PARITY; - else if (status & UART_FRAME_ERROR) - tty_flag = TTY_FRAME; - dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag); -*/ - tty = port->tty; if (tty != NULL && urb->actual_length > 0) { - tty_buffer_request_room(tty, urb->actual_length); - for (i = 0; i < urb->actual_length; ++i) - tty_insert_flip_char(tty, data[i], tty_flag); + tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } @@ -1133,18 +1096,18 @@ static void oti6858_write_bulk_callback(struct urb *urb) /* - * pl2303_buf_alloc + * oti6858_buf_alloc * * Allocate a circular buffer and all associated memory. */ -static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) +static struct oti6858_buf *oti6858_buf_alloc(unsigned int size) { - struct pl2303_buf *pb; + struct oti6858_buf *pb; if (size == 0) return NULL; - pb = kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); + pb = kmalloc(sizeof(struct oti6858_buf), GFP_KERNEL); if (pb == NULL) return NULL; @@ -1161,11 +1124,11 @@ static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) } /* - * pl2303_buf_free + * oti6858_buf_free * * Free the buffer and all associated memory. */ -static void pl2303_buf_free(struct pl2303_buf *pb) +static void oti6858_buf_free(struct oti6858_buf *pb) { if (pb) { kfree(pb->buf_buf); @@ -1174,11 +1137,11 @@ static void pl2303_buf_free(struct pl2303_buf *pb) } /* - * pl2303_buf_clear + * oti6858_buf_clear * * Clear out all data in the circular buffer. */ -static void pl2303_buf_clear(struct pl2303_buf *pb) +static void oti6858_buf_clear(struct oti6858_buf *pb) { if (pb != NULL) { /* equivalent to a get of all data available */ @@ -1187,12 +1150,12 @@ static void pl2303_buf_clear(struct pl2303_buf *pb) } /* - * pl2303_buf_data_avail + * oti6858_buf_data_avail * * Return the number of bytes of data available in the circular * buffer. */ -static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) +static unsigned int oti6858_buf_data_avail(struct oti6858_buf *pb) { if (pb == NULL) return 0; @@ -1200,12 +1163,12 @@ static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) } /* - * pl2303_buf_space_avail + * oti6858_buf_space_avail * * Return the number of bytes of space available in the circular * buffer. */ -static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) +static unsigned int oti6858_buf_space_avail(struct oti6858_buf *pb) { if (pb == NULL) return 0; @@ -1213,14 +1176,14 @@ static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) } /* - * pl2303_buf_put + * oti6858_buf_put * * Copy data data from a user buffer and put it into the circular buffer. * Restrict to the amount of space available. * * Return the number of bytes copied. */ -static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, +static unsigned int oti6858_buf_put(struct oti6858_buf *pb, const char *buf, unsigned int count) { unsigned int len; @@ -1228,7 +1191,7 @@ static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, if (pb == NULL) return 0; - len = pl2303_buf_space_avail(pb); + len = oti6858_buf_space_avail(pb); if (count > len) count = len; @@ -1252,14 +1215,14 @@ static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, } /* - * pl2303_buf_get + * oti6858_buf_get * * Get data from the circular buffer and copy to the given buffer. * Restrict to the amount of data available. * * Return the number of bytes copied. */ -static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, +static unsigned int oti6858_buf_get(struct oti6858_buf *pb, char *buf, unsigned int count) { unsigned int len; @@ -1267,7 +1230,7 @@ static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, if (pb == NULL) return 0; - len = pl2303_buf_data_avail(pb); + len = oti6858_buf_data_avail(pb); if (count > len) count = len; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 2cd3f1d4b68..ae3ec1a6400 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -65,6 +65,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) }, { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, + { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, @@ -84,8 +85,10 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) }, { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, - { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID) }, { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) }, + { USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) }, + { USB_DEVICE(HL340_VENDOR_ID, HL340_PRODUCT_ID) }, + { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -96,7 +99,10 @@ static struct usb_driver pl2303_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .suspend = usb_serial_suspend, + .resume = usb_serial_resume, .no_dynamic_id = 1, + .supports_autosuspend = 1, }; #define SET_LINE_REQUEST_TYPE 0x21 @@ -309,12 +315,39 @@ static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, return count; } +static int pl2303_vendor_read(__u16 value, __u16 index, + struct usb_serial *serial, unsigned char *buf) +{ + int res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE, + value, index, buf, 1, 100); + dbg("0x%x:0x%x:0x%x:0x%x %d - %x", VENDOR_READ_REQUEST_TYPE, + VENDOR_READ_REQUEST, value, index, res, buf[0]); + return res; +} + +static int pl2303_vendor_write(__u16 value, __u16 index, + struct usb_serial *serial) +{ + int res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE, + value, index, NULL, 0, 100); + dbg("0x%x:0x%x:0x%x:0x%x %d", VENDOR_WRITE_REQUEST_TYPE, + VENDOR_WRITE_REQUEST, value, index, res); + return res; +} + static int pl2303_startup(struct usb_serial *serial) { struct pl2303_private *priv; enum pl2303_type type = type_0; + unsigned char *buf; int i; + buf = kmalloc(10, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + if (serial->dev->descriptor.bDeviceClass == 0x02) type = type_0; else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) @@ -339,9 +372,27 @@ static int pl2303_startup(struct usb_serial *serial) priv->type = type; usb_set_serial_port_data(serial->port[i], priv); } + + pl2303_vendor_read(0x8484, 0, serial, buf); + pl2303_vendor_write(0x0404, 0, serial); + pl2303_vendor_read(0x8484, 0, serial, buf); + pl2303_vendor_read(0x8383, 0, serial, buf); + pl2303_vendor_read(0x8484, 0, serial, buf); + pl2303_vendor_write(0x0404, 1, serial); + pl2303_vendor_read(0x8484, 0, serial, buf); + pl2303_vendor_read(0x8383, 0, serial, buf); + pl2303_vendor_write(0, 1, serial); + pl2303_vendor_write(1, 0, serial); + if (type == HX) + pl2303_vendor_write(2, 0x44, serial); + else + pl2303_vendor_write(2, 0x24, serial); + + kfree(buf); return 0; cleanup: + kfree(buf); for (--i; i>=0; --i) { priv = usb_get_serial_port_data(serial->port[i]); pl2303_buf_free(priv->buf); @@ -482,6 +533,13 @@ static void pl2303_set_termios(struct usb_serial_port *port, } spin_unlock_irqrestore(&priv->lock, flags); + /* The PL2303 is reported to lose bytes if you change + serial settings even to the same values as before. Thus + we actually need to filter in this specific case */ + + if (!tty_termios_hw_change(port->tty->termios, old_termios)) + return; + cflag = port->tty->termios->c_cflag; buf = kzalloc(7, GFP_KERNEL); @@ -574,24 +632,12 @@ static void pl2303_set_termios(struct usb_serial_port *port, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (cflag & CRTSCTS) { - __u16 index; if (priv->type == HX) - index = 0x61; + pl2303_vendor_write(0x0, 0x61, serial); else - index = 0x41; - i = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - VENDOR_WRITE_REQUEST, - VENDOR_WRITE_REQUEST_TYPE, - 0x0, index, NULL, 0, 100); - dbg("0x40:0x1:0x0:0x%x %d", index, i); + pl2303_vendor_write(0x0, 0x41, serial); } else { - i = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - VENDOR_WRITE_REQUEST, - VENDOR_WRITE_REQUEST_TYPE, - 0x0, 0x0, NULL, 0, 100); - dbg ("0x40:0x1:0x0:0x0 %d", i); + pl2303_vendor_write(0x0, 0x0, serial); } /* FIXME: Need to read back resulting baud rate */ @@ -621,7 +667,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) set_current_state(TASK_INTERRUPTIBLE); if (pl2303_buf_data_avail(priv->buf) == 0 || timeout == 0 || signal_pending(current) || - !usb_get_intfdata(port->serial->interface)) /* disconnect */ + port->serial->disconnected) break; spin_unlock_irqrestore(&priv->lock, flags); timeout = schedule_timeout(timeout); @@ -670,7 +716,6 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) struct ktermios tmp_termios; struct usb_serial *serial = port->serial; struct pl2303_private *priv = usb_get_serial_port_data(port); - unsigned char *buf; int result; dbg("%s - port %d", __FUNCTION__, port->number); @@ -678,45 +723,12 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) if (priv->type != HX) { usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); - } - - buf = kmalloc(10, GFP_KERNEL); - if (buf==NULL) - return -ENOMEM; - -#define FISH(a,b,c,d) \ - result=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0), \ - b, a, c, d, buf, 1, 100); \ - dbg("0x%x:0x%x:0x%x:0x%x %d - %x",a,b,c,d,result,buf[0]); - -#define SOUP(a,b,c,d) \ - result=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \ - b, a, c, d, NULL, 0, 100); \ - dbg("0x%x:0x%x:0x%x:0x%x %d",a,b,c,d,result); - - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 0); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0); - - if (priv->type == HX) { - /* HX chip */ - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44); - /* reset upstream data pipes */ - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 8, 0); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 9, 0); } else { - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x24); + /* reset upstream data pipes */ + pl2303_vendor_write(8, 0, serial); + pl2303_vendor_write(9, 0, serial); } - kfree(buf); - /* Setup termios */ if (port->tty) { pl2303_set_termios(port, &tmp_termios); diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index ed603e3decd..237a41f6638 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -35,6 +35,7 @@ #define RATOC_VENDOR_ID 0x0584 #define RATOC_PRODUCT_ID 0xb000 +#define RATOC_PRODUCT_ID_USB60F 0xb020 #define TRIPP_VENDOR_ID 0x2478 #define TRIPP_PRODUCT_ID 0x2008 @@ -96,11 +97,18 @@ #define ALCOR_VENDOR_ID 0x058F #define ALCOR_PRODUCT_ID 0x9720 -/* Huawei E620 UMTS/HSDPA card (ID: 12d1:1001) */ -#define HUAWEI_VENDOR_ID 0x12d1 -#define HUAWEI_PRODUCT_ID 0x1001 - /* Willcom WS002IN Data Driver (by NetIndex Inc.) */ #define WS002IN_VENDOR_ID 0x11f6 #define WS002IN_PRODUCT_ID 0x2001 +/* Corega CG-USBRS232R Serial Adapter */ +#define COREGA_VENDOR_ID 0x07aa +#define COREGA_PRODUCT_ID 0x002a + +/* HL HL-340 (ID: 4348:5523) */ +#define HL340_VENDOR_ID 0x4348 +#define HL340_PRODUCT_ID 0x5523 + +/* Y.C. Cable U.S.A., Inc - USB to RS-232 */ +#define YCCABLE_VENDOR_ID 0x05ad +#define YCCABLE_PRODUCT_ID 0x0fba diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 833f6e1e372..4c925e3e8a6 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -1,7 +1,7 @@ /* USB Driver for Sierra Wireless - Copyright (C) 2006, 2007 Kevin Lloyd <linux@sierrawireless.com> + Copyright (C) 2006, 2007, 2008 Kevin Lloyd <linux@sierrawireless.com> IMPORTANT DISCLAIMER: This driver is not commercially supported by Sierra Wireless. Use at your own risk. @@ -14,7 +14,7 @@ Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org> */ -#define DRIVER_VERSION "v.1.2.5b" +#define DRIVER_VERSION "v.1.2.7" #define DRIVER_AUTHOR "Kevin Lloyd <linux@sierrawireless.com>" #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems" @@ -26,10 +26,12 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> +#include <linux/usb/ch9.h> +#define SWIMS_USB_REQUEST_SetPower 0x00 +#define SWIMS_USB_REQUEST_SetNmea 0x07 #define SWIMS_USB_REQUEST_SetMode 0x0B -#define SWIMS_USB_REQUEST_TYPE_SetMode 0x40 -#define SWIMS_USB_INDEX_SetMode 0x0000 +#define SWIMS_USB_REQUEST_TYPE_VSC_SET 0x40 #define SWIMS_SET_MODE_Modem 0x0001 /* per port private data */ @@ -38,6 +40,8 @@ #define IN_BUFLEN 4096 static int debug; +static int nmea; +static int truinstall = 1; enum devicetype { DEVICE_3_PORT = 0, @@ -50,48 +54,96 @@ static int sierra_set_power_state(struct usb_device *udev, __u16 swiState) int result; dev_dbg(&udev->dev, "%s", "SET POWER STATE\n"); result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x00, /* __u8 request */ - 0x40, /* __u8 request type */ - swiState, /* __u16 value */ - 0, /* __u16 index */ - NULL, /* void *data */ - 0, /* __u16 size */ - USB_CTRL_SET_TIMEOUT); /* int timeout */ + SWIMS_USB_REQUEST_SetPower, /* __u8 request */ + SWIMS_USB_REQUEST_TYPE_VSC_SET, /* __u8 request type */ + swiState, /* __u16 value */ + 0, /* __u16 index */ + NULL, /* void *data */ + 0, /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ return result; } -static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSocMode) +static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode) { int result; dev_dbg(&udev->dev, "%s", "DEVICE MODE SWITCH\n"); result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), SWIMS_USB_REQUEST_SetMode, /* __u8 request */ - SWIMS_USB_REQUEST_TYPE_SetMode, /* __u8 request type */ - eSocMode, /* __u16 value */ - SWIMS_USB_INDEX_SetMode, /* __u16 index */ + SWIMS_USB_REQUEST_TYPE_VSC_SET, /* __u8 request type */ + eSWocMode, /* __u16 value */ + 0x0000, /* __u16 index */ NULL, /* void *data */ 0, /* __u16 size */ USB_CTRL_SET_TIMEOUT); /* int timeout */ return result; } -static int sierra_probe(struct usb_interface *iface, - const struct usb_device_id *id) +static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable) +{ + int result; + dev_dbg(&udev->dev, "%s", "NMEA Enable sent\n"); + result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SWIMS_USB_REQUEST_SetNmea, /* __u8 request */ + SWIMS_USB_REQUEST_TYPE_VSC_SET, /* __u8 request type */ + enable, /* __u16 value */ + 0x0000, /* __u16 index */ + NULL, /* void *data */ + 0, /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + return result; +} + +static int sierra_calc_num_ports(struct usb_serial *serial) { int result; + int *num_ports = usb_get_serial_data(serial); + + result = *num_ports; + + if (result) { + kfree(num_ports); + usb_set_serial_data(serial, NULL); + } + + return result; +} + +static int sierra_probe(struct usb_serial *serial, + const struct usb_device_id *id) +{ + int result = 0; struct usb_device *udev; + int *num_ports; + u8 ifnum; + + num_ports = kmalloc(sizeof(*num_ports), GFP_KERNEL); + if (!num_ports) + return -ENOMEM; - udev = usb_get_dev(interface_to_usbdev(iface)); + ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; + udev = serial->dev; /* Check if in installer mode */ - if (id->driver_info == DEVICE_INSTALLER) { - dev_dbg(&udev->dev, "%s", "FOUND DEVICE(SW)\n"); + if (truinstall && id->driver_info == DEVICE_INSTALLER) { + dev_dbg(&udev->dev, "%s", "FOUND TRU-INSTALL DEVICE(SW)\n"); result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); - /*We do not want to bind to the device when in installer mode*/ + /* Don't bind to the device when in installer mode */ + kfree(num_ports); return -EIO; - } + } else if (id->driver_info == DEVICE_1_PORT) + *num_ports = 1; + else if (ifnum == 0x99) + *num_ports = 0; + else + *num_ports = 3; + /* + * save off our num_ports info so that we can use it in the + * calc_num_ports callback + */ + usb_set_serial_data(serial, (void *)num_ports); - return usb_serial_probe(iface, id); + return result; } static struct usb_device_id id_table [] = { @@ -100,14 +152,17 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x0f30, 0x1b1d) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ + { USB_DEVICE(0x1199, 0x0220) }, /* Sierra Wireless MC5725 */ { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ { USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless USB Dongle 595U */ + { USB_DEVICE(0x1199, 0x0023) }, /* Sierra Wireless AirCard */ { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 & AC 875U */ + { USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 (Thinkpad internal) */ { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ { USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780*/ { USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781*/ @@ -115,54 +170,29 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x6851) }, /* Sierra Wireless AirCard 881 */ { USB_DEVICE(0x1199, 0x6852) }, /* Sierra Wireless AirCard 880 E */ { USB_DEVICE(0x1199, 0x6853) }, /* Sierra Wireless AirCard 881 E */ + { USB_DEVICE(0x1199, 0x6855) }, /* Sierra Wireless AirCard 880 U */ + { USB_DEVICE(0x1199, 0x6856) }, /* Sierra Wireless AirCard 881 U */ + + { USB_DEVICE(0x1199, 0x6468) }, /* Sierra Wireless MP3G - EVDO */ + { USB_DEVICE(0x1199, 0x6469) }, /* Sierra Wireless MP3G - UMTS/HSPA */ { USB_DEVICE(0x1199, 0x0112), .driver_info = DEVICE_1_PORT }, /* Sierra Wireless AirCard 580 */ { USB_DEVICE(0x0F3D, 0x0112), .driver_info = DEVICE_1_PORT }, /* Airprime/Sierra PC 5220 */ + { USB_DEVICE(0x05C6, 0x6613), .driver_info = DEVICE_1_PORT }, /* Onda H600/ZTE MF330 */ { USB_DEVICE(0x1199, 0x0FFF), .driver_info = DEVICE_INSTALLER}, { } }; MODULE_DEVICE_TABLE(usb, id_table); -static struct usb_device_id id_table_1port [] = { - { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */ - { USB_DEVICE(0x0F3D, 0x0112) }, /* AirPrime/Sierra PC 5220 */ - { } -}; - -static struct usb_device_id id_table_3port [] = { - { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ - { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ - { USB_DEVICE(0x0f30, 0x1b1d) }, /* Sierra Wireless MC5720 */ - { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ - { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ - { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ - { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ - { USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless USB Dongle 595U*/ - - { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ - { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ - { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ - { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 & AC 875U */ - { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ - { USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780*/ - { USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781*/ - { USB_DEVICE(0x1199, 0x6850) }, /* Sierra Wireless AirCard 880 */ - { USB_DEVICE(0x1199, 0x6851) }, /* Sierra Wireless AirCard 881 */ - { USB_DEVICE(0x1199, 0x6852) }, /* Sierra Wireless AirCard 880E */ - { USB_DEVICE(0x1199, 0x6853) }, /* Sierra Wireless AirCard 881E */ - { } -}; - static struct usb_driver sierra_driver = { .name = "sierra", - .probe = sierra_probe, + .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, .no_dynamic_id = 1, }; - struct sierra_port_private { spinlock_t lock; /* lock the structure */ int outstanding_urbs; /* number of out urbs in flight */ @@ -184,6 +214,7 @@ static int sierra_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; + __u16 interface = 0; dbg("%s", __FUNCTION__); @@ -196,9 +227,18 @@ static int sierra_send_setup(struct usb_serial_port *port) if (portdata->rts_state) val |= 0x02; + /* Determine which port is targeted */ + 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,0,NULL,0,USB_CTRL_SET_TIMEOUT); + 0x22, 0x21, val, interface, + NULL, 0, USB_CTRL_SET_TIMEOUT); } return 0; @@ -557,7 +597,10 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) portdata->dtr_state = 0; if (serial->dev) { - sierra_send_setup(port); + mutex_lock(&serial->disc_mutex); + if (!serial->disconnected) + sierra_send_setup(port); + mutex_unlock(&serial->disc_mutex); /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) @@ -579,9 +622,13 @@ static int sierra_startup(struct usb_serial *serial) dbg("%s", __FUNCTION__); - /*Set Device mode to D0 */ + /* Set Device mode to D0 */ sierra_set_power_state(serial->dev, 0x0000); + /* Check NMEA and set */ + if (nmea) + sierra_vsc_set_nmea(serial->dev, 1); + /* Now setup per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; @@ -642,47 +689,19 @@ static void sierra_shutdown(struct usb_serial *serial) } } -static struct usb_serial_driver sierra_1port_device = { +static struct usb_serial_driver sierra_device = { .driver = { .owner = THIS_MODULE, .name = "sierra1", }, - .description = "Sierra USB modem (1 port)", - .id_table = id_table_1port, + .description = "Sierra USB modem", + .id_table = id_table, .usb_driver = &sierra_driver, .num_interrupt_in = NUM_DONT_CARE, - .num_bulk_in = 1, - .num_bulk_out = 1, - .num_ports = 1, - .open = sierra_open, - .close = sierra_close, - .write = sierra_write, - .write_room = sierra_write_room, - .chars_in_buffer = sierra_chars_in_buffer, - .throttle = sierra_rx_throttle, - .unthrottle = sierra_rx_unthrottle, - .ioctl = sierra_ioctl, - .set_termios = sierra_set_termios, - .break_ctl = sierra_break_ctl, - .tiocmget = sierra_tiocmget, - .tiocmset = sierra_tiocmset, - .attach = sierra_startup, - .shutdown = sierra_shutdown, - .read_int_callback = sierra_instat_callback, -}; - -static struct usb_serial_driver sierra_3port_device = { - .driver = { - .owner = THIS_MODULE, - .name = "sierra3", - }, - .description = "Sierra USB modem (3 port)", - .id_table = id_table_3port, - .usb_driver = &sierra_driver, - .num_interrupt_in = NUM_DONT_CARE, - .num_bulk_in = 3, - .num_bulk_out = 3, - .num_ports = 3, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .calc_num_ports = sierra_calc_num_ports, + .probe = sierra_probe, .open = sierra_open, .close = sierra_close, .write = sierra_write, @@ -704,12 +723,9 @@ static struct usb_serial_driver sierra_3port_device = { static int __init sierra_init(void) { int retval; - retval = usb_serial_register(&sierra_1port_device); - if (retval) - goto failed_1port_device_register; - retval = usb_serial_register(&sierra_3port_device); + retval = usb_serial_register(&sierra_device); if (retval) - goto failed_3port_device_register; + goto failed_device_register; retval = usb_register(&sierra_driver); @@ -721,18 +737,15 @@ static int __init sierra_init(void) return 0; failed_driver_register: - usb_serial_deregister(&sierra_3port_device); -failed_3port_device_register: - usb_serial_deregister(&sierra_1port_device); -failed_1port_device_register: + usb_serial_deregister(&sierra_device); +failed_device_register: return retval; } static void __exit sierra_exit(void) { usb_deregister (&sierra_driver); - usb_serial_deregister(&sierra_1port_device); - usb_serial_deregister(&sierra_3port_device); + usb_serial_deregister(&sierra_device); } module_init(sierra_init); @@ -743,6 +756,12 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); +module_param(truinstall, bool, 0); +MODULE_PARM_DESC(truinstall, "TRU-Install support"); + +module_param(nmea, bool, 0); +MODULE_PARM_DESC(nmea, "NMEA streaming"); + #ifdef CONFIG_USB_DEBUG module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug messages"); diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 1f0149495fb..b517f93352e 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -80,6 +80,7 @@ #include <linux/ioctl.h> #include <linux/serial.h> #include <linux/circ_buf.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/semaphore.h> #include <linux/usb.h> @@ -139,7 +140,7 @@ struct ti_port { }; struct ti_device { - struct semaphore td_open_close_sem; + struct mutex td_open_close_lock; int td_open_port_count; struct usb_serial *td_serial; int td_is_3410; @@ -424,7 +425,7 @@ static int ti_startup(struct usb_serial *serial) dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); return -ENOMEM; } - sema_init(&tdev->td_open_close_sem, 1); + mutex_init(&tdev->td_open_close_lock); tdev->td_serial = serial; usb_set_serial_data(serial, tdev); @@ -547,7 +548,7 @@ static int ti_open(struct usb_serial_port *port, struct file *file) tdev = tport->tp_tdev; /* only one open on any port on a device at a time */ - if (down_interruptible(&tdev->td_open_close_sem)) + if (mutex_lock_interruptible(&tdev->td_open_close_lock)) return -ERESTARTSYS; if (port->tty) @@ -568,7 +569,7 @@ static int ti_open(struct usb_serial_port *port, struct file *file) if (!urb) { dev_err(&port->dev, "%s - no interrupt urb\n", __FUNCTION__); status = -EINVAL; - goto up_sem; + goto release_lock; } urb->complete = ti_interrupt_callback; urb->context = tdev; @@ -576,11 +577,11 @@ static int ti_open(struct usb_serial_port *port, struct file *file) status = usb_submit_urb(urb, GFP_KERNEL); if (status) { dev_err(&port->dev, "%s - submit interrupt urb failed, %d\n", __FUNCTION__, status); - goto up_sem; + goto release_lock; } } - ti_set_termios(port, NULL); + ti_set_termios(port, port->tty->termios); dbg("%s - sending TI_OPEN_PORT", __FUNCTION__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, @@ -617,7 +618,7 @@ static int ti_open(struct usb_serial_port *port, struct file *file) usb_clear_halt(dev, port->write_urb->pipe); usb_clear_halt(dev, port->read_urb->pipe); - ti_set_termios(port, NULL); + ti_set_termios(port, port->tty->termios); dbg("%s - sending TI_OPEN_PORT (2)", __FUNCTION__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, @@ -656,13 +657,13 @@ static int ti_open(struct usb_serial_port *port, struct file *file) tport->tp_is_open = 1; ++tdev->td_open_port_count; - goto up_sem; + goto release_lock; unlink_int_urb: if (tdev->td_open_port_count == 0) usb_kill_urb(port->serial->port[0]->interrupt_in_urb); -up_sem: - up(&tdev->td_open_close_sem); +release_lock: + mutex_unlock(&tdev->td_open_close_lock); dbg("%s - exit %d", __FUNCTION__, status); return status; } @@ -674,7 +675,7 @@ static void ti_close(struct usb_serial_port *port, struct file *file) struct ti_port *tport; int port_number; int status; - int do_up; + int do_unlock; dbg("%s - port %d", __FUNCTION__, port->number); @@ -699,16 +700,16 @@ static void ti_close(struct usb_serial_port *port, struct file *file) if (status) dev_err(&port->dev, "%s - cannot send close port command, %d\n" , __FUNCTION__, status); - /* if down is interrupted, continue anyway */ - do_up = !down_interruptible(&tdev->td_open_close_sem); + /* if mutex_lock is interrupted, continue anyway */ + do_unlock = !mutex_lock_interruptible(&tdev->td_open_close_lock); --tport->tp_tdev->td_open_port_count; if (tport->tp_tdev->td_open_port_count <= 0) { /* last port is closed, shut down interrupt urb */ usb_kill_urb(port->serial->port[0]->interrupt_in_urb); tport->tp_tdev->td_open_port_count = 0; } - if (do_up) - up(&tdev->td_open_close_sem); + if (do_unlock) + mutex_unlock(&tdev->td_open_close_lock); dbg("%s - exit", __FUNCTION__); } @@ -896,24 +897,11 @@ static void ti_set_termios(struct usb_serial_port *port, dbg("%s - port %d", __FUNCTION__, port->number); - if (!tty || !tty->termios) { - dbg("%s - no tty or termios", __FUNCTION__); - return; - } - cflag = tty->termios->c_cflag; iflag = tty->termios->c_iflag; - if (old_termios && cflag == old_termios->c_cflag - && iflag == old_termios->c_iflag) { - dbg("%s - nothing to change", __FUNCTION__); - return; - } - - dbg("%s - clfag %08x, iflag %08x", __FUNCTION__, cflag, iflag); - - if (old_termios) - dbg("%s - old clfag %08x, old iflag %08x", __FUNCTION__, old_termios->c_cflag, old_termios->c_iflag); + dbg("%s - cflag %08x, iflag %08x", __FUNCTION__, cflag, iflag); + dbg("%s - old clfag %08x, old iflag %08x", __FUNCTION__, old_termios->c_cflag, old_termios->c_iflag); if (tport == NULL) return; @@ -947,6 +935,9 @@ static void ti_set_termios(struct usb_serial_port *port, break; } + /* CMSPAR isn't supported by this driver */ + tty->termios->c_cflag &= ~CMSPAR; + if (cflag & PARENB) { if (cflag & PARODD) { config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING; @@ -989,12 +980,17 @@ static void ti_set_termios(struct usb_serial_port *port, } baud = tty_get_baud_rate(tty); - if (!baud) baud = 9600; + if (!baud) + baud = 9600; if (tport->tp_tdev->td_is_3410) config->wBaudRate = (__u16)((923077 + baud/2) / baud); else config->wBaudRate = (__u16)((461538 + baud/2) / baud); + /* FIXME: Should calculate resulting baud here and report it back */ + if ((cflag & CBAUD) != B0) + tty_encode_baud_rate(tty, baud, baud); + dbg("%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d", __FUNCTION__, baud, config->wBaudRate, config->wFlags, config->bDataBits, config->bParity, config->bStopBits, config->cXon, config->cXoff, config->bUartMode); @@ -1497,11 +1493,10 @@ static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush) struct ti_device *tdev = tport->tp_tdev; struct usb_serial_port *port = tport->tp_port; wait_queue_t wait; - unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); - spin_lock_irqsave(&tport->tp_lock, flags); + spin_lock_irq(&tport->tp_lock); /* wait for data to drain from the buffer */ tdev->td_urb_error = 0; @@ -1512,11 +1507,11 @@ static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush) if (ti_buf_data_avail(tport->tp_write_buf) == 0 || timeout == 0 || signal_pending(current) || tdev->td_urb_error - || !usb_get_intfdata(port->serial->interface)) /* disconnect */ + || port->serial->disconnected) /* disconnect */ break; - spin_unlock_irqrestore(&tport->tp_lock, flags); + spin_unlock_irq(&tport->tp_lock); timeout = schedule_timeout(timeout); - spin_lock_irqsave(&tport->tp_lock, flags); + spin_lock_irq(&tport->tp_lock); } set_current_state(TASK_RUNNING); remove_wait_queue(&tport->tp_write_wait, &wait); @@ -1525,19 +1520,23 @@ static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush) if (flush) ti_buf_clear(tport->tp_write_buf); - spin_unlock_irqrestore(&tport->tp_lock, flags); + spin_unlock_irq(&tport->tp_lock); + mutex_lock(&port->serial->disc_mutex); /* wait for data to drain from the device */ /* wait for empty tx register, plus 20 ms */ timeout += jiffies; tport->tp_lsr &= ~TI_LSR_TX_EMPTY; while ((long)(jiffies - timeout) < 0 && !signal_pending(current) && !(tport->tp_lsr&TI_LSR_TX_EMPTY) && !tdev->td_urb_error - && usb_get_intfdata(port->serial->interface)) { /* not disconnected */ + && !port->serial->disconnected) { if (ti_get_lsr(tport)) break; + mutex_unlock(&port->serial->disc_mutex); msleep_interruptible(20); + mutex_lock(&port->serial->disc_mutex); } + mutex_unlock(&port->serial->disc_mutex); } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 497e29a700c..3ce98e8d7bc 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -225,16 +225,21 @@ static int serial_open (struct tty_struct *tty, struct file * filp) goto bailout_mutex_unlock; } + retval = usb_autopm_get_interface(serial->interface); + if (retval) + goto bailout_module_put; /* only call the device specific open if this * is the first time the port is opened */ retval = serial->type->open(port, filp); if (retval) - goto bailout_module_put; + goto bailout_interface_put; } mutex_unlock(&port->mutex); return 0; +bailout_interface_put: + usb_autopm_put_interface(serial->interface); bailout_module_put: module_put(serial->type->driver.owner); bailout_mutex_unlock: @@ -264,17 +269,21 @@ static void serial_close(struct tty_struct *tty, struct file * filp) } --port->open_count; - if (port->open_count == 0) { + if (port->open_count == 0) /* only call the device specific close if this * port is being closed by the last owner */ port->serial->type->close(port, filp); + if (port->open_count == (port->console? 1 : 0)) { if (port->tty) { if (port->tty->driver_data) port->tty->driver_data = NULL; port->tty = NULL; } + } + if (port->open_count == 0) { + usb_autopm_put_interface(port->serial->interface); module_put(port->serial->type->driver.owner); } @@ -625,6 +634,7 @@ static struct usb_serial * create_serial (struct usb_device *dev, serial->type = driver; serial->interface = interface; kref_init(&serial->kref); + mutex_init(&serial->disc_mutex); return serial; } @@ -1080,20 +1090,22 @@ void usb_serial_disconnect(struct usb_interface *interface) usb_serial_console_disconnect(serial); dbg ("%s", __FUNCTION__); + mutex_lock(&serial->disc_mutex); usb_set_intfdata (interface, NULL); - if (serial) { - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - if (port) { - if (port->tty) - tty_hangup(port->tty); - kill_traffic(port); - } + /* must set a flag, to signal subdrivers */ + serial->disconnected = 1; + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + if (port) { + if (port->tty) + tty_hangup(port->tty); + kill_traffic(port); } - /* let the last holder of this object - * cause it to be cleaned up */ - usb_serial_put(serial); } + /* let the last holder of this object + * cause it to be cleaned up */ + mutex_unlock(&serial->disc_mutex); + usb_serial_put(serial); dev_info(dev, "device disconnected\n"); } @@ -1103,9 +1115,6 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) struct usb_serial_port *port; int i, r = 0; - if (!serial) /* device has been disconnected */ - return 0; - for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) @@ -1253,6 +1262,7 @@ static void fixup_generic(struct usb_serial_driver *device) 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, resume); } int usb_serial_register(struct usb_serial_driver *driver) /* must be called with BKL held */ diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 7ee087fed91..22b3f78a388 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -349,16 +349,20 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); - /* Try to send shutdown message, if the device is gone, this will just fail. */ - transfer_buffer = kmalloc (0x12, GFP_KERNEL); - if (transfer_buffer) { - usb_control_msg (port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - VISOR_CLOSE_NOTIFICATION, 0xc2, - 0x0000, 0x0000, - transfer_buffer, 0x12, 300); - kfree (transfer_buffer); + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) { + /* Try to send shutdown message, unless the device is gone */ + transfer_buffer = kmalloc (0x12, GFP_KERNEL); + if (transfer_buffer) { + usb_control_msg (port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + VISOR_CLOSE_NOTIFICATION, 0xc2, + 0x0000, 0x0000, + transfer_buffer, 0x12, 300); + kfree (transfer_buffer); + } } + mutex_unlock(&port->serial->disc_mutex); if (stats) dev_info(&port->dev, "Bytes In = %d Bytes Out = %d\n", diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index ee5dd8b5a71..38726ef3132 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -610,8 +610,7 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) if (retval) goto exit; - if (port->tty) - port->tty->low_latency = 1; + port->tty->low_latency = 1; /* send an open port command */ retval = firm_open(port); @@ -659,11 +658,14 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) struct list_head *tmp2; dbg("%s - port %d", __FUNCTION__, port->number); - + + mutex_lock(&port->serial->disc_mutex); /* filp is NULL when called from usb_serial_disconnect */ - if (filp && (tty_hung_up_p(filp))) { + if ((filp && (tty_hung_up_p(filp))) || port->serial->disconnected) { + mutex_unlock(&port->serial->disc_mutex); return; } + mutex_unlock(&port->serial->disc_mutex); port->tty->closing = 1; |