diff options
Diffstat (limited to 'drivers/usb/serial')
44 files changed, 5112 insertions, 1315 deletions
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 8bd44fda5eaf..5076b9d97057 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -53,6 +53,15 @@ config USB_SERIAL_GENERIC support" be compiled as a module for this driver to be used properly. +config USB_SERIAL_AIRCABLE + tristate "AIRcable USB Bluetooth Dongle Driver (EXPERIMENTAL)" + depends on USB_SERIAL && EXPERIMENTAL + help + Say Y here if you want to use AIRcable USB Bluetoot Dongle. + + To compile this driver as a module, choose M here: the module + will be called aircable. + config USB_SERIAL_AIRPRIME tristate "USB AirPrime CDMA Wireless Driver" depends on USB_SERIAL @@ -62,15 +71,6 @@ config USB_SERIAL_AIRPRIME To compile this driver as a module, choose M here: the module will be called airprime. -config USB_SERIAL_ANYDATA - tristate "USB AnyData CDMA Wireless Driver" - depends on USB_SERIAL - help - Say Y here if you want to use a AnyData CDMA device. - - To compile this driver as a module, choose M here: the - module will be called anydata. - config USB_SERIAL_ARK3116 tristate "USB ARK Micro 3116 USB Serial Driver (EXPERIMENTAL)" depends on USB_SERIAL && EXPERIMENTAL @@ -422,6 +422,21 @@ config USB_SERIAL_MCT_U232 To compile this driver as a module, choose M here: the module will be called mct_u232. +config USB_SERIAL_MOS7840 + tristate "USB Moschip 7840/7820 USB Serial Driver" + depends on USB_SERIAL + ---help--- + Say Y here if you want to use a MCS7840 Quad-Serial or MCS7820 + Dual-Serial port device from MosChip Semiconductor. + + The MCS7840 and MCS7820 have been developed to connect a wide range + of standard serial devices to a USB host. The MCS7840 has a USB + device controller connected to four (4) individual UARTs while the + MCS7820 controller connects to two (2) individual UARTs. + + To compile this driver as a module, choose M here: the + module will be called mos7840. If unsure, choose N. + config USB_SERIAL_NAVMAN tristate "USB Navman GPS device" depends on USB_SERIAL @@ -456,6 +471,17 @@ config USB_SERIAL_SAFE_PADDED bool "USB Secure Encapsulated Driver - Padded" depends on USB_SERIAL_SAFE +config USB_SERIAL_SIERRAWIRELESS + tristate "USB Sierra Wireless Driver" + depends on USB_SERIAL + help + Say M here if you want to use a Sierra Wireless device (if + using an PC 5220 or AC580 please use the Airprime driver + instead). + + To compile this driver as a module, choose M here: the + module will be called sierra. + config USB_SERIAL_TI tristate "USB TI 3410/5052 Serial Driver" depends on USB_SERIAL @@ -491,15 +517,18 @@ config USB_SERIAL_XIRCOM module will be called keyspan_pda. config USB_SERIAL_OPTION - tristate "USB driver for GSM modems" + tristate "USB driver for GSM and CDMA modems" depends on USB_SERIAL help - Say Y here if you have an "Option" GSM PCMCIA card - (or an OEM version: branded Huawei, Audiovox, or Novatel). + Say Y here if you have a GSM or CDMA modem that's connected to USB. - These cards feature a built-in OHCI-USB adapter and an - internally-connected GSM modem. The USB bus is not - accessible externally. + This driver also supports several PCMCIA cards which have a + built-in OHCI-USB adapter and an internally-connected GSM modem. + The USB bus on these cards is not accessible externally. + + Supported devices include (some of?) those made by: + Option, Huawei, Audiovox, Sierra Wireless, Novatel Wireless, or + Anydata. To compile this driver as a module, choose M here: the module will be called option. @@ -521,5 +550,6 @@ config USB_EZUSB depends on USB_SERIAL_KEYSPAN_PDA || USB_SERIAL_XIRCOM || USB_SERIAL_KEYSPAN || USB_SERIAL_WHITEHEAT default y + endmenu diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 5a0960fc9d3e..8dce83340e31 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -11,8 +11,8 @@ usbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.o usbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y) +obj-$(CONFIG_USB_SERIAL_AIRCABLE) += aircable.o obj-$(CONFIG_USB_SERIAL_AIRPRIME) += airprime.o -obj-$(CONFIG_USB_SERIAL_ANYDATA) += anydata.o obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o obj-$(CONFIG_USB_SERIAL_CP2101) += cp2101.o @@ -34,11 +34,13 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o +obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o obj-$(CONFIG_USB_SERIAL_OPTION) += option.o obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o +obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c new file mode 100644 index 000000000000..2ccd9ded52a5 --- /dev/null +++ b/drivers/usb/serial/aircable.c @@ -0,0 +1,625 @@ +/* + * AIRcable USB Bluetooth Dongle Driver. + * + * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com) + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * The device works as an standard CDC device, it has 2 interfaces, the first + * one is for firmware access and the second is the serial one. + * The protocol is very simply, there are two posibilities reading or writing. + * When writting the first urb must have a Header that starts with 0x20 0x29 the + * next two bytes must say how much data will be sended. + * When reading the process is almost equal except that the header starts with + * 0x00 0x20. + * + * The device simply need some stuff to understand data comming from the usb + * buffer: The First and Second byte is used for a Header, the Third and Fourth + * tells the device the amount of information the package holds. + * Packages are 60 bytes long Header Stuff. + * When writting to the device the first two bytes of the header are 0x20 0x29 + * When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange + * situation, when too much data arrives to the device because it sends the data + * but with out the header. I will use a simply hack to override this situation, + * if there is data coming that does not contain any header, then that is data + * that must go directly to the tty, as there is no documentation about if there + * is any other control code, I will simply check for the first + * one. + * + * The driver registers himself with the USB-serial core and the USB Core. I had + * to implement a probe function agains USB-serial, because other way, the + * driver was attaching himself to both interfaces. I have tryed with different + * configurations of usb_serial_driver with out exit, only the probe function + * could handle this correctly. + * + * I have taken some info from a Greg Kroah-Hartman article: + * http://www.linuxjournal.com/article/6573 + * And from Linux Device Driver Kit CD, which is a great work, the authors taken + * the work to recompile lots of information an knowladge in drivers development + * and made it all avaible inside a cd. + * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/ + * + */ + +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/circ_buf.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> + +static int debug; + +/* Vendor and Product ID */ +#define AIRCABLE_VID 0x16CA +#define AIRCABLE_USB_PID 0x1502 + +/* write buffer size defines */ +#define AIRCABLE_BUF_SIZE 2048 + +/* Protocol Stuff */ +#define HCI_HEADER_LENGTH 0x4 +#define TX_HEADER_0 0x20 +#define TX_HEADER_1 0x29 +#define RX_HEADER_0 0x00 +#define RX_HEADER_1 0x20 +#define MAX_HCI_FRAMESIZE 60 +#define HCI_COMPLETE_FRAME 64 + +/* rx_flags */ +#define THROTTLED 0x01 +#define ACTUALLY_THROTTLED 0x02 + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.0b2" +#define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>" +#define DRIVER_DESC "AIRcable USB Driver" + +/* ID table that will be registered with USB core */ +static struct usb_device_id id_table [] = { + { USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + + +/* Internal Structure */ +struct aircable_private { + spinlock_t rx_lock; /* spinlock for the receive lines */ + struct circ_buf *tx_buf; /* write buffer */ + struct circ_buf *rx_buf; /* read buffer */ + int rx_flags; /* for throttilng */ + struct work_struct rx_work; /* work cue for the receiving line */ +}; + +/* Private methods */ + +/* Circular Buffer Methods, code from ti_usb_3410_5052 used */ +/* + * serial_buf_clear + * + * Clear out all data in the circular buffer. + */ +static void serial_buf_clear(struct circ_buf *cb) +{ + cb->head = cb->tail = 0; +} + +/* + * serial_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ +static struct circ_buf *serial_buf_alloc(void) +{ + struct circ_buf *cb; + cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL); + if (cb == NULL) + return NULL; + cb->buf = kmalloc(AIRCABLE_BUF_SIZE, GFP_KERNEL); + if (cb->buf == NULL) { + kfree(cb); + return NULL; + } + serial_buf_clear(cb); + return cb; +} + +/* + * serial_buf_free + * + * Free the buffer and all associated memory. + */ +static void serial_buf_free(struct circ_buf *cb) +{ + kfree(cb->buf); + kfree(cb); +} + +/* + * serial_buf_data_avail + * + * Return the number of bytes of data available in the circular + * buffer. + */ +static int serial_buf_data_avail(struct circ_buf *cb) +{ + return CIRC_CNT(cb->head,cb->tail,AIRCABLE_BUF_SIZE); +} + +/* + * serial_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 int serial_buf_put(struct circ_buf *cb, const char *buf, int count) +{ + int c, ret = 0; + while (1) { + c = CIRC_SPACE_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(cb->buf + cb->head, buf, c); + cb->head = (cb->head + c) & (AIRCABLE_BUF_SIZE-1); + buf += c; + count -= c; + ret= c; + } + return ret; +} + +/* + * serial_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 int serial_buf_get(struct circ_buf *cb, char *buf, int count) +{ + int c, ret = 0; + while (1) { + c = CIRC_CNT_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(buf, cb->buf + cb->tail, c); + cb->tail = (cb->tail + c) & (AIRCABLE_BUF_SIZE-1); + buf += c; + count -= c; + ret= c; + } + return ret; +} + +/* End of circula buffer methods */ + +static void aircable_send(struct usb_serial_port *port) +{ + int count, result; + struct aircable_private *priv = usb_get_serial_port_data(port); + unsigned char* buf; + dbg("%s - port %d", __FUNCTION__, port->number); + if (port->write_urb_busy) + return; + + count = min(serial_buf_data_avail(priv->tx_buf), MAX_HCI_FRAMESIZE); + if (count == 0) + return; + + buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_ATOMIC); + if (!buf) { + err("%s- kzalloc(%d) failed.", __FUNCTION__, + count + HCI_HEADER_LENGTH); + return; + } + + buf[0] = TX_HEADER_0; + buf[1] = TX_HEADER_1; + buf[2] = (unsigned char)count; + buf[3] = (unsigned char)(count >> 8); + serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE); + + memcpy(port->write_urb->transfer_buffer, buf, + count + HCI_HEADER_LENGTH); + + kfree(buf); + port->write_urb_busy = 1; + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, + count + HCI_HEADER_LENGTH, + port->write_urb->transfer_buffer); + port->write_urb->transfer_buffer_length = count + HCI_HEADER_LENGTH; + port->write_urb->dev = port->serial->dev; + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + + if (result) { + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __FUNCTION__, result); + port->write_urb_busy = 0; + } + + schedule_work(&port->work); +} + +static void aircable_read(void *params) +{ + struct usb_serial_port *port = params; + struct aircable_private *priv = usb_get_serial_port_data(port); + struct tty_struct *tty; + unsigned char *data; + int count; + if (priv->rx_flags & THROTTLED){ + if (priv->rx_flags & ACTUALLY_THROTTLED) + schedule_work(&priv->rx_work); + return; + } + + /* By now I will flush data to the tty in packages of no more than + * 64 bytes, to ensure I do not get throttled. + * Ask USB mailing list for better aproach. + */ + tty = port->tty; + + if (!tty) + schedule_work(&priv->rx_work); + + count = min(64, serial_buf_data_avail(priv->rx_buf)); + + if (count <= 0) + return; //We have finished sending everything. + + tty_prepare_flip_string(tty, &data, count); + if (!data){ + err("%s- kzalloc(%d) failed.", __FUNCTION__, count); + return; + } + + serial_buf_get(priv->rx_buf, data, count); + + tty_flip_buffer_push(tty); + + if (serial_buf_data_avail(priv->rx_buf)) + schedule_work(&priv->rx_work); + + return; +} +/* End of private methods */ + +static int aircable_probe(struct usb_serial *serial, + const struct usb_device_id *id) +{ + struct usb_host_interface *iface_desc = serial->interface->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + int num_bulk_out=0; + int i; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + if (((endpoint->bEndpointAddress & 0x80) == 0x00) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found our bulk out endpoint */ + dbg("found bulk out on endpoint %d", i); + ++num_bulk_out; + } + } + + if (num_bulk_out == 0) { + dbg("Invalid interface, discarding"); + return -ENODEV; + } + + return 0; +} + +static int aircable_attach (struct usb_serial *serial) +{ + struct usb_serial_port *port = serial->port[0]; + struct aircable_private *priv; + + priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL); + if (!priv){ + err("%s- kmalloc(%Zd) failed.", __FUNCTION__, + sizeof(struct aircable_private)); + return -ENOMEM; + } + + /* Allocation of Circular Buffers */ + priv->tx_buf = serial_buf_alloc(); + if (priv->tx_buf == NULL) { + kfree(priv); + return -ENOMEM; + } + + priv->rx_buf = serial_buf_alloc(); + if (priv->rx_buf == NULL) { + kfree(priv->tx_buf); + kfree(priv); + return -ENOMEM; + } + + priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); + INIT_WORK(&priv->rx_work, aircable_read, port); + + usb_set_serial_port_data(serial->port[0], priv); + + return 0; +} + +static void aircable_shutdown(struct usb_serial *serial) +{ + + struct usb_serial_port *port = serial->port[0]; + struct aircable_private *priv = usb_get_serial_port_data(port); + + dbg("%s", __FUNCTION__); + + if (priv) { + serial_buf_free(priv->tx_buf); + serial_buf_free(priv->rx_buf); + usb_set_serial_port_data(port, NULL); + kfree(priv); + } +} + +static int aircable_write_room(struct usb_serial_port *port) +{ + struct aircable_private *priv = usb_get_serial_port_data(port); + return serial_buf_data_avail(priv->tx_buf); +} + +static int aircable_write(struct usb_serial_port *port, + const unsigned char *source, int count) +{ + struct aircable_private *priv = usb_get_serial_port_data(port); + int temp; + + dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, source); + + if (!count){ + dbg("%s - write request of 0 bytes", __FUNCTION__); + return count; + } + + temp = serial_buf_put(priv->tx_buf, source, count); + + aircable_send(port); + + if (count > AIRCABLE_BUF_SIZE) + count = AIRCABLE_BUF_SIZE; + + return count; + +} + +static void aircable_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + int result; + + dbg("%s - urb->status: %d", __FUNCTION__ , urb->status); + + /* This has been taken from cypress_m8.c cypress_write_int_callback */ + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + port->write_urb_busy = 0; + return; + default: + /* error in the urb, so we have to resubmit it */ + dbg("%s - Overflow in write", __FUNCTION__); + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + port->write_urb->transfer_buffer_length = 1; + port->write_urb->dev = port->serial->dev; + result = usb_submit_urb(port->write_urb, GFP_KERNEL); + if (result) + dev_err(&urb->dev->dev, + "%s - failed resubmitting write urb, error %d\n", + __FUNCTION__, result); + else + return; + } + + port->write_urb_busy = 0; + + aircable_send(port); +} + +static void aircable_read_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + struct aircable_private *priv = usb_get_serial_port_data(port); + struct tty_struct *tty; + unsigned long no_packages, remaining, package_length, i; + int result, shift = 0; + unsigned char *temp; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (urb->status) { + dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + if (!port->open_count) { + dbg("%s - port is closed, exiting.", __FUNCTION__); + return; + } + if (urb->status == -EPROTO) { + dbg("%s - caught -EPROTO, resubmitting the urb", + __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, + port->read_urb->transfer_buffer_length, + aircable_read_bulk_callback, port); + + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&urb->dev->dev, + "%s - failed resubmitting read urb, error %d\n", + __FUNCTION__, result); + return; + } + dbg("%s - unable to handle the error, exiting.", __FUNCTION__); + return; + } + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, + urb->actual_length,urb->transfer_buffer); + + tty = port->tty; + if (tty && urb->actual_length) { + if (urb->actual_length <= 2) { + /* This is an incomplete package */ + serial_buf_put(priv->rx_buf, urb->transfer_buffer, + urb->actual_length); + } else { + temp = urb->transfer_buffer; + if (temp[0] == RX_HEADER_0) + shift = HCI_HEADER_LENGTH; + + remaining = urb->actual_length; + no_packages = urb->actual_length / (HCI_COMPLETE_FRAME); + + if (urb->actual_length % HCI_COMPLETE_FRAME != 0) + no_packages+=1; + + for (i = 0; i < no_packages ;i++) { + if (remaining > (HCI_COMPLETE_FRAME)) + package_length = HCI_COMPLETE_FRAME; + else + package_length = remaining; + remaining -= package_length; + + serial_buf_put(priv->rx_buf, + urb->transfer_buffer + shift + + (HCI_COMPLETE_FRAME) * (i), + package_length - shift); + } + } + aircable_read(port); + } + + /* Schedule the next read _if_ we are still open */ + if (port->open_count) { + usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + aircable_read_bulk_callback, port); + + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&urb->dev->dev, + "%s - failed resubmitting read urb, error %d\n", + __FUNCTION__, result); + } + + return; +} + +/* Based on ftdi_sio.c throttle */ +static void aircable_throttle(struct usb_serial_port *port) +{ + struct aircable_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->rx_lock, flags); + priv->rx_flags |= THROTTLED; + spin_unlock_irqrestore(&priv->rx_lock, flags); +} + +/* Based on ftdi_sio.c unthrottle */ +static void aircable_unthrottle(struct usb_serial_port *port) +{ + struct aircable_private *priv = usb_get_serial_port_data(port); + int actually_throttled; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->rx_lock, flags); + actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; + priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); + spin_unlock_irqrestore(&priv->rx_lock, flags); + + if (actually_throttled) + schedule_work(&priv->rx_work); +} + +static struct usb_serial_driver aircable_device = { + .description = "aircable", + .id_table = id_table, + .num_ports = 1, + .attach = aircable_attach, + .probe = aircable_probe, + .shutdown = aircable_shutdown, + .write = aircable_write, + .write_room = aircable_write_room, + .write_bulk_callback = aircable_write_bulk_callback, + .read_bulk_callback = aircable_read_bulk_callback, + .throttle = aircable_throttle, + .unthrottle = aircable_unthrottle, +}; + +static struct usb_driver aircable_driver = { + .name = "aircable", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, +}; + +static int __init aircable_init (void) +{ + int retval; + retval = usb_serial_register(&aircable_device); + if (retval) + goto failed_serial_register; + retval = usb_register(&aircable_driver); + if (retval) + goto failed_usb_register; + return 0; + +failed_serial_register: + usb_serial_deregister(&aircable_device); +failed_usb_register: + return retval; +} + +static void __exit aircable_exit (void) +{ + usb_deregister(&aircable_driver); + usb_serial_deregister(&aircable_device); +} + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +module_init(aircable_init); +module_exit(aircable_exit); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index 94b9ba0ff875..6e1a84a858d4 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -1,7 +1,7 @@ /* * AirPrime CDMA Wireless Serial USB driver * - * Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (C) 2005-2006 Greg Kroah-Hartman <gregkh@suse.de> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version @@ -11,26 +11,264 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/module.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> static struct usb_device_id id_table [] = { { USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */ - { USB_DEVICE(0xf3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */ - { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */ + { USB_DEVICE(0x0f3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */ + { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ + { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless Aircard 580 */ { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */ { }, }; MODULE_DEVICE_TABLE(usb, id_table); +#define URB_TRANSFER_BUFFER_SIZE 4096 +#define NUM_READ_URBS 4 +#define NUM_WRITE_URBS 4 +#define NUM_BULK_EPS 3 +#define MAX_BULK_EPS 6 + +/* if overridden by the user, then use their value for the size of the + * read and write urbs, and the number of endpoints */ +static int buffer_size = URB_TRANSFER_BUFFER_SIZE; +static int endpoints = NUM_BULK_EPS; +static int debug; +struct airprime_private { + spinlock_t lock; + int outstanding_urbs; + int throttled; + struct urb *read_urbp[NUM_READ_URBS]; +}; + +static void airprime_read_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; + int result; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (urb->status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, urb->status); + /* something happened, so free up the memory for this urb */ + if (urb->transfer_buffer) { + kfree (urb->transfer_buffer); + urb->transfer_buffer = NULL; + } + return; + } + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); + + tty = port->tty; + if (tty && urb->actual_length) { + tty_insert_flip_string (tty, data, urb->actual_length); + tty_flip_buffer_push (tty); + } + + result = usb_submit_urb (urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", + __FUNCTION__, result); + return; +} + +static void airprime_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + struct airprime_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* free up the transfer buffer, as usb_free_urb() does not do this */ + kfree (urb->transfer_buffer); + + if (urb->status) + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + spin_lock_irqsave(&priv->lock, flags); + --priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + + usb_serial_port_softint(port); +} + +static int airprime_open(struct usb_serial_port *port, struct file *filp) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + struct urb *urb; + char *buffer = NULL; + int i; + int result = 0; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* initialize our private data structure if it isn't already created */ + if (!priv) { + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + result = -ENOMEM; + goto out; + } + spin_lock_init(&priv->lock); + usb_set_serial_port_data(port, priv); + } + + for (i = 0; i < NUM_READ_URBS; ++i) { + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) { + dev_err(&port->dev, "%s - out of memory.\n", + __FUNCTION__); + result = -ENOMEM; + goto errout; + } + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_err(&port->dev, "%s - no more urbs?\n", + __FUNCTION__); + result = -ENOMEM; + goto errout; + } + usb_fill_bulk_urb(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + buffer, buffer_size, + airprime_read_bulk_callback, port); + result = usb_submit_urb(urb, GFP_KERNEL); + if (result) { + dev_err(&port->dev, + "%s - failed submitting read urb %d for port %d, error %d\n", + __FUNCTION__, i, port->number, result); + goto errout; + } + /* remember this urb so we can kill it when the port is closed */ + priv->read_urbp[i] = urb; + } + goto out; + + errout: + /* some error happened, cancel any submitted urbs and clean up anything that + got allocated successfully */ + + for ( ; i >= 0; --i) { + urb = priv->read_urbp[i]; + if (urb) { + /* This urb was submitted successfully. So we have to + cancel it. + Unlinking the urb will invoke read_bulk_callback() + with an error status, so its transfer buffer will + be freed there */ + if (usb_unlink_urb (urb) != -EINPROGRESS) { + /* comments in drivers/usb/core/urb.c say this + can only happen if the urb was never submitted, + or has completed already. + Either way we may have to free the transfer + buffer here. */ + if (urb->transfer_buffer) { + kfree (urb->transfer_buffer); + urb->transfer_buffer = NULL; + } + } + usb_free_urb (urb); + } + } + + out: + return result; +} + +static void airprime_close(struct usb_serial_port *port, struct file * filp) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + int i; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* killing the urb will invoke read_bulk_callback() with an error status, + so the transfer buffer will be freed there */ + for (i = 0; i < NUM_READ_URBS; ++i) { + usb_kill_urb (priv->read_urbp[i]); + usb_free_urb (priv->read_urbp[i]); + } + + /* free up private structure */ + kfree (priv); + usb_set_serial_port_data(port, NULL); +} + +static int airprime_write(struct usb_serial_port *port, + const unsigned char *buf, int count) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + struct urb *urb; + unsigned char *buffer; + unsigned long flags; + int status; + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + if (priv->outstanding_urbs > NUM_WRITE_URBS) { + spin_unlock_irqrestore(&priv->lock, flags); + dbg("%s - write limit hit\n", __FUNCTION__); + return 0; + } + spin_unlock_irqrestore(&priv->lock, flags); + buffer = kmalloc(count, GFP_ATOMIC); + if (!buffer) { + dev_err(&port->dev, "out of memory\n"); + return -ENOMEM; + } + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(&port->dev, "no more free urbs\n"); + kfree (buffer); + return -ENOMEM; + } + memcpy (buffer, buf, count); + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer); + + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + buffer, count, + airprime_write_bulk_callback, port); + + /* send it down the pipe */ + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + dev_err(&port->dev, + "%s - usb_submit_urb(write bulk) failed with status = %d\n", + __FUNCTION__, status); + count = status; + kfree (buffer); + } else { + spin_lock_irqsave(&priv->lock, flags); + ++priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + } + /* we are done with this urb, so let the host driver + * really free it when it is finished with it */ + usb_free_urb (urb); + return count; +} + static struct usb_driver airprime_driver = { .name = "airprime", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, - .no_dynamic_id = 1, + .no_dynamic_id = 1, }; static struct usb_serial_driver airprime_device = { @@ -42,13 +280,17 @@ static struct usb_serial_driver airprime_device = { .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, - .num_ports = 1, + .open = airprime_open, + .close = airprime_close, + .write = airprime_write, }; static int __init airprime_init(void) { int retval; + airprime_device.num_ports = + (endpoints > 0 && endpoints <= MAX_BULK_EPS) ? endpoints : NUM_BULK_EPS; retval = usb_serial_register(&airprime_device); if (retval) return retval; @@ -60,6 +302,8 @@ static int __init airprime_init(void) static void __exit airprime_exit(void) { + dbg("%s", __FUNCTION__); + usb_deregister(&airprime_driver); usb_serial_deregister(&airprime_device); } @@ -67,3 +311,10 @@ static void __exit airprime_exit(void) module_init(airprime_init); module_exit(airprime_exit); MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled"); +module_param(buffer_size, int, 0); +MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers in bytes (default 4096)"); +module_param(endpoints, int, 0); +MODULE_PARM_DESC(endpoints, "Number of bulk EPs to configure (default 3)"); diff --git a/drivers/usb/serial/anydata.c b/drivers/usb/serial/anydata.c deleted file mode 100644 index 343f6f228220..000000000000 --- a/drivers/usb/serial/anydata.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * AnyData CDMA Serial USB driver - * - * Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/tty.h> -#include <linux/module.h> -#include <linux/usb.h> -#include "usb-serial.h" - -static struct usb_device_id id_table [] = { - { USB_DEVICE(0x16d5, 0x6501) }, /* AirData CDMA device */ - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -/* if overridden by the user, then use their value for the size of the - * read and write urbs */ -static int buffer_size; -static int debug; - -static struct usb_driver anydata_driver = { - .name = "anydata", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, - .no_dynamic_id = 1, -}; - -static int anydata_open(struct usb_serial_port *port, struct file *filp) -{ - char *buffer; - int result = 0; - - dbg("%s - port %d", __FUNCTION__, port->number); - - if (buffer_size) { - /* override the default buffer sizes */ - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(&port->dev, "%s - out of memory.\n", - __FUNCTION__); - return -ENOMEM; - } - kfree (port->read_urb->transfer_buffer); - port->read_urb->transfer_buffer = buffer; - port->read_urb->transfer_buffer_length = buffer_size; - - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(&port->dev, "%s - out of memory.\n", - __FUNCTION__); - return -ENOMEM; - } - kfree (port->write_urb->transfer_buffer); - port->write_urb->transfer_buffer = buffer; - port->write_urb->transfer_buffer_length = buffer_size; - port->bulk_out_size = buffer_size; - } - - /* Start reading from the device */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - usb_serial_generic_write_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __FUNCTION__, result); - - return result; -} - -static struct usb_serial_driver anydata_device = { - .driver = { - .owner = THIS_MODULE, - .name = "anydata", - }, - .id_table = id_table, - .num_interrupt_in = NUM_DONT_CARE, - .num_bulk_in = NUM_DONT_CARE, - .num_bulk_out = NUM_DONT_CARE, - .num_ports = 1, - .open = anydata_open, -}; - -static int __init anydata_init(void) -{ - int retval; - - retval = usb_serial_register(&anydata_device); - if (retval) - return retval; - retval = usb_register(&anydata_driver); - if (retval) - usb_serial_deregister(&anydata_device); - return retval; -} - -static void __exit anydata_exit(void) -{ - usb_deregister(&anydata_driver); - usb_serial_deregister(&anydata_device); -} - -module_init(anydata_init); -module_exit(anydata_exit); -MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled or not"); -module_param(buffer_size, int, 0); -MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers"); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 8dec796222a0..ca52f12f0e24 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2006 + * Simon Schulz (ark3116_driver <at> auctionant.de) + * * ark3116 * - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547, * productid=0x0232) (used in a datacable called KQ-U8A) @@ -8,8 +11,6 @@ * * - based on logs created by usbsnoopy * - * Author : Simon Schulz [ark3116_driver<AT>auctionant.de] - * * 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 @@ -21,7 +22,9 @@ #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> +#include <linux/serial.h> +#include <asm/uaccess.h> static int debug; @@ -43,10 +46,10 @@ static inline void ARK3116_SND(struct usb_serial *serial, int seq, { int result; result = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev,0), + usb_sndctrlpipe(serial->dev, 0), request, requesttype, value, index, - NULL,0x00, 1000); - dbg("%03d > ok",seq); + NULL, 0x00, 1000); + dbg("%03d > ok", seq); } static inline void ARK3116_RCV(struct usb_serial *serial, int seq, @@ -56,27 +59,25 @@ static inline void ARK3116_RCV(struct usb_serial *serial, int seq, { int result; result = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev,0), - request, requesttype, value, index, - buf, 0x0000001, 1000); + usb_rcvctrlpipe(serial->dev, 0), + request, requesttype, value, index, + buf, 0x0000001, 1000); if (result) - dbg("%03d < %d bytes [0x%02X]",seq, result, buf[0]); + dbg("%03d < %d bytes [0x%02X]", seq, result, buf[0]); else dbg("%03d < 0 bytes", seq); } - static inline void ARK3116_RCV_QUIET(struct usb_serial *serial, __u8 request, __u8 requesttype, __u16 value, __u16 index, char *buf) { usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev,0), + usb_rcvctrlpipe(serial->dev, 0), request, requesttype, value, index, buf, 0x0000001, 1000); } - static int ark3116_attach(struct usb_serial *serial) { char *buf; @@ -84,10 +85,10 @@ static int ark3116_attach(struct usb_serial *serial) int i; for (i = 0; i < serial->num_ports; ++i) { - priv = kmalloc (sizeof (struct ark3116_private), GFP_KERNEL); + priv = kmalloc(sizeof (struct ark3116_private), GFP_KERNEL); if (!priv) goto cleanup; - memset (priv, 0x00, sizeof (struct ark3116_private)); + memset(priv, 0x00, sizeof (struct ark3116_private)); spin_lock_init(&priv->lock); usb_set_serial_port_data(serial->port[i], priv); @@ -95,63 +96,62 @@ static int ark3116_attach(struct usb_serial *serial) buf = kmalloc(1, GFP_KERNEL); if (!buf) { - dbg("error kmalloc -> out of mem ?"); + dbg("error kmalloc -> out of mem?"); goto cleanup; } /* 3 */ - ARK3116_SND(serial, 3,0xFE,0x40,0x0008,0x0002); - ARK3116_SND(serial, 4,0xFE,0x40,0x0008,0x0001); - ARK3116_SND(serial, 5,0xFE,0x40,0x0000,0x0008); - ARK3116_SND(serial, 6,0xFE,0x40,0x0000,0x000B); + ARK3116_SND(serial, 3, 0xFE, 0x40, 0x0008, 0x0002); + ARK3116_SND(serial, 4, 0xFE, 0x40, 0x0008, 0x0001); + ARK3116_SND(serial, 5, 0xFE, 0x40, 0x0000, 0x0008); + ARK3116_SND(serial, 6, 0xFE, 0x40, 0x0000, 0x000B); /* <-- seq7 */ - ARK3116_RCV(serial, 7,0xFE,0xC0,0x0000,0x0003, 0x00, buf); - ARK3116_SND(serial, 8,0xFE,0x40,0x0080,0x0003); - ARK3116_SND(serial, 9,0xFE,0x40,0x001A,0x0000); - ARK3116_SND(serial,10,0xFE,0x40,0x0000,0x0001); - ARK3116_SND(serial,11,0xFE,0x40,0x0000,0x0003); + ARK3116_RCV(serial, 7, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf); + ARK3116_SND(serial, 8, 0xFE, 0x40, 0x0080, 0x0003); + ARK3116_SND(serial, 9, 0xFE, 0x40, 0x001A, 0x0000); + ARK3116_SND(serial, 10, 0xFE, 0x40, 0x0000, 0x0001); + ARK3116_SND(serial, 11, 0xFE, 0x40, 0x0000, 0x0003); /* <-- seq12 */ - ARK3116_RCV(serial,12,0xFE,0xC0,0x0000,0x0004, 0x00, buf); - ARK3116_SND(serial,13,0xFE,0x40,0x0000,0x0004); + ARK3116_RCV(serial, 12, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf); + ARK3116_SND(serial, 13, 0xFE, 0x40, 0x0000, 0x0004); /* 14 */ - ARK3116_RCV(serial,14,0xFE,0xC0,0x0000,0x0004, 0x00, buf); - ARK3116_SND(serial,15,0xFE,0x40,0x0000,0x0004); + ARK3116_RCV(serial, 14, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf); + ARK3116_SND(serial, 15, 0xFE, 0x40, 0x0000, 0x0004); /* 16 */ - ARK3116_RCV(serial,16,0xFE,0xC0,0x0000,0x0004, 0x00, buf); + ARK3116_RCV(serial, 16, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf); /* --> seq17 */ - ARK3116_SND(serial,17,0xFE,0x40,0x0001,0x0004); + ARK3116_SND(serial, 17, 0xFE, 0x40, 0x0001, 0x0004); /* <-- seq18 */ - ARK3116_RCV(serial,18,0xFE,0xC0,0x0000,0x0004, 0x01, buf); + ARK3116_RCV(serial, 18, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf); /* --> seq19 */ - ARK3116_SND(serial,19,0xFE,0x40,0x0003,0x0004); - + ARK3116_SND(serial, 19, 0xFE, 0x40, 0x0003, 0x0004); /* <-- seq20 */ - /* seems like serial port status info (RTS, CTS,...) */ - /* returns modem control line status ?! */ - ARK3116_RCV(serial,20,0xFE,0xC0,0x0000,0x0006, 0xFF, buf); - - /* set 9600 baud & do some init ?! */ - ARK3116_SND(serial,147,0xFE,0x40,0x0083,0x0003); - ARK3116_SND(serial,148,0xFE,0x40,0x0038,0x0000); - ARK3116_SND(serial,149,0xFE,0x40,0x0001,0x0001); - ARK3116_SND(serial,150,0xFE,0x40,0x0003,0x0003); - ARK3116_RCV(serial,151,0xFE,0xC0,0x0000,0x0004,0x03, buf); - ARK3116_SND(serial,152,0xFE,0x40,0x0000,0x0003); - ARK3116_RCV(serial,153,0xFE,0xC0,0x0000,0x0003,0x00, buf); - ARK3116_SND(serial,154,0xFE,0x40,0x0003,0x0003); + /* seems like serial port status info (RTS, CTS, ...) */ + /* returns modem control line status?! */ + ARK3116_RCV(serial, 20, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf); + + /* set 9600 baud & do some init?! */ + ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003); + ARK3116_SND(serial, 148, 0xFE, 0x40, 0x0038, 0x0000); + ARK3116_SND(serial, 149, 0xFE, 0x40, 0x0001, 0x0001); + ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003); + ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf); + ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003); + ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf); + ARK3116_SND(serial, 154, 0xFE, 0x40, 0x0003, 0x0003); kfree(buf); - return(0); + return 0; cleanup: - for (--i; i>=0; --i) + for (--i; i >= 0; --i) usb_set_serial_port_data(serial->port[i], NULL); return -ENOMEM; } @@ -180,7 +180,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + port->tty->termios->c_cflag = B9600 | CS8 + | CREAD | HUPCL | CLOCAL; priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); @@ -204,8 +205,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, } /* set data bit count (8/7/6/5) */ - if (cflag & CSIZE){ - switch (cflag & CSIZE){ + if (cflag & CSIZE) { + switch (cflag & CSIZE) { case CS5: config |= 0x00; dbg("setting CS5"); @@ -219,7 +220,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, dbg("setting CS7"); break; default: - err ("CSIZE was set but not CS5-CS8, using CS8!"); + err("CSIZE was set but not CS5-CS8, using CS8!"); + /* fall through */ case CS8: config |= 0x03; dbg("setting CS8"); @@ -227,8 +229,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, } } - /* set parity (NONE,EVEN,ODD) */ - if (cflag & PARENB){ + /* set parity (NONE/EVEN/ODD) */ + if (cflag & PARENB) { if (cflag & PARODD) { config |= 0x08; dbg("setting parity to ODD"); @@ -240,20 +242,19 @@ static void ark3116_set_termios(struct usb_serial_port *port, dbg("setting parity to NONE"); } - /* SET STOPBIT (1/2) */ + /* set stop bit (1/2) */ if (cflag & CSTOPB) { config |= 0x04; - dbg ("setting 2 stop bits"); + dbg("setting 2 stop bits"); } else { - dbg ("setting 1 stop bit"); + dbg("setting 1 stop bit"); } - - /* set baudrate: */ + /* set baudrate */ baud = 0; - switch (cflag & CBAUD){ + switch (cflag & CBAUD) { case B0: - err("can't set 0baud, using 9600 instead"); + err("can't set 0 baud, using 9600 instead"); break; case B75: baud = 75; break; case B150: baud = 150; break; @@ -285,38 +286,40 @@ static void ark3116_set_termios(struct usb_serial_port *port, */ if (baud == 460800) /* strange, for 460800 the formula is wrong - * (dont use round(), then 9600baud is wrong) */ + * if using round() then 9600baud is wrong) */ ark3116_baud = 7; else ark3116_baud = 3000000 / baud; /* ? */ - ARK3116_RCV(serial,0,0xFE,0xC0,0x0000,0x0003, 0x03, buf); + ARK3116_RCV(serial, 0, 0xFE, 0xC0, 0x0000, 0x0003, 0x03, buf); + /* offset = buf[0]; */ /* offset = 0x03; */ - /* dbg("using 0x%04X as target for 0x0003:",0x0080+offset); */ - + /* dbg("using 0x%04X as target for 0x0003:", 0x0080 + offset); */ /* set baudrate */ - dbg("setting baudrate to %d (->reg=%d)",baud,ark3116_baud); - ARK3116_SND(serial,147,0xFE,0x40,0x0083,0x0003); - ARK3116_SND(serial,148,0xFE,0x40,(ark3116_baud & 0x00FF) ,0x0000); - ARK3116_SND(serial,149,0xFE,0x40,(ark3116_baud & 0xFF00)>>8,0x0001); - ARK3116_SND(serial,150,0xFE,0x40,0x0003,0x0003); + dbg("setting baudrate to %d (->reg=%d)", baud, ark3116_baud); + ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003); + ARK3116_SND(serial, 148, 0xFE, 0x40, + (ark3116_baud & 0x00FF), 0x0000); + ARK3116_SND(serial, 149, 0xFE, 0x40, + (ark3116_baud & 0xFF00) >> 8, 0x0001); + ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003); /* ? */ - ARK3116_RCV(serial,151,0xFE,0xC0,0x0000,0x0004,0x03, buf); - ARK3116_SND(serial,152,0xFE,0x40,0x0000,0x0003); + ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf); + ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003); /* set data bit count, stop bit count & parity: */ dbg("updating bit count, stop bit or parity (cfg=0x%02X)", config); - ARK3116_RCV(serial,153,0xFE,0xC0,0x0000,0x0003,0x00, buf); - ARK3116_SND(serial,154,0xFE,0x40,config,0x0003); + ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf); + ARK3116_SND(serial, 154, 0xFE, 0x40, config, 0x0003); if (cflag & CRTSCTS) - dbg("CRTSCTS not supported by chipset ?!"); + dbg("CRTSCTS not supported by chipset?!"); - /* TEST ARK3116_SND(154,0xFE,0x40,0xFFFF, 0x0006); */ + /* TEST ARK3116_SND(154, 0xFE, 0x40, 0xFFFF, 0x0006); */ kfree(buf); return; @@ -329,11 +332,11 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) char *buf; int result = 0; - dbg("%s - port %d", __FUNCTION__, port->number); + dbg("%s - port %d", __FUNCTION__, port->number); buf = kmalloc(1, GFP_KERNEL); if (!buf) { - dbg("error kmalloc -> out of mem ?"); + dbg("error kmalloc -> out of mem?"); return -ENOMEM; } @@ -342,44 +345,68 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) return result; /* open */ - ARK3116_RCV(serial,111,0xFE,0xC0,0x0000,0x0003, 0x02, buf); + ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf); - ARK3116_SND(serial,112,0xFE,0x40,0x0082,0x0003); - ARK3116_SND(serial,113,0xFE,0x40,0x001A,0x0000); - ARK3116_SND(serial,114,0xFE,0x40,0x0000,0x0001); - ARK3116_SND(serial,115,0xFE,0x40,0x0002,0x0003); + ARK3116_SND(serial, 112, 0xFE, 0x40, 0x0082, 0x0003); + ARK3116_SND(serial, 113, 0xFE, 0x40, 0x001A, 0x0000); + ARK3116_SND(serial, 114, 0xFE, 0x40, 0x0000, 0x0001); + ARK3116_SND(serial, 115, 0xFE, 0x40, 0x0002, 0x0003); - ARK3116_RCV(serial,116,0xFE,0xC0,0x0000,0x0004, 0x03, buf); - ARK3116_SND(serial,117,0xFE,0x40,0x0002,0x0004); + ARK3116_RCV(serial, 116, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf); + ARK3116_SND(serial, 117, 0xFE, 0x40, 0x0002, 0x0004); - ARK3116_RCV(serial,118,0xFE,0xC0,0x0000,0x0004, 0x02, buf); - ARK3116_SND(serial,119,0xFE,0x40,0x0000,0x0004); + ARK3116_RCV(serial, 118, 0xFE, 0xC0, 0x0000, 0x0004, 0x02, buf); + ARK3116_SND(serial, 119, 0xFE, 0x40, 0x0000, 0x0004); - ARK3116_RCV(serial,120,0xFE,0xC0,0x0000,0x0004, 0x00, buf); + ARK3116_RCV(serial, 120, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf); - ARK3116_SND(serial,121,0xFE,0x40,0x0001,0x0004); + ARK3116_SND(serial, 121, 0xFE, 0x40, 0x0001, 0x0004); - ARK3116_RCV(serial,122,0xFE,0xC0,0x0000,0x0004, 0x01, buf); + ARK3116_RCV(serial, 122, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf); - ARK3116_SND(serial,123,0xFE,0x40,0x0003,0x0004); + ARK3116_SND(serial, 123, 0xFE, 0x40, 0x0003, 0x0004); - /* returns different values (control lines ?!) */ - ARK3116_RCV(serial,124,0xFE,0xC0,0x0000,0x0006, 0xFF, buf); + /* returns different values (control lines?!) */ + ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf); - /* initialise termios: */ + /* initialise termios */ if (port->tty) ark3116_set_termios(port, &tmp_termios); kfree(buf); return result; - } static int ark3116_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) { - dbg("ioctl not supported yet..."); + struct serial_struct serstruct; + void __user *user_arg = (void __user *)arg; + + switch (cmd) { + case TIOCGSERIAL: + /* XXX: Some of these values are probably wrong. */ + memset(&serstruct, 0, sizeof (serstruct)); + serstruct.type = PORT_16654; + serstruct.line = port->serial->minor; + serstruct.port = port->number; + serstruct.custom_divisor = 0; + serstruct.baud_base = 460800; + + if (copy_to_user(user_arg, &serstruct, sizeof (serstruct))) + return -EFAULT; + + return 0; + case TIOCSSERIAL: + if (copy_from_user(&serstruct, user_arg, sizeof (serstruct))) + return -EFAULT; + return 0; + default: + dbg("%s cmd 0x%04x not supported", __FUNCTION__, cmd); + break; + } + return -ENOIOCTLCMD; } @@ -389,7 +416,7 @@ static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file) char *buf; char temp; - /* seems like serial port status info (RTS, CTS,...) is stored + /* seems like serial port status info (RTS, CTS, ...) is stored * in reg(?) 0x0006 * pcb connection point 11 = GND -> sets bit4 of response * pcb connection point 7 = GND -> sets bit6 of response @@ -401,16 +428,16 @@ static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file) return -ENOMEM; } - /* read register: */ - ARK3116_RCV_QUIET(serial,0xFE,0xC0,0x0000,0x0006,buf); + /* read register */ + ARK3116_RCV_QUIET(serial, 0xFE, 0xC0, 0x0000, 0x0006, buf); temp = buf[0]; kfree(buf); - /* i do not really know if bit4=CTS and bit6=DSR... was just a - * quick guess !! + /* i do not really know if bit4=CTS and bit6=DSR... just a + * quick guess! */ - return (temp & (1<<4) ? TIOCM_CTS : 0) | - (temp & (1<<6) ? TIOCM_DSR : 0); + return (temp & (1<<4) ? TIOCM_CTS : 0) + | (temp & (1<<6) ? TIOCM_DSR : 0); } static struct usb_driver ark3116_driver = { diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 4144777ea18b..70ece9e01ce4 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -63,7 +63,6 @@ * UsbSnoop on Windows2000 and from examining the other USB drivers. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -75,7 +74,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "belkin_sa.h" static int debug; diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index e9f9f4bafa17..6542f220468f 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -8,13 +8,12 @@ * 2 as published by the Free Software Foundation. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> static int usb_serial_device_match (struct device *dev, struct device_driver *drv) { diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index f3404e10afb4..3a9073dbfe6a 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -11,18 +11,16 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/console.h> #include <linux/usb.h> +#include <linux/usb/serial.h> static int debug; -#include "usb-serial.h" - struct usbcons_info { int magic; int break_flag; diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index f8c0cb287736..486c7411b9a7 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -17,7 +17,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> @@ -27,7 +26,7 @@ #include <linux/moduleparam.h> #include <linux/usb.h> #include <asm/uaccess.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> /* * Version Information diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 1fd5c5a9f2ef..d954ec34b018 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -28,7 +28,6 @@ */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -40,7 +39,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #define CYBERJACK_LOCAL_BUF_SIZE 32 @@ -215,14 +214,14 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b return (0); } - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); spin_lock_irqsave(&priv->lock, flags); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 5de76efe1b37..e1173c1aee37 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -48,7 +48,6 @@ /* Code originates and was built up from ftdi_sio, belkin, pl2303 and others. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -60,11 +59,11 @@ #include <linux/moduleparam.h> #include <linux/spinlock.h> #include <linux/usb.h> +#include <linux/usb/serial.h> #include <linux/serial.h> #include <linux/delay.h> #include <asm/uaccess.h> -#include "usb-serial.h" #include "cypress_m8.h" @@ -130,6 +129,9 @@ struct cypress_private { int cmd_ctrl; /* always set this to 1 before issuing a command */ struct cypress_buf *buf; /* write buffer */ int write_urb_in_use; /* write urb in use indicator */ + int write_urb_interval; /* interval to use for write urb */ + int read_urb_interval; /* interval to use for read urb */ + int comm_is_ok; /* true if communication is (still) ok */ int termios_initialized; __u8 line_control; /* holds dtr / rts value */ __u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */ @@ -169,6 +171,7 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, static int cypress_chars_in_buffer (struct usb_serial_port *port); static void cypress_throttle (struct usb_serial_port *port); static void cypress_unthrottle (struct usb_serial_port *port); +static void cypress_set_dead (struct usb_serial_port *port); static void cypress_read_int_callback (struct urb *urb, struct pt_regs *regs); static void cypress_write_int_callback (struct urb *urb, struct pt_regs *regs); /* baud helper functions */ @@ -289,6 +292,9 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m priv = usb_get_serial_port_data(port); + if (!priv->comm_is_ok) + return -ENODEV; + switch(cypress_request_type) { case CYPRESS_SET_CONFIG: @@ -366,13 +372,12 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m if (tries++ >= 3) break; - if (retval == EPIPE) - usb_clear_halt(port->serial->dev, 0x00); - } while (retval != 8 && retval != ENODEV); + } while (retval != 8 && retval != -ENODEV); - if (retval != 8) + if (retval != 8) { err("%s - failed sending serial line settings - %d", __FUNCTION__, retval); - else { + cypress_set_dead(port); + } else { spin_lock_irqsave(&priv->lock, flags); priv->baud_rate = new_baudrate; priv->cbr_mask = baud_mask; @@ -393,12 +398,11 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m if (tries++ >= 3) break; - if (retval == EPIPE) - usb_clear_halt(port->serial->dev, 0x00); - } while (retval != 5 && retval != ENODEV); + } while (retval != 5 && retval != -ENODEV); if (retval != 5) { err("%s - failed to retrieve serial line settings - %d", __FUNCTION__, retval); + cypress_set_dead(port); return retval; } else { spin_lock_irqsave(&priv->lock, flags); @@ -420,6 +424,24 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m } /* cypress_serial_control */ +static void cypress_set_dead(struct usb_serial_port *port) +{ + struct cypress_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + if (!priv->comm_is_ok) { + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + priv->comm_is_ok = 0; + spin_unlock_irqrestore(&priv->lock, flags); + + err("cypress_m8 suspending failing port %d - interval might be too short", + port->number); +} + + /* given a baud mask, it will return integer baud on success */ static int mask_to_rate (unsigned mask) { @@ -473,13 +495,15 @@ static unsigned rate_to_mask (int rate) static int generic_startup (struct usb_serial *serial) { struct cypress_private *priv; + struct usb_serial_port *port = serial->port[0]; - dbg("%s - port %d", __FUNCTION__, serial->port[0]->number); + dbg("%s - port %d", __FUNCTION__, port->number); priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->comm_is_ok = !0; spin_lock_init(&priv->lock); priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); if (priv->buf == NULL) { @@ -490,13 +514,24 @@ static int generic_startup (struct usb_serial *serial) usb_reset_configuration (serial->dev); - interval = 1; priv->cmd_ctrl = 0; priv->line_control = 0; priv->termios_initialized = 0; priv->rx_flags = 0; priv->cbr_mask = B300; - usb_set_serial_port_data(serial->port[0], priv); + if (interval > 0) { + priv->write_urb_interval = interval; + priv->read_urb_interval = interval; + dbg("%s - port %d read & write intervals forced to %d", + __FUNCTION__,port->number,interval); + } else { + priv->write_urb_interval = port->interrupt_out_urb->interval; + priv->read_urb_interval = port->interrupt_in_urb->interval; + dbg("%s - port %d intervals: read=%d write=%d", + __FUNCTION__,port->number, + priv->read_urb_interval,priv->write_urb_interval); + } + usb_set_serial_port_data(port, priv); return 0; } @@ -586,6 +621,9 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __FUNCTION__, port->number); + if (!priv->comm_is_ok) + return -EIO; + /* clear halts before open */ usb_clear_halt(serial->dev, 0x81); usb_clear_halt(serial->dev, 0x02); @@ -625,11 +663,12 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) usb_fill_int_urb(port->interrupt_in_urb, serial->dev, usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress), port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length, - cypress_read_int_callback, port, interval); + cypress_read_int_callback, port, priv->read_urb_interval); result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result){ dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result); + cypress_set_dead(port); } return result; @@ -734,6 +773,9 @@ static void cypress_send(struct usb_serial_port *port) struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; + if (!priv->comm_is_ok) + return; + dbg("%s - port %d", __FUNCTION__, port->number); dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size); @@ -807,14 +849,16 @@ send: usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size, port->interrupt_out_urb->transfer_buffer); - port->interrupt_out_urb->transfer_buffer_length = actual_size; - port->interrupt_out_urb->dev = port->serial->dev; - port->interrupt_out_urb->interval = interval; + usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev, + usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress), + port->interrupt_out_buffer, port->interrupt_out_size, + cypress_write_int_callback, port, priv->write_urb_interval); result = usb_submit_urb (port->interrupt_out_urb, GFP_ATOMIC); if (result) { dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); priv->write_urb_in_use = 0; + cypress_set_dead(port); } spin_lock_irqsave(&priv->lock, flags); @@ -1215,13 +1259,18 @@ static void cypress_unthrottle (struct usb_serial_port *port) priv->rx_flags = 0; spin_unlock_irqrestore(&priv->lock, flags); + if (!priv->comm_is_ok) + return; + if (actually_throttled) { port->interrupt_in_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); - if (result) + if (result) { dev_err(&port->dev, "%s - failed submitting read urb, " "error %d\n", __FUNCTION__, result); + cypress_set_dead(port); + } } } @@ -1241,9 +1290,22 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs) dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - nonzero read status received: %d", __FUNCTION__, - urb->status); + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* precursor to disconnect so just go away */ + return; + case -EPIPE: + usb_clear_halt(port->serial->dev,0x81); + break; + default: + /* something ugly is going on... */ + dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n", + __FUNCTION__,urb->status); + cypress_set_dead(port); return; } @@ -1344,18 +1406,20 @@ continue_read: /* Continue trying to always read... unless the port has closed. */ - if (port->open_count > 0) { + if (port->open_count > 0 && priv->comm_is_ok) { usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress), port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length, - cypress_read_int_callback, port, interval); + cypress_read_int_callback, port, priv->read_urb_interval); result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); - if (result) + if (result) { dev_err(&urb->dev->dev, "%s - failed resubmitting " "read urb, error %d\n", __FUNCTION__, result); + cypress_set_dead(port); + } } return; @@ -1381,20 +1445,26 @@ static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs) dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); priv->write_urb_in_use = 0; return; - case -EPIPE: /* no break needed */ + case -EPIPE: /* no break needed; clear halt and resubmit */ + if (!priv->comm_is_ok) + break; usb_clear_halt(port->serial->dev, 0x02); - default: /* error in the urb, so we have to resubmit it */ - dbg("%s - Overflow in write", __FUNCTION__); dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); port->interrupt_out_urb->transfer_buffer_length = 1; port->interrupt_out_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); - if (result) - dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", - __FUNCTION__, result); - else + if (!result) return; + dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", + __FUNCTION__, result); + cypress_set_dead(port); + break; + default: + dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n", + __FUNCTION__,urb->status); + cypress_set_dead(port); + break; } priv->write_urb_in_use = 0; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index b3f776a90c93..9b225183fc7a 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -233,7 +233,6 @@ * $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $ */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -247,7 +246,7 @@ #include <asm/uaccess.h> #include <linux/usb.h> #include <linux/wait.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> /* Defines */ diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index afca1eae5fb5..daafe405d86d 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -51,7 +51,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -63,7 +62,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> static int debug; diff --git a/drivers/usb/serial/ezusb.c b/drivers/usb/serial/ezusb.c index 1e419c8d7392..5169c2d154ab 100644 --- a/drivers/usb/serial/ezusb.c +++ b/drivers/usb/serial/ezusb.c @@ -8,7 +8,6 @@ * 2 as published by the Free Software Foundation. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -16,7 +15,7 @@ #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */ #define CPUCS_REG 0x7F92 diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index b2bfea7c815a..e774a27c6c98 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -245,7 +245,6 @@ /* to talk to the device */ /* Thanx to gkh and the rest of the usb dev group for all code I have assimilated :-) */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -258,7 +257,7 @@ #include <asm/uaccess.h> #include <linux/usb.h> #include <linux/serial.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "ftdi_sio.h" /* @@ -307,6 +306,8 @@ static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = { static struct usb_device_id id_table_combined [] = { + { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) }, @@ -314,6 +315,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, @@ -337,10 +339,12 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) }, @@ -501,6 +505,12 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACG_HFDUAL_PID) }, { USB_DEVICE(FTDI_VID, FTDI_YEI_SERVOCENTER31_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) }, + { USB_DEVICE(TESTO_VID, TESTO_USB_INTERFACE_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; @@ -549,11 +559,17 @@ struct ftdi_private { spinlock_t rx_lock; /* spinlock for receive state */ struct work_struct rx_work; int rx_processed; + unsigned long rx_bytes; __u16 interface; /* FT2232C port interface (0 for FT232/245) */ int force_baud; /* if non-zero, force the baud rate to this value */ int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */ + + spinlock_t tx_lock; /* spinlock for transmit state */ + unsigned long tx_bytes; + unsigned long tx_outstanding_bytes; + unsigned long tx_outstanding_urbs; }; /* Used for TIOCMIWAIT */ @@ -627,6 +643,9 @@ static struct usb_serial_driver ftdi_sio_device = { #define HIGH 1 #define LOW 0 +/* number of outstanding urbs to prevent userspace DoS from happening */ +#define URB_UPPER_LIMIT 42 + /* * *************************************************************************** * Utlity functions @@ -1086,25 +1105,29 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer); static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char); -static void create_sysfs_attrs(struct usb_serial *serial) -{ +static int create_sysfs_attrs(struct usb_serial *serial) +{ struct ftdi_private *priv; struct usb_device *udev; + int retval = 0; dbg("%s",__FUNCTION__); - + priv = usb_get_serial_port_data(serial->port[0]); udev = serial->dev; - + /* XXX I've no idea if the original SIO supports the event_char * sysfs parameter, so I'm playing it safe. */ if (priv->chip_type != SIO) { dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]); - device_create_file(&udev->dev, &dev_attr_event_char); - if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) { - device_create_file(&udev->dev, &dev_attr_latency_timer); + retval = device_create_file(&udev->dev, &dev_attr_event_char); + if ((!retval) && + (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) { + retval = device_create_file(&udev->dev, + &dev_attr_latency_timer); } } + return retval; } static void remove_sysfs_attrs(struct usb_serial *serial) @@ -1147,7 +1170,8 @@ static int ftdi_sio_attach (struct usb_serial *serial) struct usb_serial_port *port = serial->port[0]; struct ftdi_private *priv; struct ftdi_sio_quirk *quirk; - + int retval; + dbg("%s",__FUNCTION__); priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL); @@ -1157,6 +1181,7 @@ static int ftdi_sio_attach (struct usb_serial *serial) } spin_lock_init(&priv->rx_lock); + spin_lock_init(&priv->tx_lock); init_waitqueue_head(&priv->delta_msr_wait); /* This will push the characters through immediately rather than queue a task to deliver them */ @@ -1187,15 +1212,18 @@ static int ftdi_sio_attach (struct usb_serial *serial) usb_set_serial_port_data(serial->port[0], priv); ftdi_determine_type (serial->port[0]); - create_sysfs_attrs(serial); + retval = create_sysfs_attrs(serial); + if (retval) + dev_err(&serial->dev->dev, "Error creating sysfs files, " + "continuing\n"); /* Check for device requiring special set up. */ quirk = (struct ftdi_sio_quirk *)usb_get_serial_data(serial); if (quirk && quirk->setup) { quirk->setup(serial); } - - return (0); + + return 0; } /* ftdi_sio_attach */ @@ -1271,6 +1299,13 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) dbg("%s", __FUNCTION__); + spin_lock_irqsave(&priv->tx_lock, flags); + priv->tx_bytes = 0; + spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_lock_irqsave(&priv->rx_lock, flags); + priv->rx_bytes = 0; + spin_unlock_irqrestore(&priv->rx_lock, flags); + if (port->tty) port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; @@ -1373,6 +1408,7 @@ static int ftdi_write (struct usb_serial_port *port, int data_offset ; /* will be 1 for the SIO and 0 otherwise */ int status; int transfer_size; + unsigned long flags; dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count); @@ -1380,6 +1416,13 @@ static int ftdi_write (struct usb_serial_port *port, dbg("write request of 0 bytes"); return 0; } + spin_lock_irqsave(&priv->tx_lock, flags); + if (priv->tx_outstanding_urbs > URB_UPPER_LIMIT) { + spin_unlock_irqrestore(&priv->tx_lock, flags); + dbg("%s - write limit hit\n", __FUNCTION__); + return 0; + } + spin_unlock_irqrestore(&priv->tx_lock, flags); data_offset = priv->write_offset; dbg("data_offset set to %d",data_offset); @@ -1446,6 +1489,12 @@ static int ftdi_write (struct usb_serial_port *port, err("%s - failed submitting write urb, error %d", __FUNCTION__, status); count = status; kfree (buffer); + } else { + spin_lock_irqsave(&priv->tx_lock, flags); + ++priv->tx_outstanding_urbs; + priv->tx_outstanding_bytes += count; + priv->tx_bytes += count; + spin_unlock_irqrestore(&priv->tx_lock, flags); } /* we are done with this urb, so let the host driver @@ -1461,7 +1510,11 @@ static int ftdi_write (struct usb_serial_port *port, static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { + unsigned long flags; struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct ftdi_private *priv; + int data_offset; /* will be 1 for the SIO and 0 otherwise */ + unsigned long countback; /* free up the transfer buffer, as usb_free_urb() does not do this */ kfree (urb->transfer_buffer); @@ -1473,34 +1526,67 @@ static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs) return; } + priv = usb_get_serial_port_data(port); + if (!priv) { + dbg("%s - bad port private data pointer - exiting", __FUNCTION__); + return; + } + /* account for transferred data */ + countback = urb->actual_length; + data_offset = priv->write_offset; + if (data_offset > 0) { + /* Subtract the control bytes */ + countback -= (data_offset * ((countback + (PKTSZ - 1)) / PKTSZ)); + } + spin_lock_irqsave(&priv->tx_lock, flags); + --priv->tx_outstanding_urbs; + priv->tx_outstanding_bytes -= countback; + spin_unlock_irqrestore(&priv->tx_lock, flags); + usb_serial_port_softint(port); } /* ftdi_write_bulk_callback */ static int ftdi_write_room( struct usb_serial_port *port ) { + struct ftdi_private *priv = usb_get_serial_port_data(port); + int room; + unsigned long flags; + dbg("%s - port %d", __FUNCTION__, port->number); - /* - * We really can take anything the user throws at us - * but let's pick a nice big number to tell the tty - * layer that we have lots of free space - */ - return 2048; + spin_lock_irqsave(&priv->tx_lock, flags); + if (priv->tx_outstanding_urbs < URB_UPPER_LIMIT) { + /* + * We really can take anything the user throws at us + * but let's pick a nice big number to tell the tty + * layer that we have lots of free space + */ + room = 2048; + } else { + room = 0; + } + spin_unlock_irqrestore(&priv->tx_lock, flags); + return room; } /* ftdi_write_room */ static int ftdi_chars_in_buffer (struct usb_serial_port *port) { /* ftdi_chars_in_buffer */ + struct ftdi_private *priv = usb_get_serial_port_data(port); + int buffered; + unsigned long flags; + dbg("%s - port %d", __FUNCTION__, port->number); - /* - * We can't really account for how much data we - * have sent out, but hasn't made it through to the - * device, so just tell the tty layer that everything - * is flushed. - */ - return 0; + spin_lock_irqsave(&priv->tx_lock, flags); + buffered = (int)priv->tx_outstanding_bytes; + spin_unlock_irqrestore(&priv->tx_lock, flags); + if (buffered < 0) { + err("%s outstanding tx bytes is negative!", __FUNCTION__); + buffered = 0; + } + return buffered; } /* ftdi_chars_in_buffer */ @@ -1510,6 +1596,8 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct tty_struct *tty; struct ftdi_private *priv; + unsigned long countread; + unsigned long flags; if (urb->number_of_packets > 0) { err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__, @@ -1544,6 +1632,13 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs) return; } + /* count data bytes, but not status bytes */ + countread = urb->actual_length; + countread -= 2 * ((countread + (PKTSZ - 1)) / PKTSZ); + spin_lock_irqsave(&priv->rx_lock, flags); + priv->rx_bytes += countread; + spin_unlock_irqrestore(&priv->rx_lock, flags); + ftdi_process_read(port); } /* ftdi_read_bulk_callback */ diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 6ab2ac845bd7..f0edb87d2dd5 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -32,10 +32,19 @@ #define FTDI_NF_RIC_PID 0x0001 /* Product Id */ +/* www.canusb.com Lawicel CANUSB device */ +#define FTDI_CANUSB_PID 0xFFA8 /* Product Id */ + +/* AlphaMicro Components AMC-232USB01 device */ +#define FTDI_AMC232_PID 0xFF00 /* Product Id */ + /* ACT Solutions HomePro ZWave interface (http://www.act-solutions.com/HomePro.htm) */ #define FTDI_ACTZWAVE_PID 0xF2D0 +/* www.starting-point-systems.com µChameleon device */ +#define FTDI_MICRO_CHAMELEON_PID 0xCAA0 /* Product Id */ + /* www.irtrans.de device */ #define FTDI_IRTRANS_PID 0xFC60 /* Product Id */ @@ -102,6 +111,7 @@ #define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */ #define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */ #define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */ +#define SEALEVEL_2106_PID 0x9020 /* SeaLINK+422 (2106) */ #define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */ #define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */ #define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */ @@ -179,6 +189,10 @@ /* http://home.earthlink.net/~jrhees/USBUIRT/index.htm */ #define FTDI_USB_UIRT_PID 0xF850 /* Product Id */ +/* TNC-X USB-to-packet-radio adapter, versions prior to 3.0 (DLP module) */ + +#define FTDI_TNC_X_PID 0xEBE0 + /* * ELV USB devices submitted by Christian Abt of ELV (www.elv.de). * All of these devices use FTDI's vendor ID (0x0403). @@ -442,6 +456,32 @@ */ #define FTDI_YEI_SERVOCENTER31_PID 0xE050 /* YEI ServoCenter3.1 USB */ +/* + * ThorLabs USB motor drivers + */ +#define FTDI_THORLABS_PID 0xfaf0 /* ThorLabs USB motor drivers */ + +/* + * Testo products (http://www.testo.com/) + * Submitted by Colin Leroy + */ +#define TESTO_VID 0x128D +#define TESTO_USB_INTERFACE_PID 0x0001 + +/* + * Gamma Scout (http://gamma-scout.com/). Submitted by rsc@runtux.com. + */ +#define FTDI_GAMMA_SCOUT_PID 0xD678 /* Gamma Scout online */ + +/* + * Tactrix OpenPort (ECU) devices. + * OpenPort 1.3M submitted by Donour Sizemore. + * OpenPort 1.3S and 1.3U submitted by Ian Abbott. + */ +#define FTDI_TACTRIX_OPENPORT_13M_PID 0xCC48 /* OpenPort 1.3 Mitsubishi */ +#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */ +#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff --git a/drivers/usb/serial/funsoft.c b/drivers/usb/serial/funsoft.c index 803721b97e2e..77b977206a8c 100644 --- a/drivers/usb/serial/funsoft.c +++ b/drivers/usb/serial/funsoft.c @@ -13,7 +13,7 @@ #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> static struct usb_device_id id_table [] = { { USB_DEVICE(0x1404, 0xcddc) }, diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 04767759cf8a..4b1196a8b09e 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1,7 +1,7 @@ /* * Garmin GPS driver * - * Copyright (C) 2004 Hermann Kneissel herkne@users.sourceforge.net + * Copyright (C) 2006 Hermann Kneissel herkne@users.sourceforge.net * * The latest version of the driver can be found at * http://sourceforge.net/projects/garmin-gps/ @@ -23,7 +23,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -36,6 +35,9 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> +#include <linux/usb/serial.h> + +#include <linux/version.h> /* the mode to be set when the port ist opened */ static int initial_mode = 1; @@ -43,8 +45,6 @@ static int initial_mode = 1; /* debug flag */ static int debug = 0; -#include "usb-serial.h" - #define GARMIN_VENDOR_ID 0x091E /* @@ -52,7 +52,7 @@ static int debug = 0; */ #define VERSION_MAJOR 0 -#define VERSION_MINOR 23 +#define VERSION_MINOR 28 #define _STR(s) #s #define _DRIVER_VERSION(a,b) "v" _STR(a) "." _STR(b) @@ -166,7 +166,8 @@ struct garmin_data { #define FLAGS_SESSION_REPLY1_SEEN 0x0080 #define FLAGS_SESSION_REPLY2_SEEN 0x0040 #define FLAGS_BULK_IN_ACTIVE 0x0020 -#define FLAGS_THROTTLED 0x0010 +#define FLAGS_BULK_IN_RESTART 0x0010 +#define FLAGS_THROTTLED 0x0008 #define CLEAR_HALT_REQUIRED 0x0001 #define FLAGS_QUEUING 0x0100 @@ -226,7 +227,7 @@ static struct usb_driver garmin_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, - .no_dynamic_id = 1, + .no_dynamic_id = 1, }; @@ -272,7 +273,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf) static void send_to_tty(struct usb_serial_port *port, - char *data, unsigned int actual_length) + char *data, unsigned int actual_length) { struct tty_struct *tty = port->tty; @@ -296,15 +297,15 @@ static void send_to_tty(struct usb_serial_port *port, * queue a received (usb-)packet for later processing */ static int pkt_add(struct garmin_data * garmin_data_p, - unsigned char *data, unsigned int data_length) + unsigned char *data, unsigned int data_length) { + int state = 0; int result = 0; unsigned long flags; struct garmin_packet *pkt; /* process only packets containg data ... */ if (data_length) { - garmin_data_p->flags |= FLAGS_QUEUING; pkt = kmalloc(sizeof(struct garmin_packet)+data_length, GFP_ATOMIC); if (pkt == NULL) { @@ -315,14 +316,16 @@ static int pkt_add(struct garmin_data * garmin_data_p, memcpy(pkt->data, data, data_length); spin_lock_irqsave(&garmin_data_p->lock, flags); + garmin_data_p->flags |= FLAGS_QUEUING; result = list_empty(&garmin_data_p->pktlist); pkt->seq = garmin_data_p->seq_counter++; list_add_tail(&pkt->list, &garmin_data_p->pktlist); + state = garmin_data_p->state; spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* in serial mode, if someone is waiting for data from the device, iconvert and send the next packet to tty. */ - if (result && (garmin_data_p->state == STATE_GSP_WAIT_DATA)) { + if (result && (state == STATE_GSP_WAIT_DATA)) { gsp_next_packet(garmin_data_p); } } @@ -372,9 +375,9 @@ static void pkt_clear(struct garmin_data * garmin_data_p) static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id) { __u8 pkt[10]; - __u8 cksum = 0; - __u8 *ptr = pkt; - unsigned l = 0; + __u8 cksum = 0; + __u8 *ptr = pkt; + unsigned l = 0; dbg("%s - pkt-id: 0x%X.", __FUNCTION__, 0xFF & pkt_id); @@ -418,7 +421,7 @@ static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id) static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count) { const __u8* recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET; - __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer; + __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer; int cksum = 0; int n = 0; @@ -449,11 +452,11 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count) n++; } - if ((0xff & (cksum + *recpkt)) != 0) { - dbg("%s - invalid checksum, expected %02x, got %02x", - __FUNCTION__, 0xff & -cksum, 0xff & *recpkt); - return -EINVPKT; - } + if ((0xff & (cksum + *recpkt)) != 0) { + dbg("%s - invalid checksum, expected %02x, got %02x", + __FUNCTION__, 0xff & -cksum, 0xff & *recpkt); + return -EINVPKT; + } usbdata[0] = __cpu_to_le32(GARMIN_LAYERID_APPL); usbdata[1] = __cpu_to_le32(pktid); @@ -493,20 +496,28 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count) */ static int gsp_receive(struct garmin_data * garmin_data_p, - const unsigned char *buf, int count) + const unsigned char *buf, int count) { + unsigned long flags; int offs = 0; int ack_or_nak_seen = 0; int i = 0; - __u8 *dest = garmin_data_p->inbuffer; - int size = garmin_data_p->insize; + __u8 *dest; + int size; // dleSeen: set if last byte read was a DLE - int dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN; + int dleSeen; // skip: if set, skip incoming data until possible start of // new packet - int skip = garmin_data_p->flags & FLAGS_GSP_SKIP; + int skip; __u8 data; + spin_lock_irqsave(&garmin_data_p->lock, flags); + dest = garmin_data_p->inbuffer; + size = garmin_data_p->insize; + dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN; + skip = garmin_data_p->flags & FLAGS_GSP_SKIP; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); + dbg("%s - dle=%d skip=%d size=%d count=%d", __FUNCTION__, dleSeen, skip, size, count); @@ -574,6 +585,8 @@ static int gsp_receive(struct garmin_data * garmin_data_p, } } + spin_lock_irqsave(&garmin_data_p->lock, flags); + garmin_data_p->insize = size; // copy flags back to structure @@ -589,6 +602,11 @@ static int gsp_receive(struct garmin_data * garmin_data_p, if (ack_or_nak_seen) { garmin_data_p->state = STATE_GSP_WAIT_DATA; + } + + spin_unlock_irqrestore(&garmin_data_p->lock, flags); + + if (ack_or_nak_seen) { gsp_next_packet(garmin_data_p); } @@ -678,7 +696,7 @@ static int gsp_send(struct garmin_data * garmin_data_p, src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH; if (k > (GARMIN_PKTHDR_LENGTH-2)) { /* can't add stuffing DLEs in place, move data to end - of buffer ... */ + of buffer ... */ dst = garmin_data_p->outbuffer+GPS_OUT_BUFSIZ-datalen; memcpy(dst, src, datalen); src = dst; @@ -757,8 +775,9 @@ static void gsp_next_packet(struct garmin_data * garmin_data_p) * or even incomplete packets */ static int nat_receive(struct garmin_data * garmin_data_p, - const unsigned char *buf, int count) + const unsigned char *buf, int count) { + unsigned long flags; __u8 * dest; int offs = 0; int result = count; @@ -805,7 +824,9 @@ static int nat_receive(struct garmin_data * garmin_data_p, /* if this was an abort-transfer command, flush all queued data. */ if (isAbortTrfCmnd(garmin_data_p->inbuffer)) { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_DROP_DATA; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); pkt_clear(garmin_data_p); } } @@ -841,12 +862,15 @@ static void priv_status_resp(struct usb_serial_port *port) static int process_resetdev_request(struct usb_serial_port *port) { + unsigned long flags; int status; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags &= ~(CLEAR_HALT_REQUIRED); garmin_data_p->state = STATE_RESET; garmin_data_p->serial_num = 0; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); usb_kill_urb (port->interrupt_in_urb); dbg("%s - usb_reset_device", __FUNCTION__ ); @@ -864,6 +888,7 @@ static int process_resetdev_request(struct usb_serial_port *port) */ static int garmin_clear(struct garmin_data * garmin_data_p) { + unsigned long flags; int status = 0; struct usb_serial_port *port = garmin_data_p->port; @@ -877,8 +902,10 @@ static int garmin_clear(struct garmin_data * garmin_data_p) /* flush all queued data */ pkt_clear(garmin_data_p); + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->insize = 0; garmin_data_p->outsize = 0; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); return status; } @@ -890,6 +917,7 @@ static int garmin_clear(struct garmin_data * garmin_data_p) static int garmin_init_session(struct usb_serial_port *port) { + unsigned long flags; struct usb_serial *serial = port->serial; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); int status = 0; @@ -915,7 +943,9 @@ static int garmin_init_session(struct usb_serial_port *port) if (status >= 0) { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->ignorePkts++; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* not needed, but the win32 driver does it too ... */ status = garmin_write_bulk(port, @@ -923,7 +953,9 @@ static int garmin_init_session(struct usb_serial_port *port) sizeof(GARMIN_START_SESSION_REQ2)); if (status >= 0) { status = 0; + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->ignorePkts++; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); } } } @@ -937,6 +969,7 @@ static int garmin_init_session(struct usb_serial_port *port) static int garmin_open (struct usb_serial_port *port, struct file *filp) { + unsigned long flags; int status = 0; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -950,9 +983,11 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp) if (port->tty) port->tty->low_latency = 1; + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->mode = initial_mode; garmin_data_p->count = 0; garmin_data_p->flags = 0; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* shutdown any bulk reads that might be going on */ usb_kill_urb (port->write_urb); @@ -998,6 +1033,7 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp) static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { + unsigned long flags; struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -1009,7 +1045,9 @@ static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs) if (urb->status) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= CLEAR_HALT_REQUIRED; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); } usb_serial_port_softint(port); @@ -1017,8 +1055,9 @@ static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs) static int garmin_write_bulk (struct usb_serial_port *port, - const unsigned char *buf, int count) + const unsigned char *buf, int count) { + unsigned long flags; struct usb_serial *serial = port->serial; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); struct urb *urb; @@ -1028,7 +1067,9 @@ static int garmin_write_bulk (struct usb_serial_port *port, dbg("%s - port %d, state %d", __FUNCTION__, port->number, garmin_data_p->state); + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags &= ~FLAGS_DROP_DATA; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); buffer = kmalloc (count, GFP_ATOMIC); if (!buffer) { @@ -1055,7 +1096,9 @@ static int garmin_write_bulk (struct usb_serial_port *port, urb->transfer_flags |= URB_ZERO_PACKET; if (GARMIN_LAYERID_APPL == getLayerId(buffer)) { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_APP_REQ_SEEN; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); if (garmin_data_p->mode == MODE_GARMIN_SERIAL) { pkt_clear(garmin_data_p); garmin_data_p->state = STATE_GSP_WAIT_DATA; @@ -1089,8 +1132,9 @@ static int garmin_write_bulk (struct usb_serial_port *port, static int garmin_write (struct usb_serial_port *port, - const unsigned char *buf, int count) + const unsigned char *buf, int count) { + unsigned long flags; int pktid, pktsiz, len; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); __le32 *privpkt = (__le32 *)garmin_data_p->privpkt; @@ -1141,7 +1185,9 @@ static int garmin_write (struct usb_serial_port *port, break; case PRIV_PKTID_RESET_REQ: + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_APP_REQ_SEEN; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); break; case PRIV_PKTID_SET_DEF_MODE: @@ -1157,6 +1203,8 @@ static int garmin_write (struct usb_serial_port *port, } } + garmin_data_p->ignorePkts = 0; + if (garmin_data_p->mode == MODE_GARMIN_SERIAL) { return gsp_receive(garmin_data_p, buf, count); } else { /* MODE_NATIVE */ @@ -1179,10 +1227,10 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port) { /* * Report back the number of bytes currently in our input buffer. - * Will this lock up the driver - the buffer contains an incomplete - * package which will not be written to the device until it - * has been completed ? - */ + * Will this lock up the driver - the buffer contains an incomplete + * package which will not be written to the device until it + * has been completed ? + */ //struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); //return garmin_data_p->insize; return 0; @@ -1192,6 +1240,8 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port) static void garmin_read_process(struct garmin_data * garmin_data_p, unsigned char *data, unsigned data_length) { + unsigned long flags; + if (garmin_data_p->flags & FLAGS_DROP_DATA) { /* abort-transfer cmd is actice */ dbg("%s - pkt dropped", __FUNCTION__); @@ -1202,11 +1252,14 @@ static void garmin_read_process(struct garmin_data * garmin_data_p, if a reset is required or not when closing the device */ if (0 == memcmp(data, GARMIN_APP_LAYER_REPLY, - sizeof(GARMIN_APP_LAYER_REPLY))) + sizeof(GARMIN_APP_LAYER_REPLY))) { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_APP_RESP_SEEN; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); + } /* if throttling is active or postprecessing is required - put the received data in th input queue, otherwise + put the received data in the input queue, otherwise send it directly to the tty port */ if (garmin_data_p->flags & FLAGS_QUEUING) { pkt_add(garmin_data_p, data, data_length); @@ -1223,6 +1276,7 @@ static void garmin_read_process(struct garmin_data * garmin_data_p, static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs) { + unsigned long flags; struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -1247,19 +1301,30 @@ static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs) garmin_read_process(garmin_data_p, data, urb->actual_length); - /* Continue trying to read until nothing more is received */ - if (urb->actual_length > 0) { - usb_fill_bulk_urb (port->read_urb, serial->dev, - usb_rcvbulkpipe (serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - garmin_read_bulk_callback, port); + if (urb->actual_length == 0 && + 0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) { + spin_lock_irqsave(&garmin_data_p->lock, flags); + garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); status = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (status) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, status); + } else if (urb->actual_length > 0) { + /* Continue trying to read until nothing more is received */ + if (0 == (garmin_data_p->flags & FLAGS_THROTTLED)) { + status = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (status) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __FUNCTION__, status); + } + } else { + dbg("%s - end of bulk data", __FUNCTION__); + spin_lock_irqsave(&garmin_data_p->lock, flags); + garmin_data_p->flags &= ~FLAGS_BULK_IN_ACTIVE; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); } return; } @@ -1267,6 +1332,7 @@ static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs) static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs) { + unsigned long flags; int status; struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; @@ -1299,25 +1365,41 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs) dbg("%s - bulk data available.", __FUNCTION__); - /* bulk data available */ - usb_fill_bulk_urb (port->read_urb, serial->dev, - usb_rcvbulkpipe (serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - garmin_read_bulk_callback, port); - status = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (status) { - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __FUNCTION__, status); + if (0 == (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) { + + /* bulk data available */ + usb_fill_bulk_urb (port->read_urb, serial->dev, + usb_rcvbulkpipe (serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + garmin_read_bulk_callback, port); + status = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (status) { + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __FUNCTION__, status); + } else { + spin_lock_irqsave(&garmin_data_p->lock, flags); + garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE; + /* do not send this packet to the user */ + garmin_data_p->ignorePkts = 1; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); + } + } else { + /* bulk-in transfer still active */ + spin_lock_irqsave(&garmin_data_p->lock, flags); + garmin_data_p->flags |= FLAGS_BULK_IN_RESTART; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); } } else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY)) && 0 == memcmp(data, GARMIN_START_SESSION_REPLY, sizeof(GARMIN_START_SESSION_REPLY))) { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* save the serial number */ garmin_data_p->serial_num @@ -1332,7 +1414,9 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs) ignore it. */ dbg("%s - pkt ignored (%d)", __FUNCTION__, garmin_data_p->ignorePkts); + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->ignorePkts--; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); } else { garmin_read_process(garmin_data_p, data, urb->actual_length); } @@ -1353,18 +1437,20 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs) */ static int garmin_flush_queue(struct garmin_data * garmin_data_p) { + unsigned long flags; struct garmin_packet *pkt; if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) { pkt = pkt_pop(garmin_data_p); if (pkt != NULL) { - send_to_tty(garmin_data_p->port, pkt->data, pkt->size); kfree(pkt); mod_timer(&garmin_data_p->timer, (1)+jiffies); } else { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags &= ~FLAGS_QUEUING; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); } } return 0; @@ -1373,26 +1459,41 @@ static int garmin_flush_queue(struct garmin_data * garmin_data_p) static void garmin_throttle (struct usb_serial_port *port) { + unsigned long flags; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); dbg("%s - port %d", __FUNCTION__, port->number); /* set flag, data received will be put into a queue for later processing */ + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); } static void garmin_unthrottle (struct usb_serial_port *port) { + unsigned long flags; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + int status; dbg("%s - port %d", __FUNCTION__, port->number); + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags &= ~FLAGS_THROTTLED; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* in native mode send queued data to tty, in serial mode nothing needs to be done here */ if (garmin_data_p->mode == MODE_NATIVE) garmin_flush_queue(garmin_data_p); + + if (0 != (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) { + status = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (status) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __FUNCTION__, status); + } } @@ -1422,11 +1523,12 @@ static int garmin_attach (struct usb_serial *serial) dbg("%s", __FUNCTION__); - garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL); + garmin_data_p = kmalloc (sizeof(struct garmin_data), GFP_KERNEL); if (garmin_data_p == NULL) { dev_err(&port->dev, "%s - Out of memory\n", __FUNCTION__); return -ENOMEM; } + memset (garmin_data_p, 0, sizeof(struct garmin_data)); init_timer(&garmin_data_p->timer); spin_lock_init(&garmin_data_p->lock); INIT_LIST_HEAD(&garmin_data_p->pktlist); @@ -1461,10 +1563,10 @@ static void garmin_shutdown (struct usb_serial *serial) /* All of the device info needed */ static struct usb_serial_driver garmin_device = { .driver = { - .owner = THIS_MODULE, - .name = "garmin_gps", + .owner = THIS_MODULE, + .name = "garmin_gps", }, - .description = "Garmin GPS usb/tty", + .description = "Garmin GPS usb/tty", .id_table = id_table, .num_interrupt_in = 1, .num_bulk_in = 1, @@ -1485,6 +1587,7 @@ static struct usb_serial_driver garmin_device = { }; + static int __init garmin_init (void) { int retval; diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 07a478c59fb2..21cbaa0fb96b 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -9,7 +9,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> @@ -18,8 +17,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/usb.h> +#include <linux/usb/serial.h> #include <asm/uaccess.h> -#include "usb-serial.h" static int debug; @@ -176,14 +175,14 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * /* only do something if we have a bulk out endpoint */ if (serial->num_bulk_out) { - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); count = (count > port->bulk_out_size) ? port->bulk_out_size : count; @@ -286,6 +285,7 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb, struct pt_regs *reg if (result) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); } +EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); void usb_serial_generic_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { diff --git a/drivers/usb/serial/hp4x.c b/drivers/usb/serial/hp4x.c index e9719da2aca1..ebcac701b069 100644 --- a/drivers/usb/serial/hp4x.c +++ b/drivers/usb/serial/hp4x.c @@ -12,13 +12,12 @@ * See Documentation/usb/usb-serial.txt for more information on using this driver */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> /* * Version Information diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index b85d2156dfdc..c49976c3ad52 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -29,7 +29,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/errno.h> @@ -45,7 +44,7 @@ #include <linux/wait.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "io_edgeport.h" #include "io_ionsp.h" /* info for the iosp messages */ #include "io_16654.h" /* 16654 UART defines */ diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 8e1e2253748b..17c5b1d2311a 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -24,7 +24,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/errno.h> @@ -40,8 +39,8 @@ #include <asm/uaccess.h> #include <asm/semaphore.h> #include <linux/usb.h> +#include <linux/usb/serial.h> -#include "usb-serial.h" #include "io_16654.h" #include "io_usbvend.h" #include "io_ti.h" diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 9da6d2a8f2b0..cbc725a6c58e 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -44,7 +44,6 @@ * Thanks to info from Heath Robinson and Arieh Davidoff. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -56,7 +55,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "ipaq.h" #define KP_RETRIES 100 @@ -71,6 +70,8 @@ static __u16 product, vendor; static int debug; +static int connect_retries = KP_RETRIES; +static int initial_wait; /* Function prototypes for an ipaq */ static int ipaq_open (struct usb_serial_port *port, struct file *filp); @@ -249,6 +250,9 @@ static struct usb_device_id ipaq_id_table [] = { { USB_DEVICE(0x04C5, 0x1058) }, /* FUJITSU USB Sync */ { USB_DEVICE(0x04C5, 0x1079) }, /* FUJITSU USB Sync */ { USB_DEVICE(0x04DA, 0x2500) }, /* Panasonic USB Sync */ + { USB_DEVICE(0x04DD, 0x9102) }, /* SHARP WS003SH USB Modem */ + { USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */ + { USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */ { USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */ { USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */ { USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */ @@ -475,6 +479,7 @@ static struct usb_device_id ipaq_id_table [] = { { USB_DEVICE(0x0BB4, 0x0A9D) }, /* SmartPhone USB Sync */ { USB_DEVICE(0x0BB4, 0x0A9E) }, /* SmartPhone USB Sync */ { USB_DEVICE(0x0BB4, 0x0A9F) }, /* SmartPhone USB Sync */ + { USB_DEVICE(0x0BB4, 0x0BCE) }, /* "High Tech Computer Corp" */ { USB_DEVICE(0x0BF8, 0x1001) }, /* Fujitsu Siemens Computers USB Sync */ { USB_DEVICE(0x0C44, 0x03A2) }, /* Motorola iDEN Smartphone */ { USB_DEVICE(0x0C8E, 0x6000) }, /* Cesscom Luxian Series */ @@ -583,7 +588,7 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) struct ipaq_private *priv; struct ipaq_packet *pkt; int i, result = 0; - int retries = KP_RETRIES; + int retries = connect_retries; dbg("%s - port %d", __FUNCTION__, port->number); @@ -647,16 +652,7 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) port->read_urb->transfer_buffer_length = URBDATA_SIZE; port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE; - /* Start reading from the device */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ipaq_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) { - err("%s - failed submitting read urb, error %d", __FUNCTION__, result); - goto error; - } + msleep(1000*initial_wait); /* * Send out control message observed in win98 sniffs. Not sure what @@ -670,12 +666,31 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21, 0x1, 0, NULL, 0, 100); - if (result == 0) { - return 0; - } + if (!result) + break; + + msleep(1000); } - err("%s - failed doing control urb, error %d", __FUNCTION__, result); - goto error; + + if (!retries && result) { + err("%s - failed doing control urb, error %d", __FUNCTION__, + result); + goto error; + } + + /* Start reading from the device */ + usb_fill_bulk_urb(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, + ipaq_read_bulk_callback, port); + + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) { + err("%s - failed submitting read urb, error %d", __FUNCTION__, result); + goto error; + } + + return 0; enomem: result = -ENOMEM; @@ -855,6 +870,7 @@ static void ipaq_write_bulk_callback(struct urb *urb, struct pt_regs *regs) if (urb->status) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + return; } spin_lock_irqsave(&write_list_lock, flags); @@ -967,3 +983,9 @@ MODULE_PARM_DESC(vendor, "User specified USB idVendor"); module_param(product, ushort, 0); MODULE_PARM_DESC(product, "User specified USB idProduct"); + +module_param(connect_retries, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(connect_retries, "Maximum number of connect retries (one second each)"); + +module_param(initial_wait, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(initial_wait, "Time to wait before attempting a connection (in seconds)"); diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index a4a0bfeaab00..812bc213a963 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -46,8 +46,8 @@ #include <linux/module.h> #include <linux/spinlock.h> #include <linux/usb.h> +#include <linux/usb/serial.h> #include <asm/uaccess.h> -#include "usb-serial.h" /* * Version Information @@ -373,6 +373,8 @@ static void ipw_write_bulk_callback(struct urb *urb, struct pt_regs *regs) dbg("%s", __FUNCTION__); + port->write_urb_busy = 0; + if (urb->status) dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); @@ -392,14 +394,14 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int return 0; } - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); count = min(count, port->bulk_out_size); memcpy(port->bulk_out_buffer, buf, count); diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 9432c7302275..1b348df388ed 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -46,7 +46,6 @@ * initial version released. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -58,7 +57,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> /* * Version Information @@ -343,14 +342,14 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int if (count == 0) return 0; - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); transfer_buffer = port->write_urb->transfer_buffer; transfer_size = min(count, port->bulk_out_size - 1); @@ -453,8 +452,7 @@ static void ir_read_bulk_callback (struct urb *urb, struct pt_regs *regs) tty = port->tty; /* - * FIXME: must not do this in IRQ context, - * must honour TTY_DONT_FLIP + * FIXME: must not do this in IRQ context */ tty->ldisc.receive_buf( tty, diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 2cf6ade704e4..015ad6cc1bbb 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -95,7 +95,6 @@ */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/errno.h> @@ -108,7 +107,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "keyspan.h" static int debug; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index b0441c35f98f..59e777f1e8fd 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -66,7 +66,6 @@ */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -79,6 +78,7 @@ #include <linux/workqueue.h> #include <asm/uaccess.h> #include <linux/usb.h> +#include <linux/usb/serial.h> static int debug; @@ -108,8 +108,6 @@ struct ezusb_hex_record { #include "xircom_pgs_fw.h" #endif -#include "usb-serial.h" - /* * Version Information */ @@ -520,13 +518,13 @@ static int keyspan_pda_write(struct usb_serial_port *port, the TX urb is in-flight (wait until it completes) the device is full (wait until it says there is room) */ - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy || priv->tx_throttled) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); /* At this point the URB is in our control, nobody else can submit it again (the only sudden transition was the one from EINPROGRESS to diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 65d79f630fa4..2a2f3e2da055 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -45,7 +45,6 @@ */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -56,7 +55,7 @@ #include <linux/module.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "kl5kusb105.h" static int debug; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 87dfcd89ffab..d50dce034958 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -35,7 +35,6 @@ */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -47,8 +46,8 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> +#include <linux/usb/serial.h> #include <linux/ioctl.h> -#include "usb-serial.h" #include "kobil_sct.h" static int debug; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 35bd29b6c408..f4d4305c2c02 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -64,7 +64,6 @@ * (via linux-usb-devel). */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -76,7 +75,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "mct_u232.h" /* diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c new file mode 100644 index 000000000000..95bf57166c59 --- /dev/null +++ b/drivers/usb/serial/mos7840.c @@ -0,0 +1,2962 @@ +/* + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Clean ups from Moschip version and a few ioctl implementations by: + * Paul B Schroeder <pschroeder "at" uplogix "dot" com> + * + * Originally based on drivers/usb/serial/io_edgeport.c which is: + * Copyright (C) 2000 Inside Out Networks, All rights reserved. + * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> + * + */ + +#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/module.h> +#include <linux/serial.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include <asm/uaccess.h> + +/* + * Version Information + */ +#define DRIVER_VERSION "1.3.1" +#define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver" + +/* + * 16C50 UART register defines + */ + +#define LCR_BITS_5 0x00 /* 5 bits/char */ +#define LCR_BITS_6 0x01 /* 6 bits/char */ +#define LCR_BITS_7 0x02 /* 7 bits/char */ +#define LCR_BITS_8 0x03 /* 8 bits/char */ +#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */ + +#define LCR_STOP_1 0x00 /* 1 stop bit */ +#define LCR_STOP_1_5 0x04 /* 1.5 stop bits (if 5 bits/char) */ +#define LCR_STOP_2 0x04 /* 2 stop bits (if 6-8 bits/char) */ +#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */ + +#define LCR_PAR_NONE 0x00 /* No parity */ +#define LCR_PAR_ODD 0x08 /* Odd parity */ +#define LCR_PAR_EVEN 0x18 /* Even parity */ +#define LCR_PAR_MARK 0x28 /* Force parity bit to 1 */ +#define LCR_PAR_SPACE 0x38 /* Force parity bit to 0 */ +#define LCR_PAR_MASK 0x38 /* Mask for parity field */ + +#define LCR_SET_BREAK 0x40 /* Set Break condition */ +#define LCR_DL_ENABLE 0x80 /* Enable access to divisor latch */ + +#define MCR_DTR 0x01 /* Assert DTR */ +#define MCR_RTS 0x02 /* Assert RTS */ +#define MCR_OUT1 0x04 /* Loopback only: Sets state of RI */ +#define MCR_MASTER_IE 0x08 /* Enable interrupt outputs */ +#define MCR_LOOPBACK 0x10 /* Set internal (digital) loopback mode */ +#define MCR_XON_ANY 0x20 /* Enable any char to exit XOFF mode */ + +#define MOS7840_MSR_CTS 0x10 /* Current state of CTS */ +#define MOS7840_MSR_DSR 0x20 /* Current state of DSR */ +#define MOS7840_MSR_RI 0x40 /* Current state of RI */ +#define MOS7840_MSR_CD 0x80 /* Current state of CD */ + +/* + * Defines used for sending commands to port + */ + +#define WAIT_FOR_EVER (HZ * 0 ) /* timeout urb is wait for ever */ +#define MOS_WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ + +#define MOS_PORT1 0x0200 +#define MOS_PORT2 0x0300 +#define MOS_VENREG 0x0000 +#define MOS_MAX_PORT 0x02 +#define MOS_WRITE 0x0E +#define MOS_READ 0x0D + +/* Requests */ +#define MCS_RD_RTYPE 0xC0 +#define MCS_WR_RTYPE 0x40 +#define MCS_RDREQ 0x0D +#define MCS_WRREQ 0x0E +#define MCS_CTRL_TIMEOUT 500 +#define VENDOR_READ_LENGTH (0x01) + +#define MAX_NAME_LEN 64 + +#define ZLP_REG1 0x3A //Zero_Flag_Reg1 58 +#define ZLP_REG5 0x3E //Zero_Flag_Reg5 62 + +/* For higher baud Rates use TIOCEXBAUD */ +#define TIOCEXBAUD 0x5462 + +/* vendor id and device id defines */ + +#define USB_VENDOR_ID_MOSCHIP 0x9710 +#define MOSCHIP_DEVICE_ID_7840 0x7840 +#define MOSCHIP_DEVICE_ID_7820 0x7820 + +/* Interrupt Rotinue Defines */ + +#define SERIAL_IIR_RLS 0x06 +#define SERIAL_IIR_MS 0x00 + +/* + * Emulation of the bit mask on the LINE STATUS REGISTER. + */ +#define SERIAL_LSR_DR 0x0001 +#define SERIAL_LSR_OE 0x0002 +#define SERIAL_LSR_PE 0x0004 +#define SERIAL_LSR_FE 0x0008 +#define SERIAL_LSR_BI 0x0010 + +#define MOS_MSR_DELTA_CTS 0x10 +#define MOS_MSR_DELTA_DSR 0x20 +#define MOS_MSR_DELTA_RI 0x40 +#define MOS_MSR_DELTA_CD 0x80 + +// Serial Port register Address +#define INTERRUPT_ENABLE_REGISTER ((__u16)(0x01)) +#define FIFO_CONTROL_REGISTER ((__u16)(0x02)) +#define LINE_CONTROL_REGISTER ((__u16)(0x03)) +#define MODEM_CONTROL_REGISTER ((__u16)(0x04)) +#define LINE_STATUS_REGISTER ((__u16)(0x05)) +#define MODEM_STATUS_REGISTER ((__u16)(0x06)) +#define SCRATCH_PAD_REGISTER ((__u16)(0x07)) +#define DIVISOR_LATCH_LSB ((__u16)(0x00)) +#define DIVISOR_LATCH_MSB ((__u16)(0x01)) + +#define CLK_MULTI_REGISTER ((__u16)(0x02)) +#define CLK_START_VALUE_REGISTER ((__u16)(0x03)) + +#define SERIAL_LCR_DLAB ((__u16)(0x0080)) + +/* + * URB POOL related defines + */ +#define NUM_URBS 16 /* URB Count */ +#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ + + +static struct usb_device_id moschip_port_id_table[] = { + {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, + {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, + {} /* terminating entry */ +}; + +static __devinitdata struct usb_device_id moschip_id_table_combined[] = { + {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, + {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, + {} /* terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, moschip_id_table_combined); + +/* This structure holds all of the local port information */ + +struct moschip_port { + int port_num; /*Actual port number in the device(1,2,etc) */ + struct urb *write_urb; /* write URB for this port */ + struct urb *read_urb; /* read URB for this port */ + __u8 shadowLCR; /* last LCR value received */ + __u8 shadowMCR; /* last MCR value received */ + char open; + wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ + wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */ + int delta_msr_cond; + struct async_icount icount; + struct usb_serial_port *port; /* loop back to the owner of this object */ + + /*Offsets */ + __u8 SpRegOffset; + __u8 ControlRegOffset; + __u8 DcrRegOffset; + //for processing control URBS in interrupt context + struct urb *control_urb; + char *ctrl_buf; + int MsrLsr; + + struct urb *write_urb_pool[NUM_URBS]; +}; + + +static int debug; +static int mos7840_num_ports; //this says the number of ports in the device +static int mos7840_num_open_ports; + + +/* + * mos7840_set_reg_sync + * To set the Control register by calling usb_fill_control_urb function + * by passing usb_sndctrlpipe function as parameter. + */ + +static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg, + __u16 val) +{ + struct usb_device *dev = port->serial->dev; + val = val & 0x00ff; + dbg("mos7840_set_reg_sync offset is %x, value %x\n", reg, val); + + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, + MCS_WR_RTYPE, val, reg, NULL, 0, + MOS_WDR_TIMEOUT); +} + +/* + * mos7840_get_reg_sync + * To set the Uart register by calling usb_fill_control_urb function by + * passing usb_rcvctrlpipe function as parameter. + */ + +static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg, + __u16 * val) +{ + struct usb_device *dev = port->serial->dev; + int ret = 0; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, + MCS_RD_RTYPE, 0, reg, val, VENDOR_READ_LENGTH, + MOS_WDR_TIMEOUT); + dbg("mos7840_get_reg_sync offset is %x, return val %x\n", reg, *val); + *val = (*val) & 0x00ff; + return ret; +} + +/* + * mos7840_set_uart_reg + * To set the Uart register by calling usb_fill_control_urb function by + * passing usb_sndctrlpipe function as parameter. + */ + +static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg, + __u16 val) +{ + + struct usb_device *dev = port->serial->dev; + val = val & 0x00ff; + // For the UART control registers, the application number need to be Or'ed + if (mos7840_num_ports == 4) { + val |= + (((__u16) port->number - (__u16) (port->serial->minor)) + + 1) << 8; + dbg("mos7840_set_uart_reg application number is %x\n", val); + } else { + if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) { + val |= + (((__u16) port->number - + (__u16) (port->serial->minor)) + 1) << 8; + dbg("mos7840_set_uart_reg application number is %x\n", + val); + } else { + val |= + (((__u16) port->number - + (__u16) (port->serial->minor)) + 2) << 8; + dbg("mos7840_set_uart_reg application number is %x\n", + val); + } + } + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, + MCS_WR_RTYPE, val, reg, NULL, 0, + MOS_WDR_TIMEOUT); + +} + +/* + * mos7840_get_uart_reg + * To set the Control register by calling usb_fill_control_urb function + * by passing usb_rcvctrlpipe function as parameter. + */ +static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, + __u16 * val) +{ + struct usb_device *dev = port->serial->dev; + int ret = 0; + __u16 Wval; + + //dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); + /*Wval is same as application number */ + if (mos7840_num_ports == 4) { + Wval = + (((__u16) port->number - (__u16) (port->serial->minor)) + + 1) << 8; + dbg("mos7840_get_uart_reg application number is %x\n", Wval); + } else { + if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) { + Wval = + (((__u16) port->number - + (__u16) (port->serial->minor)) + 1) << 8; + dbg("mos7840_get_uart_reg application number is %x\n", + Wval); + } else { + Wval = + (((__u16) port->number - + (__u16) (port->serial->minor)) + 2) << 8; + dbg("mos7840_get_uart_reg application number is %x\n", + Wval); + } + } + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, + MCS_RD_RTYPE, Wval, reg, val, VENDOR_READ_LENGTH, + MOS_WDR_TIMEOUT); + *val = (*val) & 0x00ff; + return ret; +} + +static void mos7840_dump_serial_port(struct moschip_port *mos7840_port) +{ + + dbg("***************************************\n"); + dbg("SpRegOffset is %2x\n", mos7840_port->SpRegOffset); + dbg("ControlRegOffset is %2x \n", mos7840_port->ControlRegOffset); + dbg("DCRRegOffset is %2x \n", mos7840_port->DcrRegOffset); + dbg("***************************************\n"); + +} + +/************************************************************************/ +/************************************************************************/ +/* I N T E R F A C E F U N C T I O N S */ +/* I N T E R F A C E F U N C T I O N S */ +/************************************************************************/ +/************************************************************************/ + +static inline void mos7840_set_port_private(struct usb_serial_port *port, + struct moschip_port *data) +{ + usb_set_serial_port_data(port, (void *)data); +} + +static inline struct moschip_port *mos7840_get_port_private(struct + usb_serial_port + *port) +{ + return (struct moschip_port *)usb_get_serial_port_data(port); +} + +static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) +{ + struct moschip_port *mos7840_port; + struct async_icount *icount; + mos7840_port = port; + icount = &mos7840_port->icount; + if (new_msr & + (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI | + MOS_MSR_DELTA_CD)) { + icount = &mos7840_port->icount; + + /* update input line counters */ + if (new_msr & MOS_MSR_DELTA_CTS) { + icount->cts++; + } + if (new_msr & MOS_MSR_DELTA_DSR) { + icount->dsr++; + } + if (new_msr & MOS_MSR_DELTA_CD) { + icount->dcd++; + } + if (new_msr & MOS_MSR_DELTA_RI) { + icount->rng++; + } + } + + return 0; +} + +static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) +{ + struct async_icount *icount; + + dbg("%s - %02x", __FUNCTION__, new_lsr); + + if (new_lsr & SERIAL_LSR_BI) { + // + // Parity and Framing errors only count if they + // occur exclusive of a break being + // received. + // + new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI); + } + + /* update input line counters */ + icount = &port->icount; + if (new_lsr & SERIAL_LSR_BI) { + icount->brk++; + } + if (new_lsr & SERIAL_LSR_OE) { + icount->overrun++; + } + if (new_lsr & SERIAL_LSR_PE) { + icount->parity++; + } + if (new_lsr & SERIAL_LSR_FE) { + icount->frame++; + } + + return 0; +} + +/************************************************************************/ +/************************************************************************/ +/* U S B C A L L B A C K F U N C T I O N S */ +/* U S B C A L L B A C K F U N C T I O N S */ +/************************************************************************/ +/************************************************************************/ + +static void mos7840_control_callback(struct urb *urb, struct pt_regs *regs) +{ + unsigned char *data; + struct moschip_port *mos7840_port; + __u8 regval = 0x0; + + if (!urb) { + dbg("%s", "Invalid Pointer !!!!:\n"); + return; + } + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, + urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, + urb->status); + goto exit; + } + + mos7840_port = (struct moschip_port *)urb->context; + + dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length); + dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__, + mos7840_port->MsrLsr, mos7840_port->port_num); + data = urb->transfer_buffer; + regval = (__u8) data[0]; + dbg("%s data is %x\n", __FUNCTION__, regval); + if (mos7840_port->MsrLsr == 0) + mos7840_handle_new_msr(mos7840_port, regval); + else if (mos7840_port->MsrLsr == 1) + mos7840_handle_new_lsr(mos7840_port, regval); + + exit: + return; +} + +static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, + __u16 * val) +{ + struct usb_device *dev = mcs->port->serial->dev; + struct usb_ctrlrequest *dr = NULL; + unsigned char *buffer = NULL; + int ret = 0; + buffer = (__u8 *) mcs->ctrl_buf; + +// dr=(struct usb_ctrlrequest *)(buffer); + dr = (void *)(buffer + 2); + dr->bRequestType = MCS_RD_RTYPE; + dr->bRequest = MCS_RDREQ; + dr->wValue = cpu_to_le16(Wval); //0; + dr->wIndex = cpu_to_le16(reg); + dr->wLength = cpu_to_le16(2); + + usb_fill_control_urb(mcs->control_urb, dev, usb_rcvctrlpipe(dev, 0), + (unsigned char *)dr, buffer, 2, + mos7840_control_callback, mcs); + mcs->control_urb->transfer_buffer_length = 2; + ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC); + return ret; +} + +/***************************************************************************** + * mos7840_interrupt_callback + * this is the callback function for when we have received data on the + * interrupt endpoint. + *****************************************************************************/ + +static void mos7840_interrupt_callback(struct urb *urb, struct pt_regs *regs) +{ + int result; + int length; + struct moschip_port *mos7840_port; + struct usb_serial *serial; + __u16 Data; + unsigned char *data; + __u8 sp[5], st; + int i; + __u16 wval; + + dbg("%s", " : Entering\n"); + if (!urb) { + dbg("%s", "Invalid Pointer !!!!:\n"); + return; + } + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, + urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, + urb->status); + goto exit; + } + + length = urb->actual_length; + data = urb->transfer_buffer; + + serial = (struct usb_serial *)urb->context; + + /* Moschip get 5 bytes + * Byte 1 IIR Port 1 (port.number is 0) + * Byte 2 IIR Port 2 (port.number is 1) + * Byte 3 IIR Port 3 (port.number is 2) + * Byte 4 IIR Port 4 (port.number is 3) + * Byte 5 FIFO status for both */ + + if (length && length > 5) { + dbg("%s \n", "Wrong data !!!"); + return; + } + + sp[0] = (__u8) data[0]; + sp[1] = (__u8) data[1]; + sp[2] = (__u8) data[2]; + sp[3] = (__u8) data[3]; + st = (__u8) data[4]; + + for (i = 0; i < serial->num_ports; i++) { + mos7840_port = mos7840_get_port_private(serial->port[i]); + wval = + (((__u16) serial->port[i]->number - + (__u16) (serial->minor)) + 1) << 8; + if (mos7840_port->open) { + if (sp[i] & 0x01) { + dbg("SP%d No Interrupt !!!\n", i); + } else { + switch (sp[i] & 0x0f) { + case SERIAL_IIR_RLS: + dbg("Serial Port %d: Receiver status error or ", i); + dbg("address bit detected in 9-bit mode\n"); + mos7840_port->MsrLsr = 1; + mos7840_get_reg(mos7840_port, wval, + LINE_STATUS_REGISTER, + &Data); + break; + case SERIAL_IIR_MS: + dbg("Serial Port %d: Modem status change\n", i); + mos7840_port->MsrLsr = 0; + mos7840_get_reg(mos7840_port, wval, + MODEM_STATUS_REGISTER, + &Data); + break; + } + } + } + } + exit: + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) { + dev_err(&urb->dev->dev, + "%s - Error %d submitting interrupt urb\n", + __FUNCTION__, result); + } + + return; + +} + +static int mos7840_port_paranoia_check(struct usb_serial_port *port, + const char *function) +{ + if (!port) { + dbg("%s - port == NULL", function); + return -1; + } + if (!port->serial) { + dbg("%s - port->serial == NULL", function); + return -1; + } + + return 0; +} + +/* Inline functions to check the sanity of a pointer that is passed to us */ +static int mos7840_serial_paranoia_check(struct usb_serial *serial, + const char *function) +{ + if (!serial) { + dbg("%s - serial == NULL", function); + return -1; + } + if (!serial->type) { + dbg("%s - serial->type == NULL!", function); + return -1; + } + + return 0; +} + +static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port, + const char *function) +{ + /* if no port was specified, or it fails a paranoia check */ + if (!port || + mos7840_port_paranoia_check(port, function) || + mos7840_serial_paranoia_check(port->serial, function)) { + /* then say that we don't have a valid usb_serial thing, which will * end up genrating -ENODEV return values */ + return NULL; + } + + return port->serial; +} + +/***************************************************************************** + * mos7840_bulk_in_callback + * this is the callback function for when we have received data on the + * bulk in endpoint. + *****************************************************************************/ + +static void mos7840_bulk_in_callback(struct urb *urb, struct pt_regs *regs) +{ + int status; + unsigned char *data; + struct usb_serial *serial; + struct usb_serial_port *port; + struct moschip_port *mos7840_port; + struct tty_struct *tty; + + if (!urb) { + dbg("%s", "Invalid Pointer !!!!:\n"); + return; + } + + if (urb->status) { + dbg("nonzero read bulk status received: %d", urb->status); + return; + } + + mos7840_port = (struct moschip_port *)urb->context; + if (!mos7840_port) { + dbg("%s", "NULL mos7840_port pointer \n"); + return; + } + + port = (struct usb_serial_port *)mos7840_port->port; + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Port Paranoia failed \n"); + return; + } + + serial = mos7840_get_usb_serial(port, __FUNCTION__); + if (!serial) { + dbg("%s\n", "Bad serial pointer "); + return; + } + + dbg("%s\n", "Entering... \n"); + + data = urb->transfer_buffer; + + dbg("%s", "Entering ........... \n"); + + if (urb->actual_length) { + tty = mos7840_port->port->tty; + if (tty) { + tty_buffer_request_room(tty, urb->actual_length); + tty_insert_flip_string(tty, data, urb->actual_length); + dbg(" %s \n", data); + tty_flip_buffer_push(tty); + } + mos7840_port->icount.rx += urb->actual_length; + dbg("mos7840_port->icount.rx is %d:\n", + mos7840_port->icount.rx); + } + + if (!mos7840_port->read_urb) { + dbg("%s", "URB KILLED !!!\n"); + return; + } + + if (mos7840_port->read_urb->status != -EINPROGRESS) { + mos7840_port->read_urb->dev = serial->dev; + + status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); + + if (status) { + dbg(" usb_submit_urb(read bulk) failed, status = %d", + status); + } + } +} + +/***************************************************************************** + * mos7840_bulk_out_data_callback + * this is the callback function for when we have finished sending serial data + * on the bulk out endpoint. + *****************************************************************************/ + +static void mos7840_bulk_out_data_callback(struct urb *urb, + struct pt_regs *regs) +{ + struct moschip_port *mos7840_port; + struct tty_struct *tty; + if (!urb) { + dbg("%s", "Invalid Pointer !!!!:\n"); + return; + } + + if (urb->status) { + dbg("nonzero write bulk status received:%d\n", urb->status); + return; + } + + mos7840_port = (struct moschip_port *)urb->context; + if (!mos7840_port) { + dbg("%s", "NULL mos7840_port pointer \n"); + return; + } + + if (mos7840_port_paranoia_check(mos7840_port->port, __FUNCTION__)) { + dbg("%s", "Port Paranoia failed \n"); + return; + } + + dbg("%s \n", "Entering ........."); + + tty = mos7840_port->port->tty; + + if (tty && mos7840_port->open) { + /* let the tty driver wakeup if it has a special * + * write_wakeup function */ + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup) { + (tty->ldisc.write_wakeup) (tty); + } + + /* tell the tty driver that something has changed */ + wake_up_interruptible(&tty->write_wait); + } + +} + +/************************************************************************/ +/* D R I V E R T T Y I N T E R F A C E F U N C T I O N S */ +/************************************************************************/ +#ifdef MCSSerialProbe +static int mos7840_serial_probe(struct usb_serial *serial, + const struct usb_device_id *id) +{ + + /*need to implement the mode_reg reading and updating\ + structures usb_serial_ device_type\ + (i.e num_ports, num_bulkin,bulkout etc) */ + /* Also we can update the changes attach */ + return 1; +} +#endif + +/***************************************************************************** + * mos7840_open + * this function is called by the tty driver when a port is opened + * If successful, we return 0 + * Otherwise we return a negative error number. + *****************************************************************************/ + +static int mos7840_open(struct usb_serial_port *port, struct file *filp) +{ + int response; + int j; + struct usb_serial *serial; + struct urb *urb; + __u16 Data; + int status; + struct moschip_port *mos7840_port; + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Port Paranoia failed \n"); + return -ENODEV; + } + + mos7840_num_open_ports++; + serial = port->serial; + + if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) { + dbg("%s", "Serial Paranoia failed \n"); + return -ENODEV; + } + + mos7840_port = mos7840_get_port_private(port); + + if (mos7840_port == NULL) + return -ENODEV; + + usb_clear_halt(serial->dev, port->write_urb->pipe); + usb_clear_halt(serial->dev, port->read_urb->pipe); + + /* Initialising the write urb pool */ + for (j = 0; j < NUM_URBS; ++j) { + urb = usb_alloc_urb(0, SLAB_ATOMIC); + mos7840_port->write_urb_pool[j] = urb; + + if (urb == NULL) { + err("No more urbs???"); + continue; + } + + urb->transfer_buffer = NULL; + urb->transfer_buffer = + kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); + if (!urb->transfer_buffer) { + err("%s-out of memory for urb buffers.", __FUNCTION__); + continue; + } + } + +/***************************************************************************** + * Initialize MCS7840 -- Write Init values to corresponding Registers + * + * Register Index + * 1 : IER + * 2 : FCR + * 3 : LCR + * 4 : MCR + * + * 0x08 : SP1/2 Control Reg + *****************************************************************************/ + +//NEED to check the following Block + + status = 0; + Data = 0x0; + status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data); + if (status < 0) { + dbg("Reading Spreg failed\n"); + return -1; + } + Data |= 0x80; + status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); + if (status < 0) { + dbg("writing Spreg failed\n"); + return -1; + } + + Data &= ~0x80; + status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); + if (status < 0) { + dbg("writing Spreg failed\n"); + return -1; + } +//End of block to be checked + + status = 0; + Data = 0x0; + status = + mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data); + if (status < 0) { + dbg("Reading Controlreg failed\n"); + return -1; + } + Data |= 0x08; //Driver done bit + Data |= 0x20; //rx_disable + status = 0; + status = + mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); + if (status < 0) { + dbg("writing Controlreg failed\n"); + return -1; + } + //do register settings here + // Set all regs to the device default values. + //////////////////////////////////// + // First Disable all interrupts. + //////////////////////////////////// + + Data = 0x00; + status = 0; + status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); + if (status < 0) { + dbg("disableing interrupts failed\n"); + return -1; + } + // Set FIFO_CONTROL_REGISTER to the default value + Data = 0x00; + status = 0; + status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); + if (status < 0) { + dbg("Writing FIFO_CONTROL_REGISTER failed\n"); + return -1; + } + + Data = 0xcf; + status = 0; + status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); + if (status < 0) { + dbg("Writing FIFO_CONTROL_REGISTER failed\n"); + return -1; + } + + Data = 0x03; + status = 0; + status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); + mos7840_port->shadowLCR = Data; + + Data = 0x0b; + status = 0; + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + mos7840_port->shadowMCR = Data; + + Data = 0x00; + status = 0; + status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); + mos7840_port->shadowLCR = Data; + + Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80 + status = 0; + status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); + + Data = 0x0c; + status = 0; + status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data); + + Data = 0x0; + status = 0; + status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data); + + Data = 0x00; + status = 0; + status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); + + Data = Data & ~SERIAL_LCR_DLAB; + status = 0; + status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); + mos7840_port->shadowLCR = Data; + + //clearing Bulkin and Bulkout Fifo + Data = 0x0; + status = 0; + status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data); + + Data = Data | 0x0c; + status = 0; + status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); + + Data = Data & ~0x0c; + status = 0; + status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); + //Finally enable all interrupts + Data = 0x0; + Data = 0x0c; + status = 0; + status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); + + //clearing rx_disable + Data = 0x0; + status = 0; + status = + mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data); + Data = Data & ~0x20; + status = 0; + status = + mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); + + // rx_negate + Data = 0x0; + status = 0; + status = + mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data); + Data = Data | 0x10; + status = 0; + status = + mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); + + /* force low_latency on so that our tty_push actually forces * + * the data through,otherwise it is scheduled, and with * + * high data rates (like with OHCI) data can get lost. */ + + if (port->tty) + port->tty->low_latency = 1; +/* Check to see if we've set up our endpoint info yet * + * (can't set it up in mos7840_startup as the structures * + * were not set up at that time.) */ + if (mos7840_num_open_ports == 1) { + if (serial->port[0]->interrupt_in_buffer == NULL) { + + /* set up interrupt urb */ + + usb_fill_int_urb(serial->port[0]->interrupt_in_urb, + serial->dev, + usb_rcvintpipe(serial->dev, + serial->port[0]-> + interrupt_in_endpointAddress), + serial->port[0]->interrupt_in_buffer, + serial->port[0]->interrupt_in_urb-> + transfer_buffer_length, + mos7840_interrupt_callback, + serial, + serial->port[0]->interrupt_in_urb-> + interval); + + /* start interrupt read for mos7840 * + * will continue as long as mos7840 is connected */ + + response = + usb_submit_urb(serial->port[0]->interrupt_in_urb, + GFP_KERNEL); + if (response) { + err("%s - Error %d submitting interrupt urb", + __FUNCTION__, response); + } + + } + + } + + /* see if we've set up our endpoint info yet * + * (can't set it up in mos7840_startup as the * + * structures were not set up at that time.) */ + + dbg("port number is %d \n", port->number); + dbg("serial number is %d \n", port->serial->minor); + dbg("Bulkin endpoint is %d \n", port->bulk_in_endpointAddress); + dbg("BulkOut endpoint is %d \n", port->bulk_out_endpointAddress); + dbg("Interrupt endpoint is %d \n", port->interrupt_in_endpointAddress); + dbg("port's number in the device is %d\n", mos7840_port->port_num); + mos7840_port->read_urb = port->read_urb; + + /* set up our bulk in urb */ + + usb_fill_bulk_urb(mos7840_port->read_urb, + serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), + port->bulk_in_buffer, + mos7840_port->read_urb->transfer_buffer_length, + mos7840_bulk_in_callback, mos7840_port); + + dbg("mos7840_open: bulkin endpoint is %d\n", + port->bulk_in_endpointAddress); + response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL); + if (response) { + err("%s - Error %d submitting control urb", __FUNCTION__, + response); + } + + /* initialize our wait queues */ + init_waitqueue_head(&mos7840_port->wait_chase); + init_waitqueue_head(&mos7840_port->delta_msr_wait); + + /* initialize our icount structure */ + memset(&(mos7840_port->icount), 0x00, sizeof(mos7840_port->icount)); + + /* initialize our port settings */ + mos7840_port->shadowMCR = MCR_MASTER_IE; /* Must set to enable ints! */ + /* send a open port command */ + mos7840_port->open = 1; + //mos7840_change_port_settings(mos7840_port,old_termios); + mos7840_port->icount.tx = 0; + mos7840_port->icount.rx = 0; + + dbg("\n\nusb_serial serial:%x mos7840_port:%x\n usb_serial_port port:%x\n\n", (unsigned int)serial, (unsigned int)mos7840_port, (unsigned int)port); + + return 0; + +} + +/***************************************************************************** + * mos7840_chars_in_buffer + * this function is called by the tty driver when it wants to know how many + * bytes of data we currently have outstanding in the port (data that has + * been written, but hasn't made it out the port yet) + * If successful, we return the number of bytes left to be written in the + * system, + * Otherwise we return a negative error number. + *****************************************************************************/ + +static int mos7840_chars_in_buffer(struct usb_serial_port *port) +{ + int i; + int chars = 0; + struct moschip_port *mos7840_port; + + dbg("%s \n", " mos7840_chars_in_buffer:entering ..........."); + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + return -1; + } + + mos7840_port = mos7840_get_port_private(port); + if (mos7840_port == NULL) { + dbg("%s \n", "mos7840_break:leaving ..........."); + return -1; + } + + for (i = 0; i < NUM_URBS; ++i) { + if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) { + chars += URB_TRANSFER_BUFFER_SIZE; + } + } + dbg("%s - returns %d", __FUNCTION__, chars); + return (chars); + +} + +/************************************************************************ + * + * mos7840_block_until_tx_empty + * + * This function will block the close until one of the following: + * 1. TX count are 0 + * 2. The mos7840 has stopped + * 3. A timout of 3 seconds without activity has expired + * + ************************************************************************/ +static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) +{ + int timeout = HZ / 10; + int wait = 30; + int count; + + while (1) { + + count = mos7840_chars_in_buffer(mos7840_port->port); + + /* Check for Buffer status */ + if (count <= 0) { + return; + } + + /* Block the thread for a while */ + interruptible_sleep_on_timeout(&mos7840_port->wait_chase, + timeout); + + /* No activity.. count down section */ + wait--; + if (wait == 0) { + dbg("%s - TIMEOUT", __FUNCTION__); + return; + } else { + /* Reset timout value back to seconds */ + wait = 30; + } + } +} + +/***************************************************************************** + * mos7840_close + * this function is called by the tty driver when a port is closed + *****************************************************************************/ + +static void mos7840_close(struct usb_serial_port *port, struct file *filp) +{ + struct usb_serial *serial; + struct moschip_port *mos7840_port; + int j; + __u16 Data; + + dbg("%s\n", "mos7840_close:entering..."); + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Port Paranoia failed \n"); + return; + } + + serial = mos7840_get_usb_serial(port, __FUNCTION__); + if (!serial) { + dbg("%s", "Serial Paranoia failed \n"); + return; + } + + mos7840_port = mos7840_get_port_private(port); + + if (mos7840_port == NULL) { + return; + } + + for (j = 0; j < NUM_URBS; ++j) + usb_kill_urb(mos7840_port->write_urb_pool[j]); + + /* Freeing Write URBs */ + for (j = 0; j < NUM_URBS; ++j) { + if (mos7840_port->write_urb_pool[j]) { + if (mos7840_port->write_urb_pool[j]->transfer_buffer) + kfree(mos7840_port->write_urb_pool[j]-> + transfer_buffer); + + usb_free_urb(mos7840_port->write_urb_pool[j]); + } + } + + if (serial->dev) { + /* flush and block until tx is empty */ + mos7840_block_until_tx_empty(mos7840_port); + } + + /* While closing port, shutdown all bulk read, write * + * and interrupt read if they exists */ + if (serial->dev) { + + if (mos7840_port->write_urb) { + dbg("%s", "Shutdown bulk write\n"); + usb_kill_urb(mos7840_port->write_urb); + } + + if (mos7840_port->read_urb) { + dbg("%s", "Shutdown bulk read\n"); + usb_kill_urb(mos7840_port->read_urb); + } + if ((&mos7840_port->control_urb)) { + dbg("%s", "Shutdown control read\n"); + // usb_kill_urb (mos7840_port->control_urb); + + } + } +// if(mos7840_port->ctrl_buf != NULL) +// kfree(mos7840_port->ctrl_buf); + mos7840_num_open_ports--; + dbg("mos7840_num_open_ports in close%d:in port%d\n", + mos7840_num_open_ports, port->number); + if (mos7840_num_open_ports == 0) { + if (serial->port[0]->interrupt_in_urb) { + dbg("%s", "Shutdown interrupt_in_urb\n"); + } + } + + if (mos7840_port->write_urb) { + /* if this urb had a transfer buffer already (old tx) free it */ + + if (mos7840_port->write_urb->transfer_buffer != NULL) { + kfree(mos7840_port->write_urb->transfer_buffer); + } + usb_free_urb(mos7840_port->write_urb); + } + + Data = 0x0; + mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + + Data = 0x00; + mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); + + mos7840_port->open = 0; + + dbg("%s \n", "Leaving ............"); +} + +/************************************************************************ + * + * mos7840_block_until_chase_response + * + * 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 + * (1K of mos7840 data @ 2400 baud ==> 4 sec to empty) + * + ************************************************************************/ + +static void mos7840_block_until_chase_response(struct moschip_port + *mos7840_port) +{ + int timeout = 1 * HZ; + int wait = 10; + int count; + + while (1) { + count = mos7840_chars_in_buffer(mos7840_port->port); + + /* Check for Buffer status */ + if (count <= 0) { + return; + } + + /* Block the thread for a while */ + interruptible_sleep_on_timeout(&mos7840_port->wait_chase, + timeout); + /* No activity.. count down section */ + wait--; + if (wait == 0) { + dbg("%s - TIMEOUT", __FUNCTION__); + return; + } else { + /* Reset timout value back to seconds */ + wait = 10; + } + } + +} + +/***************************************************************************** + * mos7840_break + * this function sends a break to the port + *****************************************************************************/ +static void mos7840_break(struct usb_serial_port *port, int break_state) +{ + unsigned char data; + struct usb_serial *serial; + struct moschip_port *mos7840_port; + + dbg("%s \n", "Entering ..........."); + dbg("mos7840_break: Start\n"); + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Port Paranoia failed \n"); + return; + } + + serial = mos7840_get_usb_serial(port, __FUNCTION__); + if (!serial) { + dbg("%s", "Serial Paranoia failed \n"); + return; + } + + mos7840_port = mos7840_get_port_private(port); + + if (mos7840_port == NULL) { + return; + } + + if (serial->dev) { + + /* flush and block until tx is empty */ + mos7840_block_until_chase_response(mos7840_port); + } + + if (break_state == -1) { + data = mos7840_port->shadowLCR | LCR_SET_BREAK; + } else { + data = mos7840_port->shadowLCR & ~LCR_SET_BREAK; + } + + mos7840_port->shadowLCR = data; + dbg("mcs7840_break mos7840_port->shadowLCR is %x\n", + mos7840_port->shadowLCR); + mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, + mos7840_port->shadowLCR); + + return; +} + +/***************************************************************************** + * mos7840_write_room + * this function is called by the tty driver when it wants to know how many + * bytes of data we can accept for a specific port. + * If successful, we return the amount of room that we have for this port + * Otherwise we return a negative error number. + *****************************************************************************/ + +static int mos7840_write_room(struct usb_serial_port *port) +{ + int i; + int room = 0; + struct moschip_port *mos7840_port; + + dbg("%s \n", " mos7840_write_room:entering ..........."); + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + dbg("%s \n", " mos7840_write_room:leaving ..........."); + return -1; + } + + mos7840_port = mos7840_get_port_private(port); + if (mos7840_port == NULL) { + dbg("%s \n", "mos7840_break:leaving ..........."); + return -1; + } + + for (i = 0; i < NUM_URBS; ++i) { + if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) { + room += URB_TRANSFER_BUFFER_SIZE; + } + } + + dbg("%s - returns %d", __FUNCTION__, room); + return (room); + +} + +/***************************************************************************** + * mos7840_write + * this function is called by the tty driver when data should be written to + * the port. + * If successful, we return the number of bytes written, otherwise we + * return a negative error number. + *****************************************************************************/ + +static int mos7840_write(struct usb_serial_port *port, + const unsigned char *data, int count) +{ + int status; + int i; + int bytes_sent = 0; + int transfer_size; + int from_user = 0; + + struct moschip_port *mos7840_port; + struct usb_serial *serial; + struct urb *urb; + //__u16 Data; + const unsigned char *current_position = data; + unsigned char *data1; + dbg("%s \n", "entering ..........."); + //dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",mos7840_port->shadowLCR); + +#ifdef NOTMOS7840 + Data = 0x00; + status = 0; + status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); + mos7840_port->shadowLCR = Data; + dbg("mos7840_write: LINE_CONTROL_REGISTER is %x\n", Data); + dbg("mos7840_write: mos7840_port->shadowLCR is %x\n", + mos7840_port->shadowLCR); + + //Data = 0x03; + //status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data); + //mos7840_port->shadowLCR=Data;//Need to add later + + Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80 + status = 0; + status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); + + //Data = 0x0c; + //status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data); + Data = 0x00; + status = 0; + status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data); + dbg("mos7840_write:DLL value is %x\n", Data); + + Data = 0x0; + status = 0; + status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data); + dbg("mos7840_write:DLM value is %x\n", Data); + + Data = Data & ~SERIAL_LCR_DLAB; + dbg("mos7840_write: mos7840_port->shadowLCR is %x\n", + mos7840_port->shadowLCR); + status = 0; + status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); +#endif + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Port Paranoia failed \n"); + return -1; + } + + serial = port->serial; + if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) { + dbg("%s", "Serial Paranoia failed \n"); + return -1; + } + + mos7840_port = mos7840_get_port_private(port); + if (mos7840_port == NULL) { + dbg("%s", "mos7840_port is NULL\n"); + return -1; + } + + /* try to find a free urb in the list */ + urb = NULL; + + for (i = 0; i < NUM_URBS; ++i) { + if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) { + urb = mos7840_port->write_urb_pool[i]; + dbg("\nURB:%d", i); + break; + } + } + + if (urb == NULL) { + dbg("%s - no more free urbs", __FUNCTION__); + goto exit; + } + + if (urb->transfer_buffer == NULL) { + urb->transfer_buffer = + kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); + + if (urb->transfer_buffer == NULL) { + err("%s no more kernel memory...", __FUNCTION__); + goto exit; + } + } + transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); + + if (from_user) { + if (copy_from_user + (urb->transfer_buffer, current_position, transfer_size)) { + bytes_sent = -EFAULT; + goto exit; + } + } else { + memcpy(urb->transfer_buffer, current_position, transfer_size); + } + + /* fill urb with data and submit */ + usb_fill_bulk_urb(urb, + serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + urb->transfer_buffer, + transfer_size, + mos7840_bulk_out_data_callback, mos7840_port); + + data1 = urb->transfer_buffer; + dbg("\nbulkout endpoint is %d", port->bulk_out_endpointAddress); + + /* send it down the pipe */ + status = usb_submit_urb(urb, GFP_ATOMIC); + + if (status) { + err("%s - usb_submit_urb(write bulk) failed with status = %d", + __FUNCTION__, status); + bytes_sent = status; + goto exit; + } + bytes_sent = transfer_size; + mos7840_port->icount.tx += transfer_size; + dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx); + exit: + + return bytes_sent; + +} + +/***************************************************************************** + * mos7840_throttle + * this function is called by the tty driver when it wants to stop the data + * being read from the port. + *****************************************************************************/ + +static void mos7840_throttle(struct usb_serial_port *port) +{ + struct moschip_port *mos7840_port; + struct tty_struct *tty; + int status; + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + return; + } + + dbg("- port %d\n", port->number); + + mos7840_port = mos7840_get_port_private(port); + + if (mos7840_port == NULL) + return; + + if (!mos7840_port->open) { + dbg("%s\n", "port not opened"); + return; + } + + dbg("%s", "Entering .......... \n"); + + tty = port->tty; + if (!tty) { + dbg("%s - no tty available", __FUNCTION__); + return; + } + + /* if we are implementing XON/XOFF, send the stop character */ + if (I_IXOFF(tty)) { + unsigned char stop_char = STOP_CHAR(tty); + status = mos7840_write(port, &stop_char, 1); + if (status <= 0) { + return; + } + } + + /* if we are implementing RTS/CTS, toggle that line */ + if (tty->termios->c_cflag & CRTSCTS) { + mos7840_port->shadowMCR &= ~MCR_RTS; + status = 0; + status = + mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + mos7840_port->shadowMCR); + + if (status < 0) { + return; + } + } + + return; +} + +/***************************************************************************** + * mos7840_unthrottle + * this function is called by the tty driver when it wants to resume the data + * being read from the port (called after SerialThrottle is called) + *****************************************************************************/ +static void mos7840_unthrottle(struct usb_serial_port *port) +{ + struct tty_struct *tty; + int status; + struct moschip_port *mos7840_port = mos7840_get_port_private(port); + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + return; + } + + if (mos7840_port == NULL) + return; + + if (!mos7840_port->open) { + dbg("%s - port not opened", __FUNCTION__); + return; + } + + dbg("%s", "Entering .......... \n"); + + tty = port->tty; + if (!tty) { + dbg("%s - no tty available", __FUNCTION__); + return; + } + + /* if we are implementing XON/XOFF, send the start character */ + if (I_IXOFF(tty)) { + unsigned char start_char = START_CHAR(tty); + status = mos7840_write(port, &start_char, 1); + if (status <= 0) { + return; + } + } + + /* if we are implementing RTS/CTS, toggle that line */ + if (tty->termios->c_cflag & CRTSCTS) { + mos7840_port->shadowMCR |= MCR_RTS; + status = 0; + status = + mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + mos7840_port->shadowMCR); + if (status < 0) { + return; + } + } + + return; +} + +static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file) +{ + struct moschip_port *mos7840_port; + unsigned int result; + __u16 msr; + __u16 mcr; + int status = 0; + mos7840_port = mos7840_get_port_private(port); + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (mos7840_port == NULL) + return -ENODEV; + + status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr); + status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr); + result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) + | ((mcr & MCR_RTS) ? TIOCM_RTS : 0) + | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0) + | ((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0) + | ((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0) + | ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0) + | ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0); + + dbg("%s - 0x%04X", __FUNCTION__, result); + + return result; +} + +static int mos7840_tiocmset(struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear) +{ + struct moschip_port *mos7840_port; + unsigned int mcr; + unsigned int status; + + dbg("%s - port %d", __FUNCTION__, port->number); + + mos7840_port = mos7840_get_port_private(port); + + if (mos7840_port == NULL) + return -ENODEV; + + mcr = mos7840_port->shadowMCR; + if (clear & TIOCM_RTS) + mcr &= ~MCR_RTS; + if (clear & TIOCM_DTR) + mcr &= ~MCR_DTR; + if (clear & TIOCM_LOOP) + mcr &= ~MCR_LOOPBACK; + + if (set & TIOCM_RTS) + mcr |= MCR_RTS; + if (set & TIOCM_DTR) + mcr |= MCR_DTR; + if (set & TIOCM_LOOP) + mcr |= MCR_LOOPBACK; + + mos7840_port->shadowMCR = mcr; + + status = 0; + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr); + if (status < 0) { + dbg("setting MODEM_CONTROL_REGISTER Failed\n"); + return -1; + } + + return 0; +} + +/***************************************************************************** + * mos7840_calc_baud_rate_divisor + * this function calculates the proper baud rate divisor for the specified + * baud rate. + *****************************************************************************/ +static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor, + __u16 * clk_sel_val) +{ + + dbg("%s - %d", __FUNCTION__, baudRate); + + if (baudRate <= 115200) { + *divisor = 115200 / baudRate; + *clk_sel_val = 0x0; + } + if ((baudRate > 115200) && (baudRate <= 230400)) { + *divisor = 230400 / baudRate; + *clk_sel_val = 0x10; + } else if ((baudRate > 230400) && (baudRate <= 403200)) { + *divisor = 403200 / baudRate; + *clk_sel_val = 0x20; + } else if ((baudRate > 403200) && (baudRate <= 460800)) { + *divisor = 460800 / baudRate; + *clk_sel_val = 0x30; + } else if ((baudRate > 460800) && (baudRate <= 806400)) { + *divisor = 806400 / baudRate; + *clk_sel_val = 0x40; + } else if ((baudRate > 806400) && (baudRate <= 921600)) { + *divisor = 921600 / baudRate; + *clk_sel_val = 0x50; + } else if ((baudRate > 921600) && (baudRate <= 1572864)) { + *divisor = 1572864 / baudRate; + *clk_sel_val = 0x60; + } else if ((baudRate > 1572864) && (baudRate <= 3145728)) { + *divisor = 3145728 / baudRate; + *clk_sel_val = 0x70; + } + return 0; + +#ifdef NOTMCS7840 + + for (i = 0; i < ARRAY_SIZE(mos7840_divisor_table); i++) { + if (mos7840_divisor_table[i].BaudRate == baudrate) { + *divisor = mos7840_divisor_table[i].Divisor; + return 0; + } + } + + /* After trying for all the standard baud rates * + * Try calculating the divisor for this baud rate */ + + if (baudrate > 75 && baudrate < 230400) { + /* get the divisor */ + custom = (__u16) (230400L / baudrate); + + /* Check for round off */ + round1 = (__u16) (2304000L / baudrate); + round = (__u16) (round1 - (custom * 10)); + if (round > 4) { + custom++; + } + *divisor = custom; + + dbg(" Baud %d = %d\n", baudrate, custom); + return 0; + } + + dbg("%s\n", " Baud calculation Failed..."); + return -1; +#endif +} + +/***************************************************************************** + * mos7840_send_cmd_write_baud_rate + * this function sends the proper command to change the baud rate of the + * specified port. + *****************************************************************************/ + +static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, + int baudRate) +{ + int divisor = 0; + int status; + __u16 Data; + unsigned char number; + __u16 clk_sel_val; + struct usb_serial_port *port; + + if (mos7840_port == NULL) + return -1; + + port = (struct usb_serial_port *)mos7840_port->port; + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + return -1; + } + + if (mos7840_serial_paranoia_check(port->serial, __FUNCTION__)) { + dbg("%s", "Invalid Serial \n"); + return -1; + } + + dbg("%s", "Entering .......... \n"); + + number = mos7840_port->port->number - mos7840_port->port->serial->minor; + + dbg("%s - port = %d, baud = %d", __FUNCTION__, + mos7840_port->port->number, baudRate); + //reset clk_uart_sel in spregOffset + if (baudRate > 115200) { +#ifdef HW_flow_control + //NOTE: need to see the pther register to modify + //setting h/w flow control bit to 1; + status = 0; + Data = 0x2b; + mos7840_port->shadowMCR = Data; + status = + mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + if (status < 0) { + dbg("Writing spreg failed in set_serial_baud\n"); + return -1; + } +#endif + + } else { +#ifdef HW_flow_control + //setting h/w flow control bit to 0; + status = 0; + Data = 0xb; + mos7840_port->shadowMCR = Data; + status = + mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + if (status < 0) { + dbg("Writing spreg failed in set_serial_baud\n"); + return -1; + } +#endif + + } + + if (1) //baudRate <= 115200) + { + clk_sel_val = 0x0; + Data = 0x0; + status = 0; + status = + mos7840_calc_baud_rate_divisor(baudRate, &divisor, + &clk_sel_val); + status = + mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, + &Data); + if (status < 0) { + dbg("reading spreg failed in set_serial_baud\n"); + return -1; + } + Data = (Data & 0x8f) | clk_sel_val; + status = 0; + status = + mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); + if (status < 0) { + dbg("Writing spreg failed in set_serial_baud\n"); + return -1; + } + /* Calculate the Divisor */ + + if (status) { + err("%s - bad baud rate", __FUNCTION__); + dbg("%s\n", "bad baud rate"); + return status; + } + /* Enable access to divisor latch */ + Data = mos7840_port->shadowLCR | SERIAL_LCR_DLAB; + mos7840_port->shadowLCR = Data; + mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); + + /* Write the divisor */ + Data = (unsigned char)(divisor & 0xff); + dbg("set_serial_baud Value to write DLL is %x\n", Data); + mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data); + + Data = (unsigned char)((divisor & 0xff00) >> 8); + dbg("set_serial_baud Value to write DLM is %x\n", Data); + mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data); + + /* Disable access to divisor latch */ + Data = mos7840_port->shadowLCR & ~SERIAL_LCR_DLAB; + mos7840_port->shadowLCR = Data; + mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); + + } + + return status; +} + +/***************************************************************************** + * mos7840_change_port_settings + * This routine is called to set the UART on the device to match + * the specified new settings. + *****************************************************************************/ + +static void mos7840_change_port_settings(struct moschip_port *mos7840_port, + struct termios *old_termios) +{ + struct tty_struct *tty; + int baud; + unsigned cflag; + unsigned iflag; + __u8 lData; + __u8 lParity; + __u8 lStop; + int status; + __u16 Data; + struct usb_serial_port *port; + struct usb_serial *serial; + + if (mos7840_port == NULL) + return; + + port = (struct usb_serial_port *)mos7840_port->port; + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + return; + } + + if (mos7840_serial_paranoia_check(port->serial, __FUNCTION__)) { + dbg("%s", "Invalid Serial \n"); + return; + } + + serial = port->serial; + + dbg("%s - port %d", __FUNCTION__, mos7840_port->port->number); + + if (!mos7840_port->open) { + dbg("%s - port not opened", __FUNCTION__); + return; + } + + tty = mos7840_port->port->tty; + + if ((!tty) || (!tty->termios)) { + dbg("%s - no tty structures", __FUNCTION__); + return; + } + + dbg("%s", "Entering .......... \n"); + + lData = LCR_BITS_8; + lStop = LCR_STOP_1; + lParity = LCR_PAR_NONE; + + cflag = tty->termios->c_cflag; + iflag = tty->termios->c_iflag; + + /* Change the number of bits */ + if (cflag & CSIZE) { + switch (cflag & CSIZE) { + case CS5: + lData = LCR_BITS_5; + break; + + case CS6: + lData = LCR_BITS_6; + break; + + case CS7: + lData = LCR_BITS_7; + break; + default: + case CS8: + lData = LCR_BITS_8; + break; + } + } + /* Change the Parity bit */ + if (cflag & PARENB) { + if (cflag & PARODD) { + lParity = LCR_PAR_ODD; + dbg("%s - parity = odd", __FUNCTION__); + } else { + lParity = LCR_PAR_EVEN; + dbg("%s - parity = even", __FUNCTION__); + } + + } else { + dbg("%s - parity = none", __FUNCTION__); + } + + if (cflag & CMSPAR) { + lParity = lParity | 0x20; + } + + /* Change the Stop bit */ + if (cflag & CSTOPB) { + lStop = LCR_STOP_2; + dbg("%s - stop bits = 2", __FUNCTION__); + } else { + lStop = LCR_STOP_1; + dbg("%s - stop bits = 1", __FUNCTION__); + } + + /* Update the LCR with the correct value */ + mos7840_port->shadowLCR &= + ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); + mos7840_port->shadowLCR |= (lData | lParity | lStop); + + dbg("mos7840_change_port_settings mos7840_port->shadowLCR is %x\n", + mos7840_port->shadowLCR); + /* Disable Interrupts */ + Data = 0x00; + mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); + + Data = 0x00; + mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); + + Data = 0xcf; + mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); + + /* Send the updated LCR value to the mos7840 */ + Data = mos7840_port->shadowLCR; + + mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); + + Data = 0x00b; + mos7840_port->shadowMCR = Data; + mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + Data = 0x00b; + mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + + /* set up the MCR register and send it to the mos7840 */ + + mos7840_port->shadowMCR = MCR_MASTER_IE; + if (cflag & CBAUD) { + mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS); + } + + if (cflag & CRTSCTS) { + mos7840_port->shadowMCR |= (MCR_XON_ANY); + + } else { + mos7840_port->shadowMCR &= ~(MCR_XON_ANY); + } + + Data = mos7840_port->shadowMCR; + mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(tty); + + if (!baud) { + /* pick a default, any default... */ + dbg("%s\n", "Picked default baud..."); + baud = 9600; + } + + dbg("%s - baud rate = %d", __FUNCTION__, baud); + status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud); + + /* Enable Interrupts */ + Data = 0x0c; + mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); + + if (mos7840_port->read_urb->status != -EINPROGRESS) { + mos7840_port->read_urb->dev = serial->dev; + + status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); + + if (status) { + dbg(" usb_submit_urb(read bulk) failed, status = %d", + status); + } + } + wake_up(&mos7840_port->delta_msr_wait); + mos7840_port->delta_msr_cond = 1; + dbg("mos7840_change_port_settings mos7840_port->shadowLCR is End %x\n", + mos7840_port->shadowLCR); + + return; +} + +/***************************************************************************** + * mos7840_set_termios + * this function is called by the tty driver when it wants to change + * the termios structure + *****************************************************************************/ + +static void mos7840_set_termios(struct usb_serial_port *port, + struct termios *old_termios) +{ + int status; + unsigned int cflag; + struct usb_serial *serial; + struct moschip_port *mos7840_port; + struct tty_struct *tty; + dbg("mos7840_set_termios: START\n"); + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + return; + } + + serial = port->serial; + + if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) { + dbg("%s", "Invalid Serial \n"); + return; + } + + mos7840_port = mos7840_get_port_private(port); + + if (mos7840_port == NULL) + return; + + tty = port->tty; + + if (!port->tty || !port->tty->termios) { + dbg("%s - no tty or termios", __FUNCTION__); + return; + } + + if (!mos7840_port->open) { + dbg("%s - port not opened", __FUNCTION__); + return; + } + + dbg("%s\n", "setting termios - "); + + cflag = tty->termios->c_cflag; + + if (!cflag) { + dbg("%s %s\n", __FUNCTION__, "cflag is NULL"); + return; + } + + /* check that they really want us to change something */ + if (old_termios) { + if ((cflag == old_termios->c_cflag) && + (RELEVANT_IFLAG(tty->termios->c_iflag) == + RELEVANT_IFLAG(old_termios->c_iflag))) { + dbg("%s\n", "Nothing to change"); + return; + } + } + + dbg("%s - clfag %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 - port %d", __FUNCTION__, port->number); + + /* change the port settings to the new ones specified */ + + mos7840_change_port_settings(mos7840_port, old_termios); + + if (!mos7840_port->read_urb) { + dbg("%s", "URB KILLED !!!!!\n"); + return; + } + + if (mos7840_port->read_urb->status != -EINPROGRESS) { + mos7840_port->read_urb->dev = serial->dev; + status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); + if (status) { + dbg(" usb_submit_urb(read bulk) failed, status = %d", + status); + } + } + return; +} + +/***************************************************************************** + * mos7840_get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + *****************************************************************************/ + +static int mos7840_get_lsr_info(struct moschip_port *mos7840_port, + unsigned int *value) +{ + int count; + unsigned int result = 0; + + count = mos7840_chars_in_buffer(mos7840_port->port); + if (count == 0) { + dbg("%s -- Empty", __FUNCTION__); + result = TIOCSER_TEMT; + } + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +/***************************************************************************** + * mos7840_get_bytes_avail - get number of bytes available + * + * Purpose: Let user call ioctl to get the count of number of bytes available. + *****************************************************************************/ + +static int mos7840_get_bytes_avail(struct moschip_port *mos7840_port, + unsigned int *value) +{ + unsigned int result = 0; + struct tty_struct *tty = mos7840_port->port->tty; + + if (!tty) + return -ENOIOCTLCMD; + + result = tty->read_cnt; + + dbg("%s(%d) = %d", __FUNCTION__, mos7840_port->port->number, result); + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + + return -ENOIOCTLCMD; +} + +/***************************************************************************** + * mos7840_set_modem_info + * function to set modem info + *****************************************************************************/ + +static int mos7840_set_modem_info(struct moschip_port *mos7840_port, + unsigned int cmd, unsigned int *value) +{ + unsigned int mcr; + unsigned int arg; + __u16 Data; + int status; + struct usb_serial_port *port; + + if (mos7840_port == NULL) + return -1; + + port = (struct usb_serial_port *)mos7840_port->port; + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + return -1; + } + + mcr = mos7840_port->shadowMCR; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + mcr |= MCR_RTS; + if (arg & TIOCM_DTR) + mcr |= MCR_RTS; + if (arg & TIOCM_LOOP) + mcr |= MCR_LOOPBACK; + break; + + case TIOCMBIC: + if (arg & TIOCM_RTS) + mcr &= ~MCR_RTS; + if (arg & TIOCM_DTR) + mcr &= ~MCR_RTS; + if (arg & TIOCM_LOOP) + mcr &= ~MCR_LOOPBACK; + break; + + case TIOCMSET: + /* turn off the RTS and DTR and LOOPBACK + * and then only turn on what was asked to */ + mcr &= ~(MCR_RTS | MCR_DTR | MCR_LOOPBACK); + mcr |= ((arg & TIOCM_RTS) ? MCR_RTS : 0); + mcr |= ((arg & TIOCM_DTR) ? MCR_DTR : 0); + mcr |= ((arg & TIOCM_LOOP) ? MCR_LOOPBACK : 0); + break; + } + + mos7840_port->shadowMCR = mcr; + + Data = mos7840_port->shadowMCR; + status = 0; + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + if (status < 0) { + dbg("setting MODEM_CONTROL_REGISTER Failed\n"); + return -1; + } + + return 0; +} + +/***************************************************************************** + * mos7840_get_modem_info + * function to get modem info + *****************************************************************************/ + +static int mos7840_get_modem_info(struct moschip_port *mos7840_port, + unsigned int *value) +{ + unsigned int result = 0; + __u16 msr; + unsigned int mcr = mos7840_port->shadowMCR; + int status = 0; + status = + mos7840_get_uart_reg(mos7840_port->port, MODEM_STATUS_REGISTER, + &msr); + result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */ + |((mcr & MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */ + |((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */ + |((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0) /* 0x040 */ + |((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0) /* 0x080 */ + |((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0); /* 0x100 */ + + dbg("%s -- %x", __FUNCTION__, result); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +/***************************************************************************** + * mos7840_get_serial_info + * function to get information about serial port + *****************************************************************************/ + +static int mos7840_get_serial_info(struct moschip_port *mos7840_port, + struct serial_struct *retinfo) +{ + struct serial_struct tmp; + + if (mos7840_port == NULL) + return -1; + + if (!retinfo) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + + tmp.type = PORT_16550A; + tmp.line = mos7840_port->port->serial->minor; + tmp.port = mos7840_port->port->number; + tmp.irq = 0; + tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; + tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; + tmp.baud_base = 9600; + tmp.close_delay = 5 * HZ; + tmp.closing_wait = 30 * HZ; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +/***************************************************************************** + * SerialIoctl + * this function handles any ioctl calls to the driver + *****************************************************************************/ + +static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct moschip_port *mos7840_port; + struct tty_struct *tty; + + struct async_icount cnow; + struct async_icount cprev; + struct serial_icounter_struct icount; + int mosret = 0; + int retval; + struct tty_ldisc *ld; + + if (mos7840_port_paranoia_check(port, __FUNCTION__)) { + dbg("%s", "Invalid port \n"); + return -1; + } + + mos7840_port = mos7840_get_port_private(port); + tty = mos7840_port->port->tty; + + if (mos7840_port == NULL) + return -1; + + dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd); + + switch (cmd) { + /* return number of bytes available */ + + case TIOCINQ: + dbg("%s (%d) TIOCINQ", __FUNCTION__, port->number); + return mos7840_get_bytes_avail(mos7840_port, + (unsigned int *)arg); + break; + + case TIOCOUTQ: + dbg("%s (%d) TIOCOUTQ", __FUNCTION__, port->number); + return put_user(tty->driver->chars_in_buffer ? + tty->driver->chars_in_buffer(tty) : 0, + (int __user *)arg); + break; + + case TCFLSH: + retval = tty_check_change(tty); + if (retval) + return retval; + + ld = tty_ldisc_ref(tty); + switch (arg) { + case TCIFLUSH: + if (ld && ld->flush_buffer) + ld->flush_buffer(tty); + break; + case TCIOFLUSH: + if (ld && ld->flush_buffer) + ld->flush_buffer(tty); + /* fall through */ + case TCOFLUSH: + if (tty->driver->flush_buffer) + tty->driver->flush_buffer(tty); + break; + default: + tty_ldisc_deref(ld); + return -EINVAL; + } + tty_ldisc_deref(ld); + return 0; + + case TCGETS: + if (kernel_termios_to_user_termios + ((struct termios __user *)arg, tty->termios)) + return -EFAULT; + return 0; + + case TIOCSERGETLSR: + dbg("%s (%d) TIOCSERGETLSR", __FUNCTION__, port->number); + return mos7840_get_lsr_info(mos7840_port, (unsigned int *)arg); + return 0; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__, + port->number); + mosret = + mos7840_set_modem_info(mos7840_port, cmd, + (unsigned int *)arg); + return mosret; + + case TIOCMGET: + dbg("%s (%d) TIOCMGET", __FUNCTION__, port->number); + return mos7840_get_modem_info(mos7840_port, + (unsigned int *)arg); + + case TIOCGSERIAL: + dbg("%s (%d) TIOCGSERIAL", __FUNCTION__, port->number); + return mos7840_get_serial_info(mos7840_port, + (struct serial_struct *)arg); + + case TIOCSSERIAL: + dbg("%s (%d) TIOCSSERIAL", __FUNCTION__, port->number); + break; + + case TIOCMIWAIT: + dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number); + cprev = mos7840_port->icount; + while (1) { + //interruptible_sleep_on(&mos7840_port->delta_msr_wait); + mos7840_port->delta_msr_cond = 0; + wait_event_interruptible(mos7840_port->delta_msr_wait, + (mos7840_port-> + delta_msr_cond == 1)); + + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + cnow = mos7840_port->icount; + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + break; + + case TIOCGICOUNT: + cnow = mos7840_port->icount; + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __FUNCTION__, + port->number, icount.rx, icount.tx); + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + + case TIOCEXBAUD: + return 0; + default: + break; + } + + return -ENOIOCTLCMD; +} + +static int mos7840_calc_num_ports(struct usb_serial *serial) +{ + + dbg("numberofendpoints: %d \n", + (int)serial->interface->cur_altsetting->desc.bNumEndpoints); + dbg("numberofendpoints: %d \n", + (int)serial->interface->altsetting->desc.bNumEndpoints); + if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) { + mos7840_num_ports = 2; + serial->type->num_ports = 2; + } else if (serial->interface->cur_altsetting->desc.bNumEndpoints == 9) { + mos7840_num_ports = 4; + serial->type->num_bulk_in = 4; + serial->type->num_bulk_out = 4; + serial->type->num_ports = 4; + } + + return mos7840_num_ports; +} + +/**************************************************************************** + * mos7840_startup + ****************************************************************************/ + +static int mos7840_startup(struct usb_serial *serial) +{ + struct moschip_port *mos7840_port; + struct usb_device *dev; + int i, status; + + __u16 Data; + dbg("%s \n", " mos7840_startup :entering.........."); + + if (!serial) { + dbg("%s\n", "Invalid Handler"); + return -1; + } + + dev = serial->dev; + + dbg("%s\n", "Entering..."); + + /* we set up the pointers to the endpoints in the mos7840_open * + * function, as the structures aren't created yet. */ + + /* set up port private structures */ + for (i = 0; i < serial->num_ports; ++i) { + mos7840_port = kmalloc(sizeof(struct moschip_port), GFP_KERNEL); + if (mos7840_port == NULL) { + err("%s - Out of memory", __FUNCTION__); + return -ENOMEM; + } + memset(mos7840_port, 0, sizeof(struct moschip_port)); + + /* Initialize all port interrupt end point to port 0 int endpoint * + * Our device has only one interrupt end point comman to all port */ + + mos7840_port->port = serial->port[i]; + mos7840_set_port_private(serial->port[i], mos7840_port); + + mos7840_port->port_num = ((serial->port[i]->number - + (serial->port[i]->serial->minor)) + + 1); + + if (mos7840_port->port_num == 1) { + mos7840_port->SpRegOffset = 0x0; + mos7840_port->ControlRegOffset = 0x1; + mos7840_port->DcrRegOffset = 0x4; + } else if ((mos7840_port->port_num == 2) + && (mos7840_num_ports == 4)) { + mos7840_port->SpRegOffset = 0x8; + mos7840_port->ControlRegOffset = 0x9; + mos7840_port->DcrRegOffset = 0x16; + } else if ((mos7840_port->port_num == 2) + && (mos7840_num_ports == 2)) { + mos7840_port->SpRegOffset = 0xa; + mos7840_port->ControlRegOffset = 0xb; + mos7840_port->DcrRegOffset = 0x19; + } else if ((mos7840_port->port_num == 3) + && (mos7840_num_ports == 4)) { + mos7840_port->SpRegOffset = 0xa; + mos7840_port->ControlRegOffset = 0xb; + mos7840_port->DcrRegOffset = 0x19; + } else if ((mos7840_port->port_num == 4) + && (mos7840_num_ports == 4)) { + mos7840_port->SpRegOffset = 0xc; + mos7840_port->ControlRegOffset = 0xd; + mos7840_port->DcrRegOffset = 0x1c; + } + mos7840_dump_serial_port(mos7840_port); + + mos7840_set_port_private(serial->port[i], mos7840_port); + + //enable rx_disable bit in control register + + status = + mos7840_get_reg_sync(serial->port[i], + mos7840_port->ControlRegOffset, &Data); + if (status < 0) { + dbg("Reading ControlReg failed status-0x%x\n", status); + break; + } else + dbg("ControlReg Reading success val is %x, status%d\n", + Data, status); + Data |= 0x08; //setting driver done bit + Data |= 0x04; //sp1_bit to have cts change reflect in modem status reg + + //Data |= 0x20; //rx_disable bit + status = 0; + status = + mos7840_set_reg_sync(serial->port[i], + mos7840_port->ControlRegOffset, Data); + if (status < 0) { + dbg("Writing ControlReg failed(rx_disable) status-0x%x\n", status); + break; + } else + dbg("ControlReg Writing success(rx_disable) status%d\n", + status); + + //Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 and 0x24 in DCR3 + Data = 0x01; + status = 0; + status = + mos7840_set_reg_sync(serial->port[i], + (__u16) (mos7840_port->DcrRegOffset + + 0), Data); + if (status < 0) { + dbg("Writing DCR0 failed status-0x%x\n", status); + break; + } else + dbg("DCR0 Writing success status%d\n", status); + + Data = 0x05; + status = 0; + status = + mos7840_set_reg_sync(serial->port[i], + (__u16) (mos7840_port->DcrRegOffset + + 1), Data); + if (status < 0) { + dbg("Writing DCR1 failed status-0x%x\n", status); + break; + } else + dbg("DCR1 Writing success status%d\n", status); + + Data = 0x24; + status = 0; + status = + mos7840_set_reg_sync(serial->port[i], + (__u16) (mos7840_port->DcrRegOffset + + 2), Data); + if (status < 0) { + dbg("Writing DCR2 failed status-0x%x\n", status); + break; + } else + dbg("DCR2 Writing success status%d\n", status); + + // write values in clkstart0x0 and clkmulti 0x20 + Data = 0x0; + status = 0; + status = + mos7840_set_reg_sync(serial->port[i], + CLK_START_VALUE_REGISTER, Data); + if (status < 0) { + dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); + break; + } else + dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status); + + Data = 0x20; + status = 0; + status = + mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER, + Data); + if (status < 0) { + dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n", + status); + break; + } else + dbg("CLK_MULTI_REGISTER Writing success status%d\n", + status); + + //write value 0x0 to scratchpad register + Data = 0x00; + status = 0; + status = + mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER, + Data); + if (status < 0) { + dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", + status); + break; + } else + dbg("SCRATCH_PAD_REGISTER Writing success status%d\n", + status); + + //Zero Length flag register + if ((mos7840_port->port_num != 1) + && (mos7840_num_ports == 2)) { + + Data = 0xff; + status = 0; + status = mos7840_set_reg_sync(serial->port[i], + (__u16) (ZLP_REG1 + + ((__u16) + mos7840_port-> + port_num)), + Data); + dbg("ZLIP offset%x\n", + (__u16) (ZLP_REG1 + + ((__u16) mos7840_port->port_num))); + if (status < 0) { + dbg("Writing ZLP_REG%d failed status-0x%x\n", + i + 2, status); + break; + } else + dbg("ZLP_REG%d Writing success status%d\n", + i + 2, status); + } else { + Data = 0xff; + status = 0; + status = mos7840_set_reg_sync(serial->port[i], + (__u16) (ZLP_REG1 + + ((__u16) + mos7840_port-> + port_num) - + 0x1), Data); + dbg("ZLIP offset%x\n", + (__u16) (ZLP_REG1 + + ((__u16) mos7840_port->port_num) - 0x1)); + if (status < 0) { + dbg("Writing ZLP_REG%d failed status-0x%x\n", + i + 1, status); + break; + } else + dbg("ZLP_REG%d Writing success status%d\n", + i + 1, status); + + } + mos7840_port->control_urb = usb_alloc_urb(0, SLAB_ATOMIC); + mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL); + + } + + //Zero Length flag enable + Data = 0x0f; + status = 0; + 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; + } else + dbg("ZLP_REG5 Writing success status%d\n", status); + + /* setting configuration feature to one */ + usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + (__u8) 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 5 * HZ); + return 0; +} + +/**************************************************************************** + * mos7840_shutdown + * This function is called whenever the device is removed from the usb bus. + ****************************************************************************/ + +static void mos7840_shutdown(struct usb_serial *serial) +{ + int i; + struct moschip_port *mos7840_port; + dbg("%s \n", " shutdown :entering.........."); + + if (!serial) { + dbg("%s", "Invalid Handler \n"); + return; + } + + /* check for the ports to be closed,close the ports and disconnect */ + + /* free private structure allocated for serial port * + * stop reads and writes on all ports */ + + for (i = 0; i < serial->num_ports; ++i) { + mos7840_port = mos7840_get_port_private(serial->port[i]); + kfree(mos7840_port->ctrl_buf); + usb_kill_urb(mos7840_port->control_urb); + kfree(mos7840_port); + mos7840_set_port_private(serial->port[i], NULL); + } + + dbg("%s\n", "Thank u :: "); + +} + +static struct usb_serial_driver moschip7840_4port_device = { + .driver = { + .owner = THIS_MODULE, + .name = "mos7840", + }, + .description = DRIVER_DESC, + .id_table = moschip_port_id_table, + .num_interrupt_in = 1, //NUM_DONT_CARE,//1, +#ifdef check + .num_bulk_in = 4, + .num_bulk_out = 4, + .num_ports = 4, +#endif + .open = mos7840_open, + .close = mos7840_close, + .write = mos7840_write, + .write_room = mos7840_write_room, + .chars_in_buffer = mos7840_chars_in_buffer, + .throttle = mos7840_throttle, + .unthrottle = mos7840_unthrottle, + .calc_num_ports = mos7840_calc_num_ports, +#ifdef MCSSerialProbe + .probe = mos7840_serial_probe, +#endif + .ioctl = mos7840_ioctl, + .set_termios = mos7840_set_termios, + .break_ctl = mos7840_break, + .tiocmget = mos7840_tiocmget, + .tiocmset = mos7840_tiocmset, + .attach = mos7840_startup, + .shutdown = mos7840_shutdown, + .read_bulk_callback = mos7840_bulk_in_callback, + .read_int_callback = mos7840_interrupt_callback, +}; + +static struct usb_driver io_driver = { + .name = "mos7840", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = moschip_id_table_combined, +}; + +/**************************************************************************** + * moschip7840_init + * This is called by the module subsystem, or on startup to initialize us + ****************************************************************************/ +static int __init moschip7840_init(void) +{ + int retval; + + dbg("%s \n", " mos7840_init :entering.........."); + + /* Register with the usb serial */ + retval = usb_serial_register(&moschip7840_4port_device); + + if (retval) + goto failed_port_device_register; + + dbg("%s\n", "Entring..."); + info(DRIVER_DESC " " DRIVER_VERSION); + + /* Register with the usb */ + retval = usb_register(&io_driver); + + if (retval) + goto failed_usb_register; + + if (retval == 0) { + dbg("%s\n", "Leaving..."); + return 0; + } + + failed_usb_register: + usb_serial_deregister(&moschip7840_4port_device); + + failed_port_device_register: + + return retval; +} + +/**************************************************************************** + * moschip7840_exit + * Called when the driver is about to be unloaded. + ****************************************************************************/ +static void __exit moschip7840_exit(void) +{ + + dbg("%s \n", " mos7840_exit :entering.........."); + + usb_deregister(&io_driver); + + usb_serial_deregister(&moschip7840_4port_device); + + dbg("%s\n", "Entring..."); +} + +module_init(moschip7840_init); +module_exit(moschip7840_exit); + +/* Module information */ +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 7f544081032e..ac3f8b5d2c49 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -14,7 +14,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> static int debug; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 6dcdb5f598b8..a764ff4e326c 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -35,7 +35,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -47,7 +46,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> static int debug; @@ -257,14 +256,14 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf return (0); } - spin_lock(&wport->lock); + spin_lock_bh(&wport->lock); if (wport->write_urb_busy) { - spin_unlock(&wport->lock); + spin_unlock_bh(&wport->lock); dbg("%s - already writing", __FUNCTION__); return 0; } wport->write_urb_busy = 1; - spin_unlock(&wport->lock); + spin_unlock_bh(&wport->lock); count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index b0861b61bba7..c856e6f40e22 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -9,39 +9,14 @@ Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org> - History: - - 2005-05-19 v0.1 Initial version, based on incomplete docs - and analysis of misbehavior with the standard driver - 2005-05-20 v0.2 Extended the input buffer to avoid losing - random 64-byte chunks of data - 2005-05-21 v0.3 implemented chars_in_buffer() - turned on low_latency - simplified the code somewhat - 2005-05-24 v0.4 option_write() sometimes deadlocked under heavy load - removed some dead code - added sponsor notice - coding style clean-up - 2005-06-20 v0.4.1 add missing braces :-/ - killed end-of-line whitespace - 2005-07-15 v0.4.2 rename WLAN product to FUSION, add FUSION2 - 2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard - 2005-09-20 v0.4.4 increased recv buffer size: the card sometimes - wants to send >2000 bytes. - 2006-04-10 v0.5 fixed two array overrun errors :-/ - 2006-04-21 v0.5.1 added support for Sierra Wireless MC8755 - 2006-05-15 v0.6 re-enable multi-port support - 2006-06-01 v0.6.1 add COBRA - 2006-06-01 v0.6.2 add backwards-compatibility stuff - 2006-06-01 v0.6.3 add Novatel Wireless - 2006-06-01 v0.7 Option => GSM + History: see the git log. Work sponsored by: Sigos GmbH, Germany <info@sigos.de> This driver exists because the "normal" serial driver doesn't work too well with GSM modems. Issues: - data loss -- one single Receive URB is not nearly enough - - nonstandard flow (Option devices) and multiplex (Sierra) control + - nonstandard flow (Option devices) control - controlling the baud rate doesn't make sense This driver is named "option" because the most common device it's @@ -53,11 +28,10 @@ device features. */ -#define DRIVER_VERSION "v0.7.0" +#define DRIVER_VERSION "v0.7.1" #define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>" #define DRIVER_DESC "USB Driver for GSM modems" -#include <linux/config.h> #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/errno.h> @@ -65,7 +39,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> /* Function prototypes */ static int option_open(struct usb_serial_port *port, struct file *filp); @@ -96,27 +70,29 @@ static int option_send_setup(struct usb_serial_port *port); #define OPTION_VENDOR_ID 0x0AF0 #define HUAWEI_VENDOR_ID 0x12D1 #define AUDIOVOX_VENDOR_ID 0x0F3D -#define SIERRAWIRELESS_VENDOR_ID 0x1199 #define NOVATELWIRELESS_VENDOR_ID 0x1410 +#define ANYDATA_VENDOR_ID 0x16d5 #define OPTION_PRODUCT_OLD 0x5000 #define OPTION_PRODUCT_FUSION 0x6000 #define OPTION_PRODUCT_FUSION2 0x6300 #define OPTION_PRODUCT_COBRA 0x6500 +#define OPTION_PRODUCT_COBRA2 0x6600 #define HUAWEI_PRODUCT_E600 0x1001 #define AUDIOVOX_PRODUCT_AIRCARD 0x0112 -#define SIERRAWIRELESS_PRODUCT_MC8755 0x6802 #define NOVATELWIRELESS_PRODUCT_U740 0x1400 +#define ANYDATA_PRODUCT_ID 0x6501 static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, - { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, + { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -125,13 +101,11 @@ static struct usb_device_id option_ids1[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, - { } /* Terminating entry */ -}; -static struct usb_device_id option_ids3[] = { - { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) }, + { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -148,37 +122,11 @@ static struct usb_driver option_driver = { /* The card has three separate interfaces, which the serial driver * recognizes separately, thus num_port=1. */ -static struct usb_serial_driver option_3port_device = { - .driver = { - .owner = THIS_MODULE, - .name = "option", - }, - .description = "GSM modem (3-port)", - .id_table = option_ids3, - .num_interrupt_in = NUM_DONT_CARE, - .num_bulk_in = NUM_DONT_CARE, - .num_bulk_out = NUM_DONT_CARE, - .num_ports = 3, - .open = option_open, - .close = option_close, - .write = option_write, - .write_room = option_write_room, - .chars_in_buffer = option_chars_in_buffer, - .throttle = option_rx_throttle, - .unthrottle = option_rx_unthrottle, - .set_termios = option_set_termios, - .break_ctl = option_break_ctl, - .tiocmget = option_tiocmget, - .tiocmset = option_tiocmset, - .attach = option_startup, - .shutdown = option_shutdown, - .read_int_callback = option_instat_callback, -}; static struct usb_serial_driver option_1port_device = { .driver = { .owner = THIS_MODULE, - .name = "option", + .name = "option1", }, .description = "GSM modem (1-port)", .id_table = option_ids1, @@ -242,9 +190,6 @@ static int __init option_init(void) retval = usb_serial_register(&option_1port_device); if (retval) goto failed_1port_device_register; - retval = usb_serial_register(&option_3port_device); - if (retval) - goto failed_3port_device_register; retval = usb_register(&option_driver); if (retval) goto failed_driver_register; @@ -254,8 +199,6 @@ static int __init option_init(void) return 0; failed_driver_register: - usb_serial_deregister (&option_3port_device); -failed_3port_device_register: usb_serial_deregister (&option_1port_device); failed_1port_device_register: return retval; @@ -264,7 +207,6 @@ failed_1port_device_register: static void __exit option_exit(void) { usb_deregister (&option_driver); - usb_serial_deregister (&option_3port_device); usb_serial_deregister (&option_1port_device); } @@ -653,7 +595,6 @@ static void option_setup_urbs(struct usb_serial *serial) dbg("%s", __FUNCTION__); - for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; portdata = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index d88704387202..9c18173e33fb 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -14,7 +14,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -28,7 +27,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "pl2303.h" /* @@ -53,6 +52,7 @@ struct pl2303_buf { static struct usb_device_id id_table [] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, @@ -79,11 +79,14 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) }, { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) }, { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) }, - { USB_DEVICE(OTI_VENDOR_ID, OTI_PRODUCT_ID) }, + { 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) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver pl2303_driver = { .name = "pl2303", @@ -126,65 +129,6 @@ static struct usb_driver pl2303_driver = { #define UART_OVERRUN_ERROR 0x40 #define UART_CTS 0x80 -/* function prototypes for a PL2303 serial converter */ -static int pl2303_open (struct usb_serial_port *port, struct file *filp); -static void pl2303_close (struct usb_serial_port *port, struct file *filp); -static void pl2303_set_termios (struct usb_serial_port *port, - struct termios *old); -static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg); -static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs); -static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs); -static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs); -static int pl2303_write (struct usb_serial_port *port, - const unsigned char *buf, int count); -static void pl2303_send (struct usb_serial_port *port); -static int pl2303_write_room(struct usb_serial_port *port); -static int pl2303_chars_in_buffer(struct usb_serial_port *port); -static void pl2303_break_ctl(struct usb_serial_port *port,int break_state); -static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file); -static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, - unsigned int set, unsigned int clear); -static int pl2303_startup (struct usb_serial *serial); -static void pl2303_shutdown (struct usb_serial *serial); -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, - unsigned int count); -static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, - unsigned int count); - - -/* All of the device info needed for the PL2303 SIO serial converter */ -static struct usb_serial_driver pl2303_device = { - .driver = { - .owner = THIS_MODULE, - .name = "pl2303", - }, - .id_table = id_table, - .num_interrupt_in = NUM_DONT_CARE, - .num_bulk_in = 1, - .num_bulk_out = 1, - .num_ports = 1, - .open = pl2303_open, - .close = pl2303_close, - .write = pl2303_write, - .ioctl = pl2303_ioctl, - .break_ctl = pl2303_break_ctl, - .set_termios = pl2303_set_termios, - .tiocmget = pl2303_tiocmget, - .tiocmset = pl2303_tiocmset, - .read_bulk_callback = pl2303_read_bulk_callback, - .read_int_callback = pl2303_read_int_callback, - .write_bulk_callback = pl2303_write_bulk_callback, - .write_room = pl2303_write_room, - .chars_in_buffer = pl2303_chars_in_buffer, - .attach = pl2303_startup, - .shutdown = pl2303_shutdown, -}; enum pl2303_type { type_0, /* don't know the difference between type 0 and */ @@ -203,8 +147,166 @@ struct pl2303_private { enum pl2303_type type; }; +/* + * pl2303_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) +{ + struct pl2303_buf *pb; + + if (size == 0) + return NULL; + + pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); + if (pb == NULL) + return NULL; + + pb->buf_buf = kmalloc(size, GFP_KERNEL); + if (pb->buf_buf == NULL) { + kfree(pb); + return NULL; + } + + pb->buf_size = size; + pb->buf_get = pb->buf_put = pb->buf_buf; -static int pl2303_startup (struct usb_serial *serial) + return pb; +} + +/* + * pl2303_buf_free + * + * Free the buffer and all associated memory. + */ +static void pl2303_buf_free(struct pl2303_buf *pb) +{ + if (pb) { + kfree(pb->buf_buf); + kfree(pb); + } +} + +/* + * pl2303_buf_clear + * + * Clear out all data in the circular buffer. + */ +static void pl2303_buf_clear(struct pl2303_buf *pb) +{ + if (pb != NULL) + pb->buf_get = pb->buf_put; + /* equivalent to a get of all data available */ +} + +/* + * pl2303_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) +{ + if (pb == NULL) + return 0; + + return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); +} + +/* + * pl2303_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) +{ + if (pb == NULL) + return 0; + + return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); +} + +/* + * pl2303_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, + unsigned int count) +{ + unsigned int len; + + if (pb == NULL) + return 0; + + len = pl2303_buf_space_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_put; + if (count > len) { + memcpy(pb->buf_put, buf, len); + memcpy(pb->buf_buf, buf+len, count - len); + pb->buf_put = pb->buf_buf + count - len; + } else { + memcpy(pb->buf_put, buf, count); + if (count < len) + pb->buf_put += count; + else /* count == len */ + pb->buf_put = pb->buf_buf; + } + + return count; +} + +/* + * pl2303_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, + unsigned int count) +{ + unsigned int len; + + if (pb == NULL) + return 0; + + len = pl2303_buf_data_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_get; + if (count > len) { + memcpy(buf, pb->buf_get, len); + memcpy(buf+len, pb->buf_buf, count - len); + pb->buf_get = pb->buf_buf + count - len; + } else { + memcpy(buf, pb->buf_get, count); + if (count < len) + pb->buf_get += count; + else /* count == len */ + pb->buf_get = pb->buf_buf; + } + + return count; +} + +static int pl2303_startup(struct usb_serial *serial) { struct pl2303_private *priv; enum pl2303_type type = type_0; @@ -246,36 +348,17 @@ cleanup: return -ENOMEM; } -static int set_control_lines (struct usb_device *dev, u8 value) +static int set_control_lines(struct usb_device *dev, u8 value) { int retval; - retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0), - SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, - value, 0, NULL, 0, 100); + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, + value, 0, NULL, 0, 100); dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval); return retval; } -static int pl2303_write (struct usb_serial_port *port, const unsigned char *buf, int count) -{ - struct pl2303_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); - - if (!count) - return count; - - spin_lock_irqsave(&priv->lock, flags); - count = pl2303_buf_put(priv->buf, buf, count); - spin_unlock_irqrestore(&priv->lock, flags); - - pl2303_send(port); - - return count; -} - static void pl2303_send(struct usb_serial_port *port) { int count, result; @@ -292,7 +375,7 @@ static void pl2303_send(struct usb_serial_port *port) } count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, - port->bulk_out_size); + port->bulk_out_size); if (count == 0) { spin_unlock_irqrestore(&priv->lock, flags); @@ -303,13 +386,15 @@ static void pl2303_send(struct usb_serial_port *port) spin_unlock_irqrestore(&priv->lock, flags); - usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, + port->write_urb->transfer_buffer); port->write_urb->transfer_buffer_length = count; port->write_urb->dev = port->serial->dev; - result = usb_submit_urb (port->write_urb, GFP_ATOMIC); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); + dev_err(&port->dev, "%s - failed submitting write urb," + " error %d\n", __FUNCTION__, result); priv->write_urb_in_use = 0; // TODO: reschedule pl2303_send } @@ -317,6 +402,26 @@ static void pl2303_send(struct usb_serial_port *port) usb_serial_port_softint(port); } +static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf, + int count) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); + + if (!count) + return count; + + spin_lock_irqsave(&priv->lock, flags); + count = pl2303_buf_put(priv->buf, buf, count); + spin_unlock_irqrestore(&priv->lock, flags); + + pl2303_send(port); + + return count; +} + static int pl2303_write_room(struct usb_serial_port *port) { struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -349,7 +454,8 @@ static int pl2303_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) +static void pl2303_set_termios(struct usb_serial_port *port, + struct termios *old_termios) { struct usb_serial *serial = port->serial; struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -370,7 +476,8 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + port->tty->termios->c_cflag = B9600 | CS8 | CREAD | + HUPCL | CLOCAL; priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); @@ -379,24 +486,24 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol /* check that they really want us to change something */ if (old_termios) { if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("%s - nothing to change...", __FUNCTION__); - return; + (RELEVANT_IFLAG(port->tty->termios->c_iflag) == + RELEVANT_IFLAG(old_termios->c_iflag))) { + dbg("%s - nothing to change...", __FUNCTION__); + return; } } - buf = kzalloc (7, GFP_KERNEL); + buf = kzalloc(7, GFP_KERNEL); if (!buf) { dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__); return; } - - i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), - GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (cflag & CSIZE) { switch (cflag & CSIZE) { @@ -428,7 +535,8 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol case B230400: baud = 230400; break; case B460800: baud = 460800; break; default: - dev_err(&port->dev, "pl2303 driver does not support the baudrate requested (fix it)\n"); + dev_err(&port->dev, "pl2303 driver does not support" + " the baudrate requested (fix it)\n"); break; } dbg("%s - baud = %d", __FUNCTION__, baud); @@ -468,10 +576,10 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol dbg("%s - parity = none", __FUNCTION__); } - i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), - SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dbg ("0x21:0x20:0:0 %d", i); + i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + dbg("0x21:0x20:0:0 %d", i); /* change control lines if we are switching to or from B0 */ spin_lock_irqsave(&priv->lock, flags); @@ -487,13 +595,13 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol } else { spin_unlock_irqrestore(&priv->lock, flags); } - + buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0; - i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), - GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, + i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (cflag & CRTSCTS) { @@ -502,18 +610,82 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol index = 0x61; else index = 0x41; - i = usb_control_msg(serial->dev, + 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); + dbg("0x40:0x1:0x0:0x%x %d", index, i); + } + + kfree(buf); +} + +static void pl2303_close(struct usb_serial_port *port, struct file *filp) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int c_cflag; + int bps; + long timeout; + wait_queue_t wait; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* wait for data to drain from the buffer */ + spin_lock_irqsave(&priv->lock, flags); + timeout = PL2303_CLOSING_WAIT; + init_waitqueue_entry(&wait, current); + add_wait_queue(&port->tty->write_wait, &wait); + for (;;) { + 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 */ + break; + spin_unlock_irqrestore(&priv->lock, flags); + timeout = schedule_timeout(timeout); + spin_lock_irqsave(&priv->lock, flags); } + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->tty->write_wait, &wait); + /* clear out any remaining data in the buffer */ + pl2303_buf_clear(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + /* wait for characters to drain from the device */ + /* (this is long enough for the entire 256 byte */ + /* pl2303 hardware buffer to drain with no flow */ + /* control for data rates of 1200 bps or more, */ + /* for lower rates we should really know how much */ + /* data is in the buffer to compute a delay */ + /* that is not unnecessarily long) */ + bps = tty_get_baud_rate(port->tty); + if (bps > 1200) + timeout = max((HZ*2560)/bps,HZ/10); + else + timeout = 2*HZ; + schedule_timeout_interruptible(timeout); - kfree (buf); + /* 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); + + 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->line_control = 0; + spin_unlock_irqrestore(&priv->lock, flags); + set_control_lines(port->serial->dev, 0); + } + } } -static int pl2303_open (struct usb_serial_port *port, struct file *filp) +static int pl2303_open(struct usb_serial_port *port, struct file *filp) { struct termios tmp_termios; struct usb_serial *serial = port->serial; @@ -567,98 +739,35 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) /* Setup termios */ if (port->tty) { - pl2303_set_termios (port, &tmp_termios); + pl2303_set_termios(port, &tmp_termios); } //FIXME: need to assert RTS and DTR if CRTSCTS off dbg("%s - submitting read urb", __FUNCTION__); port->read_urb->dev = serial->dev; - result = usb_submit_urb (port->read_urb, GFP_KERNEL); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) { - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result); - pl2303_close (port, NULL); + dev_err(&port->dev, "%s - failed submitting read urb," + " error %d\n", __FUNCTION__, result); + pl2303_close(port, NULL); return -EPROTO; } dbg("%s - submitting interrupt urb", __FUNCTION__); port->interrupt_in_urb->dev = serial->dev; - result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result) { - dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, result); - pl2303_close (port, NULL); + dev_err(&port->dev, "%s - failed submitting interrupt urb," + " error %d\n", __FUNCTION__, result); + pl2303_close(port, NULL); return -EPROTO; } return 0; } - -static void pl2303_close (struct usb_serial_port *port, struct file *filp) -{ - struct pl2303_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - unsigned int c_cflag; - int bps; - long timeout; - wait_queue_t wait; - - dbg("%s - port %d", __FUNCTION__, port->number); - - /* wait for data to drain from the buffer */ - spin_lock_irqsave(&priv->lock, flags); - timeout = PL2303_CLOSING_WAIT; - init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); - for (;;) { - 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 */ - break; - spin_unlock_irqrestore(&priv->lock, flags); - timeout = schedule_timeout(timeout); - spin_lock_irqsave(&priv->lock, flags); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); - /* clear out any remaining data in the buffer */ - pl2303_buf_clear(priv->buf); - spin_unlock_irqrestore(&priv->lock, flags); - - /* wait for characters to drain from the device */ - /* (this is long enough for the entire 256 byte */ - /* pl2303 hardware buffer to drain with no flow */ - /* control for data rates of 1200 bps or more, */ - /* for lower rates we should really know how much */ - /* data is in the buffer to compute a delay */ - /* that is not unnecessarily long) */ - bps = tty_get_baud_rate(port->tty); - if (bps > 1200) - timeout = max((HZ*2560)/bps,HZ/10); - else - timeout = 2*HZ; - schedule_timeout_interruptible(timeout); - - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __FUNCTION__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); - usb_kill_urb(port->interrupt_in_urb); - - 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->line_control = 0; - spin_unlock_irqrestore (&priv->lock, flags); - set_control_lines (port->serial->dev, 0); - } - } -} - -static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, - unsigned int set, unsigned int clear) +static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -667,7 +776,7 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, if (!usb_get_intfdata(port->serial->interface)) return -ENODEV; - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); if (set & TIOCM_RTS) priv->line_control |= CONTROL_RTS; if (set & TIOCM_DTR) @@ -677,12 +786,12 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, if (clear & TIOCM_DTR) priv->line_control &= ~CONTROL_DTR; control = priv->line_control; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); - return set_control_lines (port->serial->dev, control); + return set_control_lines(port->serial->dev, control); } -static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file) +static int pl2303_tiocmget(struct usb_serial_port *port, struct file *file) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -695,10 +804,10 @@ static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file) if (!usb_get_intfdata(port->serial->interface)) return -ENODEV; - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); mcr = priv->line_control; status = priv->line_status; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0) | ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0) @@ -720,22 +829,22 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) unsigned int status; unsigned int changed; - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); prevstatus = priv->line_status; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); while (1) { interruptible_sleep_on(&priv->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; - - spin_lock_irqsave (&priv->lock, flags); + + spin_lock_irqsave(&priv->lock, flags); status = priv->line_status; - spin_unlock_irqrestore (&priv->lock, flags); - + spin_unlock_irqrestore(&priv->lock, flags); + changed=prevstatus^status; - + if (((arg & TIOCM_RNG) && (changed & UART_RING)) || ((arg & TIOCM_DSR) && (changed & UART_DSR)) || ((arg & TIOCM_CD) && (changed & UART_DCD)) || @@ -748,7 +857,8 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) +static int pl2303_ioctl(struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg) { dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd); @@ -765,7 +875,7 @@ static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsign return -ENOIOCTLCMD; } -static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) +static void pl2303_break_ctl(struct usb_serial_port *port, int break_state) { struct usb_serial *serial = port->serial; u16 state; @@ -779,15 +889,14 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) state = BREAK_ON; dbg("%s - turning break %s", __FUNCTION__, state==BREAK_OFF ? "off" : "on"); - result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), - BREAK_REQUEST, BREAK_REQUEST_TYPE, state, - 0, NULL, 0, 100); + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + BREAK_REQUEST, BREAK_REQUEST_TYPE, state, + 0, NULL, 0, 100); if (result) dbg("%s - error sending break = %d", __FUNCTION__, result); } - -static void pl2303_shutdown (struct usb_serial *serial) +static void pl2303_shutdown(struct usb_serial *serial) { int i; struct pl2303_private *priv; @@ -801,7 +910,7 @@ static void pl2303_shutdown (struct usb_serial *serial) kfree(priv); usb_set_serial_port_data(serial->port[i], NULL); } - } + } } static void pl2303_update_line_status(struct usb_serial_port *port, @@ -813,29 +922,33 @@ static void pl2303_update_line_status(struct usb_serial_port *port, unsigned long flags; u8 status_idx = UART_STATE; u8 length = UART_STATE + 1; + u16 idv, idp; + + idv = le16_to_cpu(port->serial->dev->descriptor.idVendor); + idp = le16_to_cpu(port->serial->dev->descriptor.idProduct); - if ((le16_to_cpu(port->serial->dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) && - (le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X65 || - le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_SX1 || - le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X75)) { - length = 1; - status_idx = 0; + + if (idv == SIEMENS_VENDOR_ID) { + if (idp == SIEMENS_PRODUCT_ID_X65 || + idp == SIEMENS_PRODUCT_ID_SX1 || + idp == SIEMENS_PRODUCT_ID_X75) { + + length = 1; + status_idx = 0; + } } if (actual_length < length) - goto exit; + return; /* Save off the uart status for others to look at */ spin_lock_irqsave(&priv->lock, flags); priv->line_status = data[status_idx]; spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible (&priv->delta_msr_wait); - -exit: - return; + wake_up_interruptible(&priv->delta_msr_wait); } -static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs) +static void pl2303_read_int_callback(struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; unsigned char *data = urb->transfer_buffer; @@ -852,25 +965,29 @@ static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", __FUNCTION__, + urb->status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", __FUNCTION__, + urb->status); goto exit; } - usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, + urb->actual_length, urb->transfer_buffer); + pl2303_update_line_status(port, data, actual_length); exit: - status = usb_submit_urb (urb, GFP_ATOMIC); + status = usb_submit_urb(urb, GFP_ATOMIC); if (status) - dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n", + dev_err(&urb->dev->dev, + "%s - usb_submit_urb failed with result %d\n", __FUNCTION__, status); } - -static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) +static void pl2303_read_bulk_callback(struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -891,20 +1008,25 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) return; } if (urb->status == -EPROTO) { - /* PL2303 mysteriously fails with -EPROTO reschedule the read */ - dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__); + /* PL2303 mysteriously fails with -EPROTO reschedule + * the read */ + dbg("%s - caught -EPROTO, resubmitting the urb", + __FUNCTION__); urb->status = 0; urb->dev = port->serial->dev; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) - dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); + dev_err(&urb->dev->dev, "%s - failed" + " resubmitting read urb, error %d\n", + __FUNCTION__, result); return; } dbg("%s - unable to handle the error, exiting.", __FUNCTION__); return; } - usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, + urb->actual_length, data); /* get tty_flag from status */ tty_flag = TTY_NORMAL; @@ -913,7 +1035,7 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) status = priv->line_status; priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible (&priv->delta_msr_wait); + wake_up_interruptible(&priv->delta_msr_wait); /* break takes precedence over parity, */ /* which takes precedence over framing errors */ @@ -932,8 +1054,8 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) if (status & UART_OVERRUN_ERROR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); for (i = 0; i < urb->actual_length; ++i) - tty_insert_flip_char (tty, data[i], tty_flag); - tty_flip_buffer_push (tty); + tty_insert_flip_char(tty, data[i], tty_flag); + tty_flip_buffer_push(tty); } /* Schedule the next read _if_ we are still open */ @@ -941,15 +1063,14 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) urb->dev = port->serial->dev; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) - dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); + dev_err(&urb->dev->dev, "%s - failed resubmitting" + " read urb, error %d\n", __FUNCTION__, result); } return; } - - -static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) +static void pl2303_write_bulk_callback(struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -965,18 +1086,21 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", __FUNCTION__, + urb->status); priv->write_urb_in_use = 0; return; default: /* error in the urb, so we have to resubmit it */ dbg("%s - Overflow in write", __FUNCTION__); - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, + urb->status); port->write_urb->transfer_buffer_length = 1; port->write_urb->dev = port->serial->dev; - result = usb_submit_urb (port->write_urb, GFP_ATOMIC); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) - dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result); + dev_err(&urb->dev->dev, "%s - failed resubmitting write" + " urb, error %d\n", __FUNCTION__, result); else return; } @@ -987,191 +1111,38 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) pl2303_send(port); } +/* All of the device info needed for the PL2303 SIO serial converter */ +static struct usb_serial_driver pl2303_device = { + .driver = { + .owner = THIS_MODULE, + .name = "pl2303", + }, + .id_table = id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = pl2303_open, + .close = pl2303_close, + .write = pl2303_write, + .ioctl = pl2303_ioctl, + .break_ctl = pl2303_break_ctl, + .set_termios = pl2303_set_termios, + .tiocmget = pl2303_tiocmget, + .tiocmset = pl2303_tiocmset, + .read_bulk_callback = pl2303_read_bulk_callback, + .read_int_callback = pl2303_read_int_callback, + .write_bulk_callback = pl2303_write_bulk_callback, + .write_room = pl2303_write_room, + .chars_in_buffer = pl2303_chars_in_buffer, + .attach = pl2303_startup, + .shutdown = pl2303_shutdown, +}; -/* - * pl2303_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ - -static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) -{ - - struct pl2303_buf *pb; - - - if (size == 0) - return NULL; - - pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); - if (pb == NULL) - return NULL; - - pb->buf_buf = kmalloc(size, GFP_KERNEL); - if (pb->buf_buf == NULL) { - kfree(pb); - return NULL; - } - - pb->buf_size = size; - pb->buf_get = pb->buf_put = pb->buf_buf; - - return pb; - -} - - -/* - * pl2303_buf_free - * - * Free the buffer and all associated memory. - */ - -static void pl2303_buf_free(struct pl2303_buf *pb) -{ - if (pb) { - kfree(pb->buf_buf); - kfree(pb); - } -} - - -/* - * pl2303_buf_clear - * - * Clear out all data in the circular buffer. - */ - -static void pl2303_buf_clear(struct pl2303_buf *pb) -{ - if (pb != NULL) - pb->buf_get = pb->buf_put; - /* equivalent to a get of all data available */ -} - - -/* - * pl2303_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) -{ - if (pb != NULL) - return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); - else - return 0; -} - - -/* - * pl2303_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) -{ - if (pb != NULL) - return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); - else - return 0; -} - - -/* - * pl2303_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, - unsigned int count) -{ - - unsigned int len; - - - if (pb == NULL) - return 0; - - len = pl2303_buf_space_avail(pb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = pb->buf_buf + pb->buf_size - pb->buf_put; - if (count > len) { - memcpy(pb->buf_put, buf, len); - memcpy(pb->buf_buf, buf+len, count - len); - pb->buf_put = pb->buf_buf + count - len; - } else { - memcpy(pb->buf_put, buf, count); - if (count < len) - pb->buf_put += count; - else /* count == len */ - pb->buf_put = pb->buf_buf; - } - - return count; - -} - - -/* - * pl2303_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, - unsigned int count) -{ - - unsigned int len; - - - if (pb == NULL) - return 0; - - len = pl2303_buf_data_avail(pb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = pb->buf_buf + pb->buf_size - pb->buf_get; - if (count > len) { - memcpy(buf, pb->buf_get, len); - memcpy(buf+len, pb->buf_buf, count - len); - pb->buf_get = pb->buf_buf + count - len; - } else { - memcpy(buf, pb->buf_get, count); - if (count < len) - pb->buf_get += count; - else /* count == len */ - pb->buf_get = pb->buf_buf; - } - - return count; - -} - -static int __init pl2303_init (void) +static int __init pl2303_init(void) { int retval; + retval = usb_serial_register(&pl2303_device); if (retval) goto failed_usb_serial_register; @@ -1186,14 +1157,12 @@ failed_usb_serial_register: return retval; } - -static void __exit pl2303_exit (void) +static void __exit pl2303_exit(void) { - usb_deregister (&pl2303_driver); - usb_serial_deregister (&pl2303_device); + usb_deregister(&pl2303_driver); + usb_serial_deregister(&pl2303_device); } - module_init(pl2303_init); module_exit(pl2303_exit); diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 7f29e81d3e35..65a5039665e7 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -10,6 +10,7 @@ #define PL2303_VENDOR_ID 0x067b #define PL2303_PRODUCT_ID 0x2303 #define PL2303_PRODUCT_ID_RSAQ2 0x04bb +#define PL2303_PRODUCT_ID_DCU11 0x1234 #define PL2303_PRODUCT_ID_PHAROS 0xaaa0 #define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 @@ -81,6 +82,18 @@ #define SPEEDDRAGON_VENDOR_ID 0x0e55 #define SPEEDDRAGON_PRODUCT_ID 0x110b -/* Ours Technology Inc DKU-5 clone, chipset: Prolific Technology Inc */ -#define OTI_VENDOR_ID 0x0ea0 -#define OTI_PRODUCT_ID 0x6858 +/* DATAPILOT Universal-2 Phone Cable */ +#define DATAPILOT_U2_VENDOR_ID 0x0731 +#define DATAPILOT_U2_PRODUCT_ID 0x2003 + +/* Belkin "F5U257" Serial Adapter */ +#define BELKIN_VENDOR_ID 0x050d +#define BELKIN_PRODUCT_ID 0x0257 + +/* Alcor Micro Corp. USB 2.0 TO RS-232 */ +#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 diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index f0215f850d2d..1e07dfad6853 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -60,7 +60,6 @@ */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -72,7 +71,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #ifndef CONFIG_USB_SAFE_PADDED @@ -299,14 +298,14 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i dbg ("%s - write request of 0 bytes", __FUNCTION__); return (0); } - spin_lock(&port->lock); + spin_lock_bh(&port->lock); if (port->write_urb_busy) { - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); dbg("%s - already writing", __FUNCTION__); return 0; } port->write_urb_busy = 1; - spin_unlock(&port->lock); + spin_unlock_bh(&port->lock); packet_length = port->bulk_out_size; // get max packetsize diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c new file mode 100644 index 000000000000..d29638daa987 --- /dev/null +++ b/drivers/usb/serial/sierra.c @@ -0,0 +1,75 @@ +/* + * Sierra Wireless CDMA Wireless Serial USB driver + * + * Current Copy modified by: Kevin Lloyd <linux@sierrawireless.com> + * Original Copyright (C) 2005-2006 Greg Kroah-Hartman <gregkh@suse.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> + +static struct usb_device_id id_table [] = { + { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ + { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ + { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ + { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ + { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 */ + { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ + /* Following devices are supported in the airprime.c driver */ + /* { USB_DEVICE(0x1199, 0x0112) }, */ /* Sierra Wireless AirCard 580 */ + /* { USB_DEVICE(0x0F3D, 0x0112) }, */ /* AirPrime/Sierra PC 5220 */ + { } +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_driver sierra_driver = { + .name = "sierra_wireless", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, +}; + +static struct usb_serial_driver sierra_device = { + .driver = { + .owner = THIS_MODULE, + .name = "Sierra_Wireless", + }, + .id_table = id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 3, +}; + +static int __init sierra_init(void) +{ + int retval; + + retval = usb_serial_register(&sierra_device); + if (retval) + return retval; + retval = usb_register(&sierra_driver); + if (retval) + usb_serial_deregister(&sierra_device); + return retval; +} + +static void __exit sierra_exit(void) +{ + usb_deregister(&sierra_driver); + usb_serial_deregister(&sierra_device); +} + +module_init(sierra_init); +module_exit(sierra_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index c3a2071b802d..ac9b8ee52d44 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -68,7 +68,6 @@ * fi */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -84,8 +83,8 @@ #include <asm/uaccess.h> #include <asm/semaphore.h> #include <linux/usb.h> +#include <linux/usb/serial.h> -#include "usb-serial.h" #include "ti_usb_3410_5052.h" #include "ti_fw_3410.h" /* firmware image for 3410 */ #include "ti_fw_5052.h" /* firmware image for 5052 */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a30135c7cfe6..0222d92842b8 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -16,7 +16,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -32,7 +31,7 @@ #include <linux/smp_lock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "pl2303.h" /* @@ -41,6 +40,8 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/" #define DRIVER_DESC "USB Serial Driver core" +static void port_free(struct usb_serial_port *port); + /* Driver structure we register with the USB core */ static struct usb_driver usb_serial_driver = { .name = "usbserial", @@ -147,23 +148,10 @@ static void destroy_serial(struct kref *kref) port = serial->port[i]; if (!port) continue; - usb_kill_urb(port->read_urb); - usb_free_urb(port->read_urb); - usb_kill_urb(port->write_urb); - usb_free_urb(port->write_urb); - usb_kill_urb(port->interrupt_in_urb); - usb_free_urb(port->interrupt_in_urb); - usb_kill_urb(port->interrupt_out_urb); - usb_free_urb(port->interrupt_out_urb); - kfree(port->bulk_in_buffer); - kfree(port->bulk_out_buffer); - kfree(port->interrupt_in_buffer); - kfree(port->interrupt_out_buffer); + port_free(port); } } - flush_scheduled_work(); /* port->work */ - usb_put_dev(serial->dev); /* free up any memory that we allocated */ @@ -476,8 +464,10 @@ static int serial_read_proc (char *page, char **start, off_t off, int count, int length += sprintf (page+length, " path:%s", tmp); length += sprintf (page+length, "\n"); - if ((length + begin) > (off + count)) + if ((length + begin) > (off + count)) { + usb_serial_put(serial); goto done; + } if ((length + begin) < off) { begin += length; length = 0; @@ -565,6 +555,11 @@ static void port_release(struct device *dev) struct usb_serial_port *port = to_usb_serial_port(dev); dbg ("%s - %s", __FUNCTION__, dev->bus_id); + port_free(port); +} + +static void port_free(struct usb_serial_port *port) +{ usb_kill_urb(port->read_urb); usb_free_urb(port->read_urb); usb_kill_urb(port->write_urb); @@ -577,6 +572,7 @@ static void port_release(struct device *dev) kfree(port->bulk_out_buffer); kfree(port->interrupt_in_buffer); kfree(port->interrupt_out_buffer); + flush_scheduled_work(); /* port->work */ kfree(port); } @@ -680,33 +676,29 @@ int usb_serial_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - - if ((endpoint->bEndpointAddress & 0x80) && - ((endpoint->bmAttributes & 3) == 0x02)) { + + if (usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ dbg("found bulk in on endpoint %d", i); bulk_in_endpoint[num_bulk_in] = endpoint; ++num_bulk_in; } - if (((endpoint->bEndpointAddress & 0x80) == 0x00) && - ((endpoint->bmAttributes & 3) == 0x02)) { + if (usb_endpoint_is_bulk_out(endpoint)) { /* we found a bulk out endpoint */ dbg("found bulk out on endpoint %d", i); bulk_out_endpoint[num_bulk_out] = endpoint; ++num_bulk_out; } - - if ((endpoint->bEndpointAddress & 0x80) && - ((endpoint->bmAttributes & 3) == 0x03)) { + + if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dbg("found interrupt in on endpoint %d", i); interrupt_in_endpoint[num_interrupt_in] = endpoint; ++num_interrupt_in; } - if (((endpoint->bEndpointAddress & 0x80) == 0x00) && - ((endpoint->bmAttributes & 3) == 0x03)) { + if (usb_endpoint_is_int_out(endpoint)) { /* we found an interrupt out endpoint */ dbg("found interrupt out on endpoint %d", i); interrupt_out_endpoint[num_interrupt_out] = endpoint; @@ -720,14 +712,15 @@ int usb_serial_probe(struct usb_interface *interface, if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) && (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) || ((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) && - (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID))) { + (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) || + ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) && + (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID))) { if (interface != dev->actconfig->interface[0]) { /* check out the endpoints of the other interface*/ iface_desc = dev->actconfig->interface[0]->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if ((endpoint->bEndpointAddress & 0x80) && - ((endpoint->bmAttributes & 3) == 0x03)) { + if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dbg("found interrupt in for Prolific device on separate interface"); interrupt_in_endpoint[num_interrupt_in] = endpoint; @@ -941,7 +934,10 @@ int usb_serial_probe(struct usb_interface *interface, snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number); dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id); - device_register (&port->dev); + retval = device_register(&port->dev); + if (retval) + dev_err(&port->dev, "Error registering port device, " + "continuing\n"); } usb_serial_console_init (debug, minor); @@ -1059,13 +1055,12 @@ static int __init usb_serial_init(void) usb_serial_tty_driver->owner = THIS_MODULE; usb_serial_tty_driver->driver_name = "usbserial"; - usb_serial_tty_driver->devfs_name = "usb/tts/"; usb_serial_tty_driver->name = "ttyUSB"; usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; usb_serial_tty_driver->minor_start = 0; usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; - usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; usb_serial_tty_driver->init_termios = tty_std_termios; usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(usb_serial_tty_driver, &serial_ops); diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h deleted file mode 100644 index d53ea9b11e81..000000000000 --- a/drivers/usb/serial/usb-serial.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * USB Serial Converter driver - * - * Copyright (C) 1999 - 2005 - * Greg Kroah-Hartman (greg@kroah.com) - * - * 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. - * - */ - - -#ifndef __LINUX_USB_SERIAL_H -#define __LINUX_USB_SERIAL_H - -#include <linux/config.h> -#include <linux/kref.h> -#include <linux/mutex.h> - -#define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ -#define SERIAL_TTY_MINORS 255 /* loads of devices :) */ - -#define MAX_NUM_PORTS 8 /* The maximum number of ports one device can grab at once */ - -/* parity check flag */ -#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - -/** - * usb_serial_port: structure for the specific ports of a device. - * @serial: pointer back to the struct usb_serial owner of this port. - * @tty: pointer to the corresponding tty for this port. - * @lock: spinlock to grab when updating portions of this structure. - * @mutex: mutex used to synchronize serial_open() and serial_close() - * access for this port. - * @number: the number of the port (the minor number). - * @interrupt_in_buffer: pointer to the interrupt in buffer for this port. - * @interrupt_in_urb: pointer to the interrupt in struct urb for this port. - * @interrupt_in_endpointAddress: endpoint address for the interrupt in pipe - * for this port. - * @interrupt_out_buffer: pointer to the interrupt out buffer for this port. - * @interrupt_out_size: the size of the interrupt_out_buffer, in bytes. - * @interrupt_out_urb: pointer to the interrupt out struct urb for this port. - * @interrupt_out_endpointAddress: endpoint address for the interrupt out pipe - * for this port. - * @bulk_in_buffer: pointer to the bulk in buffer for this port. - * @read_urb: pointer to the bulk in struct urb for this port. - * @bulk_in_endpointAddress: endpoint address for the bulk in pipe for this - * port. - * @bulk_out_buffer: pointer to the bulk out buffer for this port. - * @bulk_out_size: the size of the bulk_out_buffer, in bytes. - * @write_urb: pointer to the bulk out struct urb for this port. - * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this - * port. - * @write_wait: a wait_queue_head_t used by the port. - * @work: work queue entry for the line discipline waking up. - * @open_count: number of times this port has been opened. - * - * This structure is used by the usb-serial core and drivers for the specific - * ports of a device. - */ -struct usb_serial_port { - struct usb_serial * serial; - struct tty_struct * tty; - spinlock_t lock; - struct mutex mutex; - unsigned char number; - - unsigned char * interrupt_in_buffer; - struct urb * interrupt_in_urb; - __u8 interrupt_in_endpointAddress; - - unsigned char * interrupt_out_buffer; - int interrupt_out_size; - struct urb * interrupt_out_urb; - __u8 interrupt_out_endpointAddress; - - unsigned char * bulk_in_buffer; - int bulk_in_size; - struct urb * read_urb; - __u8 bulk_in_endpointAddress; - - unsigned char * bulk_out_buffer; - int bulk_out_size; - struct urb * write_urb; - int write_urb_busy; - __u8 bulk_out_endpointAddress; - - wait_queue_head_t write_wait; - struct work_struct work; - int open_count; - struct device dev; -}; -#define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev) - -/* get and set the port private data pointer helper functions */ -static inline void *usb_get_serial_port_data (struct usb_serial_port *port) -{ - return dev_get_drvdata(&port->dev); -} - -static inline void usb_set_serial_port_data (struct usb_serial_port *port, void *data) -{ - dev_set_drvdata(&port->dev, data); -} - -/** - * usb_serial - structure used by the usb-serial core for a device - * @dev: pointer to the struct usb_device for this device - * @type: pointer to the struct usb_serial_driver for this device - * @interface: pointer to the struct usb_interface for this device - * @minor: the starting minor number for this device - * @num_ports: the number of ports this device has - * @num_interrupt_in: number of interrupt in endpoints we have - * @num_interrupt_out: number of interrupt out endpoints we have - * @num_bulk_in: number of bulk in endpoints we have - * @num_bulk_out: number of bulk out endpoints we have - * @port: array of struct usb_serial_port structures for the different ports. - * @private: place to put any driver specific information that is needed. The - * usb-serial driver is required to manage this data, the usb-serial core - * will not touch this. Use usb_get_serial_data() and - * usb_set_serial_data() to access this. - */ -struct usb_serial { - struct usb_device * dev; - struct usb_serial_driver * type; - struct usb_interface * interface; - unsigned char minor; - unsigned char num_ports; - unsigned char num_port_pointers; - char num_interrupt_in; - char num_interrupt_out; - char num_bulk_in; - char num_bulk_out; - struct usb_serial_port * port[MAX_NUM_PORTS]; - struct kref kref; - void * private; -}; -#define to_usb_serial(d) container_of(d, struct usb_serial, kref) - -#define NUM_DONT_CARE (-1) - -/* get and set the serial private data pointer helper functions */ -static inline void *usb_get_serial_data (struct usb_serial *serial) -{ - return serial->private; -} - -static inline void usb_set_serial_data (struct usb_serial *serial, void *data) -{ - serial->private = data; -} - -/** - * usb_serial_driver - describes a usb serial driver - * @description: pointer to a string that describes this driver. This string used - * in the syslog messages when a device is inserted or removed. - * @id_table: pointer to a list of usb_device_id structures that define all - * of the devices this structure can support. - * @num_interrupt_in: the number of interrupt in endpoints this device will - * have. - * @num_interrupt_out: the number of interrupt out endpoints this device will - * have. - * @num_bulk_in: the number of bulk in endpoints this device will have. - * @num_bulk_out: the number of bulk out endpoints this device will have. - * @num_ports: the number of different ports this device will have. - * @calc_num_ports: pointer to a function to determine how many ports this - * device has dynamically. It will be called after the probe() - * callback is called, but before attach() - * @probe: pointer to the driver's probe function. - * This will be called when the device is inserted into the system, - * but before the device has been fully initialized by the usb_serial - * subsystem. Use this function to download any firmware to the device, - * or any other early initialization that might be needed. - * Return 0 to continue on with the initialization sequence. Anything - * else will abort it. - * @attach: pointer to the driver's attach function. - * This will be called when the struct usb_serial structure is fully set - * set up. Do any local initialization of the device, or any private - * memory structure allocation at this point in time. - * @shutdown: pointer to the driver's shutdown function. This will be - * called when the device is removed from the system. - * - * This structure is defines a USB Serial driver. It provides all of - * the information that the USB serial core code needs. If the function - * pointers are defined, then the USB serial core code will call them when - * the corresponding tty port functions are called. If they are not - * called, the generic serial function will be used instead. - * - * The driver.owner field should be set to the module owner of this driver. - * The driver.name field should be set to the name of this driver (remember - * it will show up in sysfs, so it needs to be short and to the point. - * Useing the module name is a good idea.) - */ -struct usb_serial_driver { - const char *description; - const struct usb_device_id *id_table; - char num_interrupt_in; - char num_interrupt_out; - char num_bulk_in; - char num_bulk_out; - char num_ports; - - struct list_head driver_list; - struct device_driver driver; - - int (*probe) (struct usb_serial *serial, const struct usb_device_id *id); - int (*attach) (struct usb_serial *serial); - int (*calc_num_ports) (struct usb_serial *serial); - - void (*shutdown) (struct usb_serial *serial); - - int (*port_probe) (struct usb_serial_port *port); - int (*port_remove) (struct usb_serial_port *port); - - /* serial function calls */ - int (*open) (struct usb_serial_port *port, struct file * filp); - void (*close) (struct usb_serial_port *port, struct file * filp); - int (*write) (struct usb_serial_port *port, const unsigned char *buf, int count); - int (*write_room) (struct usb_serial_port *port); - int (*ioctl) (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); - void (*set_termios) (struct usb_serial_port *port, struct termios * old); - void (*break_ctl) (struct usb_serial_port *port, int break_state); - int (*chars_in_buffer) (struct usb_serial_port *port); - void (*throttle) (struct usb_serial_port *port); - void (*unthrottle) (struct usb_serial_port *port); - int (*tiocmget) (struct usb_serial_port *port, struct file *file); - int (*tiocmset) (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); - - void (*read_int_callback)(struct urb *urb, struct pt_regs *regs); - void (*write_int_callback)(struct urb *urb, struct pt_regs *regs); - void (*read_bulk_callback)(struct urb *urb, struct pt_regs *regs); - void (*write_bulk_callback)(struct urb *urb, struct pt_regs *regs); -}; -#define to_usb_serial_driver(d) container_of(d, struct usb_serial_driver, driver) - -extern int usb_serial_register(struct usb_serial_driver *driver); -extern void usb_serial_deregister(struct usb_serial_driver *driver); -extern void usb_serial_port_softint(struct usb_serial_port *port); - -extern int usb_serial_probe(struct usb_interface *iface, const struct usb_device_id *id); -extern void usb_serial_disconnect(struct usb_interface *iface); - -extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest); -extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit); - -/* USB Serial console functions */ -#ifdef CONFIG_USB_SERIAL_CONSOLE -extern void usb_serial_console_init (int debug, int minor); -extern void usb_serial_console_exit (void); -extern void usb_serial_console_disconnect(struct usb_serial *serial); -#else -static inline void usb_serial_console_init (int debug, int minor) { } -static inline void usb_serial_console_exit (void) { } -static inline void usb_serial_console_disconnect(struct usb_serial *serial) {} -#endif - -/* Functions needed by other parts of the usbserial core */ -extern struct usb_serial *usb_serial_get_by_index (unsigned int minor); -extern void usb_serial_put(struct usb_serial *serial); -extern int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp); -extern int usb_serial_generic_write (struct usb_serial_port *port, const unsigned char *buf, int count); -extern void usb_serial_generic_close (struct usb_serial_port *port, struct file *filp); -extern int usb_serial_generic_write_room (struct usb_serial_port *port); -extern int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port); -extern void usb_serial_generic_read_bulk_callback (struct urb *urb, struct pt_regs *regs); -extern void usb_serial_generic_write_bulk_callback (struct urb *urb, struct pt_regs *regs); -extern void usb_serial_generic_shutdown (struct usb_serial *serial); -extern int usb_serial_generic_register (int debug); -extern void usb_serial_generic_deregister (void); - -extern int usb_serial_bus_register (struct usb_serial_driver *device); -extern void usb_serial_bus_deregister (struct usb_serial_driver *device); - -extern struct usb_serial_driver usb_serial_generic_device; -extern struct bus_type usb_serial_bus_type; -extern struct tty_driver *usb_serial_tty_driver; - -static inline void usb_serial_debug_data(int debug, - struct device *dev, - const char *function, int size, - const unsigned char *data) -{ - int i; - - if (debug) { - dev_printk(KERN_DEBUG, dev, "%s - length = %d, data = ", function, size); - for (i = 0; i < size; ++i) - printk ("%.2x ", data[i]); - printk ("\n"); - } -} - -/* Use our own dbg macro */ -#undef dbg -#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG "%s: " format "\n" , __FILE__ , ## arg); } while (0) - - - -#endif /* ifdef __LINUX_USB_SERIAL_H */ - diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 9e89b8d54f72..88949f7884ca 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -13,7 +13,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -26,7 +25,7 @@ #include <linux/spinlock.h> #include <asm/uaccess.h> #include <linux/usb.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "visor.h" /* @@ -303,7 +302,6 @@ static int visor_open (struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); priv->bytes_in = 0; priv->bytes_out = 0; - priv->outstanding_urbs = 0; priv->throttled = 0; spin_unlock_irqrestore(&priv->lock, flags); @@ -436,13 +434,25 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf, static int visor_write_room (struct usb_serial_port *port) { + struct visor_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + dbg("%s - port %d", __FUNCTION__, port->number); /* * We really can take anything the user throws at us * but let's pick a nice big number to tell the tty - * layer that we have lots of free space + * layer that we have lots of free space, unless we don't. */ + + spin_lock_irqsave(&priv->lock, flags); + if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) { + spin_unlock_irqrestore(&priv->lock, flags); + dbg("%s - write limit hit\n", __FUNCTION__); + return 0; + } + spin_unlock_irqrestore(&priv->lock, flags); + return 2048; } @@ -759,15 +769,22 @@ static int visor_calc_num_ports (struct usb_serial *serial) static int generic_startup(struct usb_serial *serial) { + struct usb_serial_port **ports = serial->port; struct visor_private *priv; int i; for (i = 0; i < serial->num_ports; ++i) { priv = kzalloc (sizeof(*priv), GFP_KERNEL); - if (!priv) + if (!priv) { + while (i-- != 0) { + priv = usb_get_serial_port_data(ports[i]); + usb_set_serial_port_data(ports[i], NULL); + kfree(priv); + } return -ENOMEM; + } spin_lock_init(&priv->lock); - usb_set_serial_port_data(serial->port[i], priv); + usb_set_serial_port_data(ports[i], priv); } return 0; } @@ -877,7 +894,18 @@ static int clie_5_attach (struct usb_serial *serial) static void visor_shutdown (struct usb_serial *serial) { + struct visor_private *priv; + int i; + dbg("%s", __FUNCTION__); + + for (i = 0; i < serial->num_ports; i++) { + priv = usb_get_serial_port_data(serial->port[i]); + if (priv) { + usb_set_serial_port_data(serial->port[i], NULL); + kfree(priv); + } + } } static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 5b06fa366098..6e6c7934be32 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -65,7 +65,6 @@ * */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -80,7 +79,7 @@ #include <linux/usb.h> #include <linux/serial_reg.h> #include <linux/serial.h> -#include "usb-serial.h" +#include <linux/usb/serial.h> #include "whiteheat_fw.h" /* firmware for the ConnectTech WhiteHEAT device */ #include "whiteheat.h" /* WhiteHEAT specific commands */ @@ -686,19 +685,16 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; usb_kill_urb(urb); - list_del(tmp); - list_add(tmp, &info->rx_urbs_free); - } - list_for_each_safe(tmp, tmp2, &info->rx_urb_q) { - list_del(tmp); - list_add(tmp, &info->rx_urbs_free); + list_move(tmp, &info->rx_urbs_free); } + list_for_each_safe(tmp, tmp2, &info->rx_urb_q) + list_move(tmp, &info->rx_urbs_free); + list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; usb_kill_urb(urb); - list_del(tmp); - list_add(tmp, &info->tx_urbs_free); + list_move(tmp, &info->tx_urbs_free); } spin_unlock_irqrestore(&info->lock, flags); @@ -1080,8 +1076,7 @@ static void whiteheat_write_callback(struct urb *urb, struct pt_regs *regs) err("%s - Not my urb!", __FUNCTION__); return; } - list_del(&wrap->list); - list_add(&wrap->list, &info->tx_urbs_free); + list_move(&wrap->list, &info->tx_urbs_free); spin_unlock(&info->lock); if (urb->status) { @@ -1371,8 +1366,7 @@ static int start_port_read(struct usb_serial_port *port) wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; usb_kill_urb(urb); - list_del(tmp); - list_add(tmp, &info->rx_urbs_free); + list_move(tmp, &info->rx_urbs_free); } break; } |