diff options
Diffstat (limited to 'drivers/usb/serial/cypress_m8.c')
| -rw-r--r-- | drivers/usb/serial/cypress_m8.c | 1621 |
1 files changed, 582 insertions, 1039 deletions
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 779d07851a4..01bf5339281 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -2,7 +2,7 @@ * USB Cypress M8 driver * * Copyright (C) 2004 - * Lonnie Mendez (dignome@gmail.com) + * Lonnie Mendez (dignome@gmail.com) * Copyright (C) 2003,2004 * Neil Whelchel (koyama@firstlight.net) * @@ -11,46 +11,22 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * * See http://geocities.com/i0xox0i for information on this driver and the * earthmate usb device. - * - * Lonnie Mendez <dignome@gmail.com> - * 4-29-2005 - * Fixed problem where setting or retreiving the serial config would fail with - * EPIPE. Removed CRTS toggling so the driver behaves more like other usbserial - * adapters. Issued new interval of 1ms instead of the default 10ms. As a - * result, transfer speed has been substantially increased. From avg. 850bps to - * avg. 3300bps. initial termios has also been modified. Cleaned up code and - * formatting issues so it is more readable. Replaced the C++ style comments. - * - * Lonnie Mendez <dignome@gmail.com> - * 12-15-2004 - * Incorporated write buffering from pl2303 driver. Fixed bug with line - * handling so both lines are raised in cypress_open. (was dropping rts) - * Various code cleanups made as well along with other misc bug fixes. - * - * Lonnie Mendez <dignome@gmail.com> - * 04-10-2004 - * Driver modified to support dynamic line settings. Various improvments - * and features. - * - * Neil Whelchel - * 10-2003 - * Driver first released. - * */ -/* Thanks to Neil Whelchel for writing the first cypress m8 implementation for linux. */ +/* Thanks to Neil Whelchel for writing the first cypress m8 implementation + for linux. */ /* Thanks to cypress for providing references for the hid reports. */ /* Thanks to Jiang Zhang for providing links and for general help. */ -/* Code originates and was built up from ftdi_sio, belkin, pl2303 and others. */ +/* Code originates and was built up from ftdi_sio, belkin, pl2303 and others.*/ #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> @@ -61,65 +37,57 @@ #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/serial.h> +#include <linux/kfifo.h> #include <linux/delay.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> +#include <asm/unaligned.h> #include "cypress_m8.h" -#ifdef CONFIG_USB_SERIAL_DEBUG - static int debug = 1; -#else - static int debug; -#endif -static int stats; +static bool stats; static int interval; +static bool unstable_bauds; -/* - * Version Information - */ -#define DRIVER_VERSION "v1.09" #define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>" #define DRIVER_DESC "Cypress USB to Serial Driver" /* write buffer size defines */ #define CYPRESS_BUF_SIZE 1024 -#define CYPRESS_CLOSING_WAIT (30*HZ) -static struct usb_device_id id_table_earthmate [] = { +static const struct usb_device_id id_table_earthmate[] = { { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) }, { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) }, { } /* Terminating entry */ }; -static struct usb_device_id id_table_cyphidcomrs232 [] = { +static const struct usb_device_id id_table_cyphidcomrs232[] = { { USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) }, { USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) }, + { USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) }, { } /* Terminating entry */ }; -static struct usb_device_id id_table_nokiaca42v2 [] = { +static const struct usb_device_id id_table_nokiaca42v2[] = { { USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) }, { } /* Terminating entry */ }; -static struct usb_device_id id_table_combined [] = { +static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) }, { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) }, { USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) }, { USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) }, + { USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) }, { USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); -static struct usb_driver cypress_driver = { - .name = "cypress", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table_combined, - .no_dynamic_id = 1, +enum packet_format { + packet_format_1, /* b0:status, b1:payload count */ + packet_format_2 /* b0[7:3]:status, b0[2:0]:payload count */ }; struct cypress_private { @@ -129,7 +97,7 @@ struct cypress_private { int bytes_out; /* used for statistics */ int cmd_count; /* used for statistics */ int cmd_ctrl; /* always set this to 1 before issuing a command */ - struct cypress_buf *buf; /* write buffer */ + struct kfifo write_fifo; /* write fifo */ 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 */ @@ -139,55 +107,40 @@ struct cypress_private { __u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */ __u8 current_config; /* stores the current configuration byte */ __u8 rx_flags; /* throttling - used from whiteheat/ftdi_sio */ - int baud_rate; /* stores current baud rate in integer form */ - int cbr_mask; /* stores current baud rate in masked form */ + enum packet_format pkt_fmt; /* format to use for packet send / receive */ + int get_cfg_unsafe; /* If true, the CYPRESS_GET_CONFIG is unsafe */ + int baud_rate; /* stores current baud rate in + integer form */ int isthrottled; /* if throttled, discard reads */ - wait_queue_head_t delta_msr_wait; /* used for TIOCMIWAIT */ - char prev_status, diff_status; /* used for TIOCMIWAIT */ - /* we pass a pointer to this as the arguement sent to cypress_set_termios old_termios */ + char prev_status; /* used for TIOCMIWAIT */ + /* we pass a pointer to this as the argument sent to + cypress_set_termios old_termios */ struct ktermios tmp_termios; /* stores the old termios settings */ }; -/* write buffer structure */ -struct cypress_buf { - unsigned int buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; - /* function prototypes for the Cypress USB to serial device */ -static int cypress_earthmate_startup (struct usb_serial *serial); -static int cypress_hidcom_startup (struct usb_serial *serial); -static int cypress_ca42v2_startup (struct usb_serial *serial); -static void cypress_shutdown (struct usb_serial *serial); -static int cypress_open (struct usb_serial_port *port, struct file *filp); -static void cypress_close (struct usb_serial_port *port, struct file *filp); -static int cypress_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static void cypress_send (struct usb_serial_port *port); -static int cypress_write_room (struct usb_serial_port *port); -static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void cypress_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int cypress_tiocmget (struct usb_serial_port *port, struct file *file); -static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -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); -static void cypress_write_int_callback (struct urb *urb); -/* baud helper functions */ -static int mask_to_rate (unsigned mask); -static unsigned rate_to_mask (int rate); -/* write buffer functions */ -static struct cypress_buf *cypress_buf_alloc(unsigned int size); -static void cypress_buf_free(struct cypress_buf *cb); -static void cypress_buf_clear(struct cypress_buf *cb); -static unsigned int cypress_buf_data_avail(struct cypress_buf *cb); -static unsigned int cypress_buf_space_avail(struct cypress_buf *cb); -static unsigned int cypress_buf_put(struct cypress_buf *cb, const char *buf, unsigned int count); -static unsigned int cypress_buf_get(struct cypress_buf *cb, char *buf, unsigned int count); - +static int cypress_earthmate_port_probe(struct usb_serial_port *port); +static int cypress_hidcom_port_probe(struct usb_serial_port *port); +static int cypress_ca42v2_port_probe(struct usb_serial_port *port); +static int cypress_port_remove(struct usb_serial_port *port); +static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port); +static void cypress_close(struct usb_serial_port *port); +static void cypress_dtr_rts(struct usb_serial_port *port, int on); +static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static void cypress_send(struct usb_serial_port *port); +static int cypress_write_room(struct tty_struct *tty); +static void cypress_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int cypress_tiocmget(struct tty_struct *tty); +static int cypress_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); +static int cypress_chars_in_buffer(struct tty_struct *tty); +static void cypress_throttle(struct tty_struct *tty); +static void cypress_unthrottle(struct tty_struct *tty); +static void cypress_set_dead(struct usb_serial_port *port); +static void cypress_read_int_callback(struct urb *urb); +static void cypress_write_int_callback(struct urb *urb); static struct usb_serial_driver cypress_earthmate_device = { .driver = { @@ -195,23 +148,19 @@ static struct usb_serial_driver cypress_earthmate_device = { .name = "earthmate", }, .description = "DeLorme Earthmate USB", - .usb_driver = &cypress_driver, .id_table = id_table_earthmate, - .num_interrupt_in = 1, - .num_interrupt_out = 1, - .num_bulk_in = NUM_DONT_CARE, - .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, - .attach = cypress_earthmate_startup, - .shutdown = cypress_shutdown, + .port_probe = cypress_earthmate_port_probe, + .port_remove = cypress_port_remove, .open = cypress_open, .close = cypress_close, + .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, - .ioctl = cypress_ioctl, .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -225,23 +174,19 @@ static struct usb_serial_driver cypress_hidcom_device = { .name = "cyphidcom", }, .description = "HID->COM RS232 Adapter", - .usb_driver = &cypress_driver, .id_table = id_table_cyphidcomrs232, - .num_interrupt_in = 1, - .num_interrupt_out = 1, - .num_bulk_in = NUM_DONT_CARE, - .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, - .attach = cypress_hidcom_startup, - .shutdown = cypress_shutdown, + .port_probe = cypress_hidcom_port_probe, + .port_remove = cypress_port_remove, .open = cypress_open, .close = cypress_close, + .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, - .ioctl = cypress_ioctl, .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -252,26 +197,22 @@ static struct usb_serial_driver cypress_hidcom_device = { static struct usb_serial_driver cypress_ca42v2_device = { .driver = { .owner = THIS_MODULE, - .name = "nokiaca42v2", + .name = "nokiaca42v2", }, .description = "Nokia CA-42 V2 Adapter", - .usb_driver = &cypress_driver, .id_table = id_table_nokiaca42v2, - .num_interrupt_in = 1, - .num_interrupt_out = 1, - .num_bulk_in = NUM_DONT_CARE, - .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, - .attach = cypress_ca42v2_startup, - .shutdown = cypress_shutdown, + .port_probe = cypress_ca42v2_port_probe, + .port_remove = cypress_port_remove, .open = cypress_open, .close = cypress_close, + .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, - .ioctl = cypress_ioctl, .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -279,152 +220,201 @@ static struct usb_serial_driver cypress_ca42v2_device = { .write_int_callback = cypress_write_int_callback, }; +static struct usb_serial_driver * const serial_drivers[] = { + &cypress_earthmate_device, &cypress_hidcom_device, + &cypress_ca42v2_device, NULL +}; + /***************************************************************************** * Cypress serial helper functions *****************************************************************************/ +/* FRWD Dongle hidcom needs to skip reset and speed checks */ +static inline bool is_frwd(struct usb_device *dev) +{ + return ((le16_to_cpu(dev->descriptor.idVendor) == VENDOR_ID_FRWD) && + (le16_to_cpu(dev->descriptor.idProduct) == PRODUCT_ID_CYPHIDCOM_FRWD)); +} + +static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate) +{ + struct cypress_private *priv; + priv = usb_get_serial_port_data(port); + + if (unstable_bauds) + return new_rate; + + /* FRWD Dongle uses 115200 bps */ + if (is_frwd(port->serial->dev)) + return new_rate; + + /* + * The general purpose firmware for the Cypress M8 allows for + * a maximum speed of 57600bps (I have no idea whether DeLorme + * chose to use the general purpose firmware or not), if you + * need to modify this speed setting for your own project + * please add your own chiptype and modify the code likewise. + * The Cypress HID->COM device will work successfully up to + * 115200bps (but the actual throughput is around 3kBps). + */ + if (port->serial->dev->speed == USB_SPEED_LOW) { + /* + * Mike Isely <isely@pobox.com> 2-Feb-2008: The + * Cypress app note that describes this mechanism + * states the the low-speed part can't handle more + * than 800 bytes/sec, in which case 4800 baud is the + * safest speed for a part like that. + */ + if (new_rate > 4800) { + dev_dbg(&port->dev, + "%s - failed setting baud rate, device incapable speed %d\n", + __func__, new_rate); + return -1; + } + } + switch (priv->chiptype) { + case CT_EARTHMATE: + if (new_rate <= 600) { + /* 300 and 600 baud rates are supported under + * the generic firmware, but are not used with + * NMEA and SiRF protocols */ + dev_dbg(&port->dev, + "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n", + __func__, new_rate); + return -1; + } + break; + default: + break; + } + return new_rate; +} + /* This function can either set or retrieve the current serial line settings */ -static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_mask, int data_bits, int stop_bits, - int parity_enable, int parity_type, int reset, int cypress_request_type) +static int cypress_serial_control(struct tty_struct *tty, + struct usb_serial_port *port, speed_t baud_rate, int data_bits, + int stop_bits, int parity_enable, int parity_type, int reset, + int cypress_request_type) { int new_baudrate = 0, retval = 0, tries = 0; struct cypress_private *priv; - __u8 feature_buffer[8]; + struct device *dev = &port->dev; + u8 *feature_buffer; + const unsigned int feature_len = 5; unsigned long flags; - dbg("%s", __FUNCTION__); - priv = usb_get_serial_port_data(port); if (!priv->comm_is_ok) return -ENODEV; - switch(cypress_request_type) { - case CYPRESS_SET_CONFIG: - - /* - * The general purpose firmware for the Cypress M8 allows for a maximum speed - * of 57600bps (I have no idea whether DeLorme chose to use the general purpose - * firmware or not), if you need to modify this speed setting for your own - * project please add your own chiptype and modify the code likewise. The - * Cypress HID->COM device will work successfully up to 115200bps (but the - * actual throughput is around 3kBps). - */ - if (baud_mask != priv->cbr_mask) { - dbg("%s - baud rate is changing", __FUNCTION__); - if ( priv->chiptype == CT_EARTHMATE ) { - /* 300 and 600 baud rates are supported under the generic firmware, - * but are not used with NMEA and SiRF protocols */ - - if ( (baud_mask == B300) || (baud_mask == B600) ) { - err("%s - failed setting baud rate, unsupported speed", - __FUNCTION__); - new_baudrate = priv->baud_rate; - } else if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) { - err("%s - failed setting baud rate, unsupported speed", - __FUNCTION__); - new_baudrate = priv->baud_rate; - } - } else if (priv->chiptype == CT_CYPHIDCOM) { - if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) { - err("%s - failed setting baud rate, unsupported speed", - __FUNCTION__); - new_baudrate = priv->baud_rate; - } - } else if (priv->chiptype == CT_CA42V2) { - if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) { - err("%s - failed setting baud rate, unsupported speed", - __FUNCTION__); - new_baudrate = priv->baud_rate; - } - } else if (priv->chiptype == CT_GENERIC) { - if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) { - err("%s - failed setting baud rate, unsupported speed", - __FUNCTION__); - new_baudrate = priv->baud_rate; - } - } else { - info("%s - please define your chiptype", __FUNCTION__); - new_baudrate = priv->baud_rate; - } - } else { /* baud rate not changing, keep the old */ - new_baudrate = priv->baud_rate; - } - dbg("%s - baud rate is being sent as %d", __FUNCTION__, new_baudrate); - - memset(feature_buffer, 0, 8); - /* fill the feature_buffer with new configuration */ - *((u_int32_t *)feature_buffer) = new_baudrate; - - feature_buffer[4] |= data_bits; /* assign data bits in 2 bit space ( max 3 ) */ - /* 1 bit gap */ - feature_buffer[4] |= (stop_bits << 3); /* assign stop bits in 1 bit space */ - feature_buffer[4] |= (parity_enable << 4); /* assign parity flag in 1 bit space */ - feature_buffer[4] |= (parity_type << 5); /* assign parity type in 1 bit space */ - /* 1 bit gap */ - feature_buffer[4] |= (reset << 7); /* assign reset at end of byte, 1 bit space */ - - dbg("%s - device is being sent this feature report:", __FUNCTION__); - dbg("%s - %02X - %02X - %02X - %02X - %02X", __FUNCTION__, feature_buffer[0], feature_buffer[1], - feature_buffer[2], feature_buffer[3], feature_buffer[4]); - - do { - retval = usb_control_msg (port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), - HID_REQ_SET_REPORT, USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS, - 0x0300, 0, feature_buffer, 8, 500); - - if (tries++ >= 3) - break; - - } while (retval != 8 && retval != -ENODEV); - - if (retval != 8) { - err("%s - failed sending serial line settings - %d", __FUNCTION__, retval); - cypress_set_dead(port); - } else { - spin_lock_irqsave(&priv->lock, flags); - priv->baud_rate = new_baudrate; - priv->cbr_mask = baud_mask; - priv->current_config = feature_buffer[4]; - spin_unlock_irqrestore(&priv->lock, flags); - } - break; - case CYPRESS_GET_CONFIG: - dbg("%s - retreiving serial line settings", __FUNCTION__); - /* set initial values in feature buffer */ - memset(feature_buffer, 0, 8); - - do { - retval = usb_control_msg (port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), - HID_REQ_GET_REPORT, USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS, - 0x0300, 0, feature_buffer, 8, 500); - - if (tries++ >= 3) - break; - - } 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); - - /* store the config in one byte, and later use bit masks to check values */ - priv->current_config = feature_buffer[4]; - priv->baud_rate = *((u_int32_t *)feature_buffer); - - if ( (priv->cbr_mask = rate_to_mask(priv->baud_rate)) == 0x40) - dbg("%s - failed setting the baud mask (not defined)", __FUNCTION__); - spin_unlock_irqrestore(&priv->lock, flags); + feature_buffer = kcalloc(feature_len, sizeof(u8), GFP_KERNEL); + if (!feature_buffer) + return -ENOMEM; + + switch (cypress_request_type) { + case CYPRESS_SET_CONFIG: + /* 0 means 'Hang up' so doesn't change the true bit rate */ + new_baudrate = priv->baud_rate; + if (baud_rate && baud_rate != priv->baud_rate) { + dev_dbg(dev, "%s - baud rate is changing\n", __func__); + retval = analyze_baud_rate(port, baud_rate); + if (retval >= 0) { + new_baudrate = retval; + dev_dbg(dev, "%s - New baud rate set to %d\n", + __func__, new_baudrate); } + } + dev_dbg(dev, "%s - baud rate is being sent as %d\n", __func__, + new_baudrate); + + /* fill the feature_buffer with new configuration */ + put_unaligned_le32(new_baudrate, feature_buffer); + feature_buffer[4] |= data_bits; /* assign data bits in 2 bit space ( max 3 ) */ + /* 1 bit gap */ + feature_buffer[4] |= (stop_bits << 3); /* assign stop bits in 1 bit space */ + feature_buffer[4] |= (parity_enable << 4); /* assign parity flag in 1 bit space */ + feature_buffer[4] |= (parity_type << 5); /* assign parity type in 1 bit space */ + /* 1 bit gap */ + feature_buffer[4] |= (reset << 7); /* assign reset at end of byte, 1 bit space */ + + dev_dbg(dev, "%s - device is being sent this feature report:\n", __func__); + dev_dbg(dev, "%s - %02X - %02X - %02X - %02X - %02X\n", __func__, + feature_buffer[0], feature_buffer[1], + feature_buffer[2], feature_buffer[3], + feature_buffer[4]); + + do { + retval = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + HID_REQ_SET_REPORT, + USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS, + 0x0300, 0, feature_buffer, + feature_len, 500); + + if (tries++ >= 3) + break; + + } while (retval != feature_len && + retval != -ENODEV); + + if (retval != feature_len) { + dev_err(dev, "%s - failed sending serial line settings - %d\n", + __func__, retval); + cypress_set_dead(port); + } else { + spin_lock_irqsave(&priv->lock, flags); + priv->baud_rate = new_baudrate; + priv->current_config = feature_buffer[4]; + spin_unlock_irqrestore(&priv->lock, flags); + /* If we asked for a speed change encode it */ + if (baud_rate) + tty_encode_baud_rate(tty, + new_baudrate, new_baudrate); + } + break; + case CYPRESS_GET_CONFIG: + if (priv->get_cfg_unsafe) { + /* Not implemented for this device, + and if we try to do it we're likely + to crash the hardware. */ + retval = -ENOTTY; + goto out; + } + dev_dbg(dev, "%s - retreiving serial line settings\n", __func__); + do { + retval = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS, + 0x0300, 0, feature_buffer, + feature_len, 500); + + if (tries++ >= 3) + break; + } while (retval != feature_len + && retval != -ENODEV); + + if (retval != feature_len) { + dev_err(dev, "%s - failed to retrieve serial line settings - %d\n", + __func__, retval); + cypress_set_dead(port); + goto out; + } else { + spin_lock_irqsave(&priv->lock, flags); + /* store the config in one byte, and later + use bit masks to check values */ + priv->current_config = feature_buffer[4]; + priv->baud_rate = get_unaligned_le32(feature_buffer); + spin_unlock_irqrestore(&priv->lock, flags); + } } spin_lock_irqsave(&priv->lock, flags); ++priv->cmd_count; spin_unlock_irqrestore(&priv->lock, flags); - +out: + kfree(feature_buffer); return retval; } /* cypress_serial_control */ @@ -442,190 +432,158 @@ static void cypress_set_dead(struct usb_serial_port *port) 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) -{ - int rate; - - switch (mask) { - case B0: rate = 0; break; - case B300: rate = 300; break; - case B600: rate = 600; break; - case B1200: rate = 1200; break; - case B2400: rate = 2400; break; - case B4800: rate = 4800; break; - case B9600: rate = 9600; break; - case B19200: rate = 19200; break; - case B38400: rate = 38400; break; - case B57600: rate = 57600; break; - case B115200: rate = 115200; break; - default: rate = -1; - } - - return rate; + dev_err(&port->dev, "cypress_m8 suspending failing port %d - " + "interval might be too short\n", port->port_number); } -static unsigned rate_to_mask (int rate) -{ - unsigned mask; - - switch (rate) { - case 0: mask = B0; break; - case 300: mask = B300; break; - case 600: mask = B600; break; - case 1200: mask = B1200; break; - case 2400: mask = B2400; break; - case 4800: mask = B4800; break; - case 9600: mask = B9600; break; - case 19200: mask = B19200; break; - case 38400: mask = B38400; break; - case 57600: mask = B57600; break; - case 115200: mask = B115200; break; - default: mask = 0x40; - } - - return mask; -} /***************************************************************************** * Cypress serial driver functions *****************************************************************************/ -static int generic_startup (struct usb_serial *serial) +static int cypress_generic_port_probe(struct usb_serial_port *port) { + struct usb_serial *serial = port->serial; struct cypress_private *priv; - struct usb_serial_port *port = serial->port[0]; - - dbg("%s - port %d", __FUNCTION__, port->number); - priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL); + 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) { + if (kfifo_alloc(&priv->write_fifo, CYPRESS_BUF_SIZE, GFP_KERNEL)) { kfree(priv); return -ENOMEM; } - init_waitqueue_head(&priv->delta_msr_wait); - - usb_reset_configuration (serial->dev); - + + /* Skip reset for FRWD device. It is a workaound: + device hangs if it receives SET_CONFIGURE in Configured + state. */ + if (!is_frwd(serial->dev)) + usb_reset_configuration(serial->dev); + priv->cmd_ctrl = 0; priv->line_control = 0; priv->termios_initialized = 0; priv->rx_flags = 0; - priv->cbr_mask = B300; + /* Default packet format setting is determined by packet size. + Anything with a size larger then 9 must have a separate + count field since the 3 bit count field is otherwise too + small. Otherwise we can use the slightly more compact + format. This is in accordance with the cypress_m8 serial + converter app note. */ + if (port->interrupt_out_size > 9) + priv->pkt_fmt = packet_format_1; + else + priv->pkt_fmt = packet_format_2; + 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); + dev_dbg(&port->dev, "%s - read & write intervals forced to %d\n", + __func__, 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); + dev_dbg(&port->dev, "%s - intervals: read=%d write=%d\n", + __func__, priv->read_urb_interval, + priv->write_urb_interval); } usb_set_serial_port_data(port, priv); - + + port->port.drain_delay = 256; + return 0; } -static int cypress_earthmate_startup (struct usb_serial *serial) +static int cypress_earthmate_port_probe(struct usb_serial_port *port) { + struct usb_serial *serial = port->serial; struct cypress_private *priv; + int ret; - dbg("%s", __FUNCTION__); - - if (generic_startup(serial)) { - dbg("%s - Failed setting up port %d", __FUNCTION__, - serial->port[0]->number); - return 1; + ret = cypress_generic_port_probe(port); + if (ret) { + dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); + return ret; } - priv = usb_get_serial_port_data(serial->port[0]); + priv = usb_get_serial_port_data(port); priv->chiptype = CT_EARTHMATE; + /* All Earthmate devices use the separated-count packet + format! Idiotic. */ + priv->pkt_fmt = packet_format_1; + if (serial->dev->descriptor.idProduct != + cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) { + /* The old original USB Earthmate seemed able to + handle GET_CONFIG requests; everything they've + produced since that time crashes if this command is + attempted :-( */ + dev_dbg(&port->dev, + "%s - Marking this device as unsafe for GET_CONFIG commands\n", + __func__); + priv->get_cfg_unsafe = !0; + } return 0; -} /* cypress_earthmate_startup */ - +} -static int cypress_hidcom_startup (struct usb_serial *serial) +static int cypress_hidcom_port_probe(struct usb_serial_port *port) { struct cypress_private *priv; + int ret; - dbg("%s", __FUNCTION__); - - if (generic_startup(serial)) { - dbg("%s - Failed setting up port %d", __FUNCTION__, - serial->port[0]->number); - return 1; + ret = cypress_generic_port_probe(port); + if (ret) { + dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); + return ret; } - priv = usb_get_serial_port_data(serial->port[0]); + priv = usb_get_serial_port_data(port); priv->chiptype = CT_CYPHIDCOM; - - return 0; -} /* cypress_hidcom_startup */ + return 0; +} -static int cypress_ca42v2_startup (struct usb_serial *serial) +static int cypress_ca42v2_port_probe(struct usb_serial_port *port) { struct cypress_private *priv; + int ret; - dbg("%s", __FUNCTION__); - - if (generic_startup(serial)) { - dbg("%s - Failed setting up port %d", __FUNCTION__, - serial->port[0]->number); - return 1; + ret = cypress_generic_port_probe(port); + if (ret) { + dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__); + return ret; } - priv = usb_get_serial_port_data(serial->port[0]); + priv = usb_get_serial_port_data(port); priv->chiptype = CT_CA42V2; return 0; -} /* cypress_ca42v2_startup */ - +} -static void cypress_shutdown (struct usb_serial *serial) +static int cypress_port_remove(struct usb_serial_port *port) { struct cypress_private *priv; - dbg ("%s - port %d", __FUNCTION__, serial->port[0]->number); - - /* all open ports are closed at this point */ + priv = usb_get_serial_port_data(port); - priv = usb_get_serial_port_data(serial->port[0]); + kfifo_free(&priv->write_fifo); + kfree(priv); - if (priv) { - cypress_buf_free(priv->buf); - kfree(priv); - usb_set_serial_port_data(serial->port[0], NULL); - } + return 0; } - -static int cypress_open (struct usb_serial_port *port, struct file *filp) +static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) { struct cypress_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; unsigned long flags; int result = 0; - dbg("%s - port %d", __FUNCTION__, port->number); - if (!priv->comm_is_ok) return -EIO; @@ -641,121 +599,75 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) priv->rx_flags = 0; spin_unlock_irqrestore(&priv->lock, flags); - /* setting to zero could cause data loss */ - port->tty->low_latency = 1; - - /* raise both lines and set termios */ - spin_lock_irqsave(&priv->lock, flags); - priv->line_control = CONTROL_DTR | CONTROL_RTS; - priv->cmd_ctrl = 1; - spin_unlock_irqrestore(&priv->lock, flags); - result = cypress_write(port, NULL, 0); - - if (result) { - dev_err(&port->dev, "%s - failed setting the control lines - error %d\n", __FUNCTION__, result); - return result; - } else - dbg("%s - success setting the control lines", __FUNCTION__); + /* Set termios */ + cypress_send(port); - cypress_set_termios(port, &priv->tmp_termios); + if (tty) + cypress_set_termios(tty, port, &priv->tmp_termios); /* setup the port and start reading from the device */ - if(!port->interrupt_in_urb){ - err("%s - interrupt_in_urb is empty!", __FUNCTION__); - return(-1); + if (!port->interrupt_in_urb) { + dev_err(&port->dev, "%s - interrupt_in_urb is empty!\n", + __func__); + return -1; } 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, + port->interrupt_in_urb->transfer_buffer, + port->interrupt_in_urb->transfer_buffer_length, 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); + if (result) { + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); cypress_set_dead(port); } return result; } /* cypress_open */ - -static void cypress_close(struct usb_serial_port *port, struct file * filp) +static void cypress_dtr_rts(struct usb_serial_port *port, int on) { struct cypress_private *priv = usb_get_serial_port_data(port); - unsigned int c_cflag; - int bps; - long timeout; - wait_queue_t wait; - - dbg("%s - port %d", __FUNCTION__, port->number); - - /* wait for data to drain from buffer */ + /* drop dtr and rts */ spin_lock_irq(&priv->lock); - timeout = CYPRESS_CLOSING_WAIT; - init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (cypress_buf_data_avail(priv->buf) == 0 - || timeout == 0 || signal_pending(current) - /* without mutex, allowed due to harmless failure mode */ - || port->serial->disconnected) - break; - spin_unlock_irq(&priv->lock); - timeout = schedule_timeout(timeout); - spin_lock_irq(&priv->lock); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); - /* clear out any remaining data in the buffer */ - cypress_buf_clear(priv->buf); + if (on == 0) + priv->line_control = 0; + else + priv->line_control = CONTROL_DTR | CONTROL_RTS; + priv->cmd_ctrl = 1; spin_unlock_irq(&priv->lock); + cypress_write(NULL, port, NULL, 0); +} - /* writing is potentially harmful, lock must be taken */ - mutex_lock(&port->serial->disc_mutex); - if (port->serial->disconnected) { - mutex_unlock(&port->serial->disc_mutex); - return; - } - /* wait for characters to drain from device */ - bps = tty_get_baud_rate(port->tty); - if (bps > 1200) - timeout = max((HZ*2560)/bps,HZ/10); - else - timeout = 2*HZ; - schedule_timeout_interruptible(timeout); - - dbg("%s - stopping urbs", __FUNCTION__); - usb_kill_urb (port->interrupt_in_urb); - usb_kill_urb (port->interrupt_out_urb); - - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; - if (c_cflag & HUPCL) { - /* drop dtr and rts */ - priv = usb_get_serial_port_data(port); - spin_lock_irq(&priv->lock); - priv->line_control = 0; - priv->cmd_ctrl = 1; - spin_unlock_irq(&priv->lock); - cypress_write(port, NULL, 0); - } - } +static void cypress_close(struct usb_serial_port *port) +{ + struct cypress_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + kfifo_reset_out(&priv->write_fifo); + spin_unlock_irqrestore(&priv->lock, flags); + + dev_dbg(&port->dev, "%s - stopping urbs\n", __func__); + usb_kill_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_out_urb); if (stats) - dev_info (&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", - priv->bytes_in, priv->bytes_out, priv->cmd_count); - mutex_unlock(&port->serial->disc_mutex); + dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", + priv->bytes_in, priv->bytes_out, priv->cmd_count); } /* cypress_close */ -static int cypress_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct cypress_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); + + dev_dbg(&port->dev, "%s - %d bytes\n", __func__, count); /* line control commands, which need to be executed immediately, are not put into the buffer for obvious reasons. @@ -764,13 +676,11 @@ static int cypress_write(struct usb_serial_port *port, const unsigned char *buf, count = 0; goto finish; } - + if (!count) return count; - - spin_lock_irqsave(&priv->lock, flags); - count = cypress_buf_put(priv->buf, buf, count); - spin_unlock_irqrestore(&priv->lock, flags); + + count = kfifo_in_locked(&priv->write_fifo, buf, count, &priv->lock); finish: cypress_send(port); @@ -783,41 +693,40 @@ static void cypress_send(struct usb_serial_port *port) { int count = 0, result, offset, actual_size; struct cypress_private *priv = usb_get_serial_port_data(port); + struct device *dev = &port->dev; 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); - + dev_dbg(dev, "%s - interrupt out size is %d\n", __func__, + port->interrupt_out_size); + spin_lock_irqsave(&priv->lock, flags); if (priv->write_urb_in_use) { - dbg("%s - can't write, urb in use", __FUNCTION__); + dev_dbg(dev, "%s - can't write, urb in use\n", __func__); spin_unlock_irqrestore(&priv->lock, flags); return; } spin_unlock_irqrestore(&priv->lock, flags); /* clear buffer */ - memset(port->interrupt_out_urb->transfer_buffer, 0, port->interrupt_out_size); + memset(port->interrupt_out_urb->transfer_buffer, 0, + port->interrupt_out_size); spin_lock_irqsave(&priv->lock, flags); - switch (port->interrupt_out_size) { - case 32: - /* this is for the CY7C64013... */ - offset = 2; - port->interrupt_out_buffer[0] = priv->line_control; - break; - case 8: - /* this is for the CY7C63743... */ - offset = 1; - port->interrupt_out_buffer[0] = priv->line_control; - break; - default: - dbg("%s - wrong packet size", __FUNCTION__); - spin_unlock_irqrestore(&priv->lock, flags); - return; + switch (priv->pkt_fmt) { + default: + case packet_format_1: + /* this is for the CY7C64013... */ + offset = 2; + port->interrupt_out_buffer[0] = priv->line_control; + break; + case packet_format_2: + /* this is for the CY7C63743... */ + offset = 1; + port->interrupt_out_buffer[0] = priv->line_control; + break; } if (priv->line_control & CONTROL_RESET) @@ -825,28 +734,29 @@ static void cypress_send(struct usb_serial_port *port) if (priv->cmd_ctrl) { priv->cmd_count++; - dbg("%s - line control command being issued", __FUNCTION__); + dev_dbg(dev, "%s - line control command being issued\n", __func__); spin_unlock_irqrestore(&priv->lock, flags); goto send; } else spin_unlock_irqrestore(&priv->lock, flags); - count = cypress_buf_get(priv->buf, &port->interrupt_out_buffer[offset], - port->interrupt_out_size-offset); - - if (count == 0) { + count = kfifo_out_locked(&priv->write_fifo, + &port->interrupt_out_buffer[offset], + port->interrupt_out_size - offset, + &priv->lock); + if (count == 0) return; - } - switch (port->interrupt_out_size) { - case 32: - port->interrupt_out_buffer[1] = count; - break; - case 8: - port->interrupt_out_buffer[0] |= count; + switch (priv->pkt_fmt) { + default: + case packet_format_1: + port->interrupt_out_buffer[1] = count; + break; + case packet_format_2: + port->interrupt_out_buffer[0] |= count; } - dbg("%s - count is %d", __FUNCTION__, count); + dev_dbg(dev, "%s - count is %d\n", __func__, count); send: spin_lock_irqsave(&priv->lock, flags); @@ -856,28 +766,31 @@ send: if (priv->cmd_ctrl) actual_size = 1; else - actual_size = count + (port->interrupt_out_size == 32 ? 2 : 1); - - usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size, + actual_size = count + + (priv->pkt_fmt == packet_format_1 ? 2 : 1); + + usb_serial_debug_data(dev, __func__, port->interrupt_out_size, port->interrupt_out_urb->transfer_buffer); 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); + 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); + dev_err_console(port, + "%s - failed submitting write urb, error %d\n", + __func__, result); priv->write_urb_in_use = 0; cypress_set_dead(port); } spin_lock_irqsave(&priv->lock, flags); - if (priv->cmd_ctrl) { + if (priv->cmd_ctrl) priv->cmd_ctrl = 0; - } - priv->bytes_out += count; /* do not count the line control and size bytes */ + + /* do not count the line control and size bytes */ + priv->bytes_out += count; spin_unlock_irqrestore(&priv->lock, flags); usb_serial_port_softint(port); @@ -885,31 +798,29 @@ send: /* returns how much space is available in the soft buffer */ -static int cypress_write_room(struct usb_serial_port *port) +static int cypress_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; - dbg("%s - port %d", __FUNCTION__, port->number); - spin_lock_irqsave(&priv->lock, flags); - room = cypress_buf_space_avail(priv->buf); + room = kfifo_avail(&priv->write_fifo); spin_unlock_irqrestore(&priv->lock, flags); - dbg("%s - returns %d", __FUNCTION__, room); + dev_dbg(&port->dev, "%s - returns %d\n", __func__, room); return room; } -static int cypress_tiocmget (struct usb_serial_port *port, struct file *file) +static int cypress_tiocmget(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); __u8 status, control; unsigned int result = 0; unsigned long flags; - - dbg("%s - port %d", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags); control = priv->line_control; @@ -923,19 +834,18 @@ static int cypress_tiocmget (struct usb_serial_port *port, struct file *file) | ((status & UART_RI) ? TIOCM_RI : 0) | ((status & UART_CD) ? TIOCM_CD : 0); - dbg("%s - result = %x", __FUNCTION__, result); + dev_dbg(&port->dev, "%s - result = %x\n", __func__, result); return result; } -static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, +static int cypress_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; - - dbg("%s - port %d", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags); if (set & TIOCM_RTS) @@ -946,134 +856,62 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, priv->line_control &= ~CONTROL_RTS; if (clear & TIOCM_DTR) priv->line_control &= ~CONTROL_DTR; + priv->cmd_ctrl = 1; spin_unlock_irqrestore(&priv->lock, flags); - priv->cmd_ctrl = 1; - return cypress_write(port, NULL, 0); + return cypress_write(tty, port, NULL, 0); } - -static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - struct cypress_private *priv = usb_get_serial_port_data(port); - - dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd); - - switch (cmd) { - case TIOCGSERIAL: - if (copy_to_user((void __user *)arg, port->tty->termios, sizeof(struct ktermios))) { - return -EFAULT; - } - return (0); - break; - case TIOCSSERIAL: - if (copy_from_user(port->tty->termios, (void __user *)arg, sizeof(struct ktermios))) { - return -EFAULT; - } - /* here we need to call cypress_set_termios to invoke the new settings */ - cypress_set_termios(port, &priv->tmp_termios); - return (0); - break; - /* This code comes from drivers/char/serial.c and ftdi_sio.c */ - case TIOCMIWAIT: - while (priv != NULL) { - interruptible_sleep_on(&priv->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - else { - char diff = priv->diff_status; - - if (diff == 0) { - return -EIO; /* no change => error */ - } - - /* consume all events */ - priv->diff_status = 0; - - /* return 0 if caller wanted to know about these bits */ - if ( ((arg & TIOCM_RNG) && (diff & UART_RI)) || - ((arg & TIOCM_DSR) && (diff & UART_DSR)) || - ((arg & TIOCM_CD) && (diff & UART_CD)) || - ((arg & TIOCM_CTS) && (diff & UART_CTS)) ) { - return 0; - } - /* otherwise caller can't care less about what happened, - * and so we continue to wait for more events. - */ - } - } - return 0; - break; - default: - break; - } - - dbg("%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h", __FUNCTION__, cmd); - - return -ENOIOCTLCMD; -} /* cypress_ioctl */ - - -static void cypress_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void cypress_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct cypress_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty; + struct device *dev = &port->dev; int data_bits, stop_bits, parity_type, parity_enable; - unsigned cflag, iflag, baud_mask; + unsigned cflag, iflag; unsigned long flags; __u8 oldlines; int linechange = 0; - dbg("%s - port %d", __FUNCTION__, port->number); - - tty = port->tty; - if ((!tty) || (!tty->termios)) { - dbg("%s - no tty structures", __FUNCTION__); - return; - } - spin_lock_irqsave(&priv->lock, flags); + /* We can't clean this one up as we don't know the device type + early enough */ if (!priv->termios_initialized) { if (priv->chiptype == CT_EARTHMATE) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B4800 | CS8 | CREAD | HUPCL | + tty->termios = tty_std_termios; + tty->termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios.c_ispeed = 4800; + tty->termios.c_ospeed = 4800; } else if (priv->chiptype == CT_CYPHIDCOM) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | + tty->termios = tty_std_termios; + tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios.c_ispeed = 9600; + tty->termios.c_ospeed = 9600; } else if (priv->chiptype == CT_CA42V2) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | + tty->termios = tty_std_termios; + tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios.c_ispeed = 9600; + tty->termios.c_ospeed = 9600; } priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); - cflag = tty->termios->c_cflag; - iflag = tty->termios->c_iflag; + /* Unsupported features need clearing */ + tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS); + + cflag = tty->termios.c_cflag; + iflag = tty->termios.c_iflag; /* check if there are new settings */ if (old_termios) { - if ((cflag != old_termios->c_cflag) || - (RELEVANT_IFLAG(iflag) != - RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("%s - attempting to set new termios settings", - __FUNCTION__); - /* should make a copy of this in case something goes - * wrong in the function, we can restore it */ - spin_lock_irqsave(&priv->lock, flags); - priv->tmp_termios = *(tty->termios); - spin_unlock_irqrestore(&priv->lock, flags); - } else { - dbg("%s - nothing to do, exiting", __FUNCTION__); - return; - } - } else - return; + spin_lock_irqsave(&priv->lock, flags); + priv->tmp_termios = tty->termios; + spin_unlock_irqrestore(&priv->lock, flags); + } /* set number of data bits, parity, stop bits */ /* when parity is disabled the parity type bit is ignored */ @@ -1088,97 +926,55 @@ static void cypress_set_termios (struct usb_serial_port *port, } else parity_enable = parity_type = 0; - if (cflag & CSIZE) { - switch (cflag & CSIZE) { - case CS5: - data_bits = 0; - break; - case CS6: - data_bits = 1; - break; - case CS7: - data_bits = 2; - break; - case CS8: - data_bits = 3; - break; - default: - err("%s - CSIZE was set, but not CS5-CS8", - __FUNCTION__); - data_bits = 3; - } - } else + switch (cflag & CSIZE) { + case CS5: + data_bits = 0; + break; + case CS6: + data_bits = 1; + break; + case CS7: + data_bits = 2; + break; + case CS8: data_bits = 3; - + break; + default: + dev_err(dev, "%s - CSIZE was set, but not CS5-CS8\n", __func__); + data_bits = 3; + } spin_lock_irqsave(&priv->lock, flags); oldlines = priv->line_control; if ((cflag & CBAUD) == B0) { /* drop dtr and rts */ - dbg("%s - dropping the lines, baud rate 0bps", __FUNCTION__); - baud_mask = B0; + dev_dbg(dev, "%s - dropping the lines, baud rate 0bps\n", __func__); priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); - } else { - baud_mask = (cflag & CBAUD); - switch(baud_mask) { - case B300: - dbg("%s - setting baud 300bps", __FUNCTION__); - break; - case B600: - dbg("%s - setting baud 600bps", __FUNCTION__); - break; - case B1200: - dbg("%s - setting baud 1200bps", __FUNCTION__); - break; - case B2400: - dbg("%s - setting baud 2400bps", __FUNCTION__); - break; - case B4800: - dbg("%s - setting baud 4800bps", __FUNCTION__); - break; - case B9600: - dbg("%s - setting baud 9600bps", __FUNCTION__); - break; - case B19200: - dbg("%s - setting baud 19200bps", __FUNCTION__); - break; - case B38400: - dbg("%s - setting baud 38400bps", __FUNCTION__); - break; - case B57600: - dbg("%s - setting baud 57600bps", __FUNCTION__); - break; - case B115200: - dbg("%s - setting baud 115200bps", __FUNCTION__); - break; - default: - dbg("%s - unknown masked baud rate", __FUNCTION__); - } + } else priv->line_control = (CONTROL_DTR | CONTROL_RTS); - } spin_unlock_irqrestore(&priv->lock, flags); - dbg("%s - sending %d stop_bits, %d parity_enable, %d parity_type, " - "%d data_bits (+5)", __FUNCTION__, stop_bits, - parity_enable, parity_type, data_bits); + dev_dbg(dev, "%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)\n", + __func__, stop_bits, parity_enable, parity_type, data_bits); - cypress_serial_control(port, baud_mask, data_bits, stop_bits, - parity_enable, parity_type, 0, CYPRESS_SET_CONFIG); + cypress_serial_control(tty, port, tty_get_baud_rate(tty), + data_bits, stop_bits, + parity_enable, parity_type, + 0, CYPRESS_SET_CONFIG); /* we perform a CYPRESS_GET_CONFIG so that the current settings are * filled into the private structure this should confirm that all is * working if it returns what we just set */ - cypress_serial_control(port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); + cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); /* Here we can define custom tty settings for devices; the main tty * termios flag base comes from empeg.c */ spin_lock_irqsave(&priv->lock, flags); - if ( (priv->chiptype == CT_EARTHMATE) && (priv->baud_rate == 4800) ) { - dbg("Using custom termios settings for a baud rate of " - "4800bps."); + if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) { + dev_dbg(dev, "Using custom termios settings for a baud rate of 4800bps.\n"); /* define custom termios settings for NMEA protocol */ - tty->termios->c_iflag /* input modes - */ + tty->termios.c_iflag /* input modes - */ &= ~(IGNBRK /* disable ignore break */ | BRKINT /* disable break causes interrupt */ | PARMRK /* disable mark parity errors */ @@ -1188,10 +984,10 @@ static void cypress_set_termios (struct usb_serial_port *port, | ICRNL /* disable translate CR to NL */ | IXON); /* disable enable XON/XOFF flow control */ - tty->termios->c_oflag /* output modes */ + tty->termios.c_oflag /* output modes */ &= ~OPOST; /* disable postprocess output char */ - tty->termios->c_lflag /* line discipline modes */ + tty->termios.c_lflag /* line discipline modes */ &= ~(ECHO /* disable echo input characters */ | ECHONL /* disable echo new line */ | ICANON /* disable erase, kill, werase, and rprnt @@ -1207,65 +1003,58 @@ static void cypress_set_termios (struct usb_serial_port *port, /* if necessary, set lines */ if (linechange) { priv->cmd_ctrl = 1; - cypress_write(port, NULL, 0); + cypress_write(tty, port, NULL, 0); } } /* cypress_set_termios */ /* returns amount of data still left in soft buffer */ -static int cypress_chars_in_buffer(struct usb_serial_port *port) +static int cypress_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; - dbg("%s - port %d", __FUNCTION__, port->number); - spin_lock_irqsave(&priv->lock, flags); - chars = cypress_buf_data_avail(priv->buf); + chars = kfifo_len(&priv->write_fifo); spin_unlock_irqrestore(&priv->lock, flags); - dbg("%s - returns %d", __FUNCTION__, chars); + dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars); return chars; } -static void cypress_throttle (struct usb_serial_port *port) +static void cypress_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - dbg("%s - port %d", __FUNCTION__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->rx_flags = THROTTLED; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } -static void cypress_unthrottle (struct usb_serial_port *port) +static void cypress_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int actually_throttled, result; - unsigned long flags; - - dbg("%s - port %d", __FUNCTION__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; priv->rx_flags = 0; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); 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); + 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); + "error %d\n", __func__, result); cypress_set_dead(port); } } @@ -1274,8 +1063,9 @@ static void cypress_unthrottle (struct usb_serial_port *port) static void cypress_read_int_callback(struct urb *urb) { - struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial_port *port = urb->context; struct cypress_private *priv = usb_get_serial_port_data(port); + struct device *dev = &urb->dev->dev; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; unsigned long flags; @@ -1286,8 +1076,6 @@ static void cypress_read_int_callback(struct urb *urb) int i = 0; int status = urb->status; - dbg("%s - port %d", __FUNCTION__, port->number); - switch (status) { case 0: /* success */ break; @@ -1297,77 +1085,89 @@ static void cypress_read_int_callback(struct urb *urb) /* precursor to disconnect so just go away */ return; case -EPIPE: - usb_clear_halt(port->serial->dev,0x81); - break; + /* Can't call usb_clear_halt while in_interrupt */ + /* FALLS THROUGH */ default: /* something ugly is going on... */ - dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n", - __FUNCTION__, status); + dev_err(dev, "%s - unexpected nonzero read status received: %d\n", + __func__, status); cypress_set_dead(port); return; } spin_lock_irqsave(&priv->lock, flags); if (priv->rx_flags & THROTTLED) { - dbg("%s - now throttling", __FUNCTION__); + dev_dbg(dev, "%s - now throttling\n", __func__); priv->rx_flags |= ACTUALLY_THROTTLED; spin_unlock_irqrestore(&priv->lock, flags); return; } spin_unlock_irqrestore(&priv->lock, flags); - tty = port->tty; + tty = tty_port_tty_get(&port->port); if (!tty) { - dbg("%s - bad tty pointer - exiting", __FUNCTION__); + dev_dbg(dev, "%s - bad tty pointer - exiting\n", __func__); return; } spin_lock_irqsave(&priv->lock, flags); - switch(urb->actual_length) { - case 32: - /* This is for the CY7C64013... */ - priv->current_status = data[0] & 0xF8; - bytes = data[1] + 2; - i = 2; - if (bytes > 2) - havedata = 1; - break; - case 8: - /* This is for the CY7C63743... */ - priv->current_status = data[0] & 0xF8; - bytes = (data[0] & 0x07) + 1; - i = 1; - if (bytes > 1) - havedata = 1; - break; - default: - dbg("%s - wrong packet size - received %d bytes", - __FUNCTION__, urb->actual_length); - spin_unlock_irqrestore(&priv->lock, flags); - goto continue_read; + result = urb->actual_length; + switch (priv->pkt_fmt) { + default: + case packet_format_1: + /* This is for the CY7C64013... */ + priv->current_status = data[0] & 0xF8; + bytes = data[1] + 2; + i = 2; + if (bytes > 2) + havedata = 1; + break; + case packet_format_2: + /* This is for the CY7C63743... */ + priv->current_status = data[0] & 0xF8; + bytes = (data[0] & 0x07) + 1; + i = 1; + if (bytes > 1) + havedata = 1; + break; } spin_unlock_irqrestore(&priv->lock, flags); + if (result < bytes) { + dev_dbg(dev, + "%s - wrong packet size - received %d bytes but packet said %d bytes\n", + __func__, result, bytes); + goto continue_read; + } - usb_serial_debug_data (debug, &port->dev, __FUNCTION__, - urb->actual_length, data); + usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); spin_lock_irqsave(&priv->lock, flags); /* check to see if status has changed */ - if (priv != NULL) { - if (priv->current_status != priv->prev_status) { - priv->diff_status |= priv->current_status ^ - priv->prev_status; - wake_up_interruptible(&priv->delta_msr_wait); - priv->prev_status = priv->current_status; + if (priv->current_status != priv->prev_status) { + u8 delta = priv->current_status ^ priv->prev_status; + + if (delta & UART_MSR_MASK) { + if (delta & UART_CTS) + port->icount.cts++; + if (delta & UART_DSR) + port->icount.dsr++; + if (delta & UART_RI) + port->icount.rng++; + if (delta & UART_CD) + port->icount.dcd++; + + wake_up_interruptible(&port->port.delta_msr_wait); } + + priv->prev_status = priv->current_status; } spin_unlock_irqrestore(&priv->lock, flags); /* hangup, as defined in acm.c... this might be a bad place for it * though */ - if (tty && !(tty->termios->c_cflag & CLOCAL) && + if (tty && !(tty->termios.c_cflag & CLOCAL) && !(priv->current_status & UART_CD)) { - dbg("%s - calling hangup", __FUNCTION__); + dev_dbg(dev, "%s - calling hangup\n", __func__); tty_hangup(tty); goto continue_read; } @@ -1380,19 +1180,15 @@ static void cypress_read_int_callback(struct urb *urb) if (priv->current_status & CYP_ERROR) { spin_unlock_irqrestore(&priv->lock, flags); tty_flag = TTY_PARITY; - dbg("%s - Parity Error detected", __FUNCTION__); + dev_dbg(dev, "%s - Parity Error detected\n", __func__); } else spin_unlock_irqrestore(&priv->lock, flags); /* process read if there is data other than line status */ - if (tty && (bytes > i)) { - bytes = tty_buffer_request_room(tty, bytes); - for (; i < bytes ; ++i) { - dbg("pushing byte number %d - %d - %c", i, data[i], - data[i]); - tty_insert_flip_char(tty, data[i], tty_flag); - } - tty_flip_buffer_push(port->tty); + if (bytes > i) { + tty_insert_flip_string_fixed_flag(&port->port, data + i, + tty_flag, bytes - i); + tty_flip_buffer_push(&port->port); } spin_lock_irqsave(&priv->lock, flags); @@ -1401,324 +1197,71 @@ static void cypress_read_int_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); continue_read: + tty_kref_put(tty); - /* Continue trying to always read... unless the port has closed. */ + /* Continue trying to always read */ - if (port->open_count > 0 && priv->comm_is_ok) { + if (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, priv->read_urb_interval); + cypress_read_int_callback, port, + priv->read_urb_interval); result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); - if (result) { - dev_err(&urb->dev->dev, "%s - failed resubmitting " - "read urb, error %d\n", __FUNCTION__, - result); + if (result && result != -EPERM) { + dev_err(dev, "%s - failed resubmitting read urb, error %d\n", + __func__, result); cypress_set_dead(port); } } - - return; } /* cypress_read_int_callback */ static void cypress_write_int_callback(struct urb *urb) { - struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial_port *port = urb->context; struct cypress_private *priv = usb_get_serial_port_data(port); - int result; + struct device *dev = &urb->dev->dev; int status = urb->status; - dbg("%s - port %d", __FUNCTION__, port->number); - switch (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__, status); - priv->write_urb_in_use = 0; - return; - case -EPIPE: /* no break needed; clear halt and resubmit */ - if (!priv->comm_is_ok) - break; - usb_clear_halt(port->serial->dev, 0x02); - /* error in the urb, so we have to resubmit it */ - dbg("%s - nonzero write bulk status received: %d", - __FUNCTION__, 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) - 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__, status); - cypress_set_dead(port); - break; + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dev_dbg(dev, "%s - urb shutting down with status: %d\n", + __func__, status); + priv->write_urb_in_use = 0; + return; + case -EPIPE: + /* Cannot call usb_clear_halt while in_interrupt */ + /* FALLTHROUGH */ + default: + dev_err(dev, "%s - unexpected nonzero write status received: %d\n", + __func__, status); + cypress_set_dead(port); + break; } - priv->write_urb_in_use = 0; - + /* send any buffered data */ cypress_send(port); } +module_usb_serial_driver(serial_drivers, id_table_combined); -/***************************************************************************** - * Write buffer functions - buffering code from pl2303 used - *****************************************************************************/ - -/* - * cypress_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ - -static struct cypress_buf *cypress_buf_alloc(unsigned int size) -{ - - struct cypress_buf *cb; - - - if (size == 0) - return NULL; - - cb = kmalloc(sizeof(struct cypress_buf), GFP_KERNEL); - if (cb == NULL) - return NULL; - - cb->buf_buf = kmalloc(size, GFP_KERNEL); - if (cb->buf_buf == NULL) { - kfree(cb); - return NULL; - } - - cb->buf_size = size; - cb->buf_get = cb->buf_put = cb->buf_buf; - - return cb; - -} - - -/* - * cypress_buf_free - * - * Free the buffer and all associated memory. - */ - -static void cypress_buf_free(struct cypress_buf *cb) -{ - if (cb) { - kfree(cb->buf_buf); - kfree(cb); - } -} - - -/* - * cypress_buf_clear - * - * Clear out all data in the circular buffer. - */ - -static void cypress_buf_clear(struct cypress_buf *cb) -{ - if (cb != NULL) - cb->buf_get = cb->buf_put; - /* equivalent to a get of all data available */ -} - - -/* - * cypress_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ - -static unsigned int cypress_buf_data_avail(struct cypress_buf *cb) -{ - if (cb != NULL) - return ((cb->buf_size + cb->buf_put - cb->buf_get) % cb->buf_size); - else - return 0; -} - - -/* - * cypress_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ - -static unsigned int cypress_buf_space_avail(struct cypress_buf *cb) -{ - if (cb != NULL) - return ((cb->buf_size + cb->buf_get - cb->buf_put - 1) % cb->buf_size); - else - return 0; -} - - -/* - * cypress_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 cypress_buf_put(struct cypress_buf *cb, const char *buf, - unsigned int count) -{ - - unsigned int len; - - - if (cb == NULL) - return 0; - - len = cypress_buf_space_avail(cb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = cb->buf_buf + cb->buf_size - cb->buf_put; - if (count > len) { - memcpy(cb->buf_put, buf, len); - memcpy(cb->buf_buf, buf+len, count - len); - cb->buf_put = cb->buf_buf + count - len; - } else { - memcpy(cb->buf_put, buf, count); - if (count < len) - cb->buf_put += count; - else /* count == len */ - cb->buf_put = cb->buf_buf; - } - - return count; - -} - - -/* - * cypress_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 cypress_buf_get(struct cypress_buf *cb, char *buf, - unsigned int count) -{ - - unsigned int len; - - - if (cb == NULL) - return 0; - - len = cypress_buf_data_avail(cb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = cb->buf_buf + cb->buf_size - cb->buf_get; - if (count > len) { - memcpy(buf, cb->buf_get, len); - memcpy(buf+len, cb->buf_buf, count - len); - cb->buf_get = cb->buf_buf + count - len; - } else { - memcpy(buf, cb->buf_get, count); - if (count < len) - cb->buf_get += count; - else /* count == len */ - cb->buf_get = cb->buf_buf; - } - - return count; - -} - -/***************************************************************************** - * Module functions - *****************************************************************************/ - -static int __init cypress_init(void) -{ - int retval; - - dbg("%s", __FUNCTION__); - - retval = usb_serial_register(&cypress_earthmate_device); - if (retval) - goto failed_em_register; - retval = usb_serial_register(&cypress_hidcom_device); - if (retval) - goto failed_hidcom_register; - retval = usb_serial_register(&cypress_ca42v2_device); - if (retval) - goto failed_ca42v2_register; - retval = usb_register(&cypress_driver); - if (retval) - goto failed_usb_register; - - info(DRIVER_DESC " " DRIVER_VERSION); - return 0; - -failed_usb_register: - usb_serial_deregister(&cypress_ca42v2_device); -failed_ca42v2_register: - usb_serial_deregister(&cypress_hidcom_device); -failed_hidcom_register: - usb_serial_deregister(&cypress_earthmate_device); -failed_em_register: - return retval; -} - - -static void __exit cypress_exit (void) -{ - dbg("%s", __FUNCTION__); - - usb_deregister (&cypress_driver); - usb_serial_deregister (&cypress_earthmate_device); - usb_serial_deregister (&cypress_hidcom_device); - usb_serial_deregister (&cypress_ca42v2_device); -} - - -module_init(cypress_init); -module_exit(cypress_exit); - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_VERSION( DRIVER_VERSION ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled or not"); module_param(stats, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(stats, "Enable statistics or not"); module_param(interval, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(interval, "Overrides interrupt interval"); +module_param(unstable_bauds, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(unstable_bauds, "Allow unstable baud rates"); |
