X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fserial%2Fcypress_m8.c;h=6bc1f404e1869c18b32679da0dd93ab8ad9b3aab;hb=a2f44b27303a5353859d77a3e96a1d3f33f56ab7;hp=68067fe117a46ab25d0ce71695643095eea2fdc3;hpb=134734d875a0a48d994ef20b9905209b4b8b6f75;p=linux-2.6.git diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 68067fe11..6bc1f404e 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 #include #include #include @@ -60,11 +59,11 @@ #include #include #include +#include #include #include #include -#include "usb-serial.h" #include "cypress_m8.h" @@ -98,10 +97,16 @@ static struct usb_device_id id_table_cyphidcomrs232 [] = { { } /* Terminating entry */ }; +static 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 [] = { { 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_DAZZLE, PRODUCT_ID_CA42) }, { } /* Terminating entry */ }; @@ -124,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 */ @@ -135,7 +143,7 @@ struct cypress_private { 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 */ - struct termios tmp_termios; /* stores the old termios settings */ + struct ktermios tmp_termios; /* stores the old termios settings */ }; /* write buffer structure */ @@ -149,6 +157,7 @@ struct cypress_buf { /* 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); @@ -156,14 +165,15 @@ static int cypress_write (struct usb_serial_port *port, const unsigned char *b 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 termios * old); +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_read_int_callback (struct urb *urb, struct pt_regs *regs); -static void cypress_write_int_callback (struct urb *urb, struct pt_regs *regs); +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); @@ -235,6 +245,34 @@ static struct usb_serial_driver cypress_hidcom_device = { .write_int_callback = cypress_write_int_callback, }; +static struct usb_serial_driver cypress_ca42v2_device = { + .driver = { + .owner = THIS_MODULE, + .name = "nokiaca42v2", + }, + .description = "Nokia CA-42 V2 Adapter", + .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, + .open = cypress_open, + .close = cypress_close, + .write = cypress_write, + .write_room = cypress_write_room, + .ioctl = cypress_ioctl, + .set_termios = cypress_set_termios, + .tiocmget = cypress_tiocmget, + .tiocmset = cypress_tiocmset, + .chars_in_buffer = cypress_chars_in_buffer, + .throttle = cypress_throttle, + .unthrottle = cypress_unthrottle, + .read_int_callback = cypress_read_int_callback, + .write_int_callback = cypress_write_int_callback, +}; /***************************************************************************** * Cypress serial helper functions @@ -254,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: @@ -286,6 +327,12 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m __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", @@ -325,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; @@ -352,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); @@ -379,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) { @@ -432,14 +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 = kmalloc(sizeof (struct cypress_private), GFP_KERNEL); + priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL); if (!priv) return -ENOMEM; - memset(priv, 0x00, sizeof (struct cypress_private)); + priv->comm_is_ok = !0; spin_lock_init(&priv->lock); priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); if (priv->buf == NULL) { @@ -450,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; } @@ -500,6 +575,25 @@ static int cypress_hidcom_startup (struct usb_serial *serial) } /* cypress_hidcom_startup */ +static int cypress_ca42v2_startup (struct usb_serial *serial) +{ + struct cypress_private *priv; + + dbg("%s", __FUNCTION__); + + if (generic_startup(serial)) { + dbg("%s - Failed setting up port %d", __FUNCTION__, + serial->port[0]->number); + return 1; + } + + priv = usb_get_serial_port_data(serial->port[0]); + priv->chiptype = CT_CA42V2; + + return 0; +} /* cypress_ca42v2_startup */ + + static void cypress_shutdown (struct usb_serial *serial) { struct cypress_private *priv; @@ -527,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); @@ -566,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; @@ -675,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); @@ -748,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); @@ -765,7 +868,7 @@ send: priv->bytes_out += count; /* do not count the line control and size bytes */ spin_unlock_irqrestore(&priv->lock, flags); - schedule_work(&port->work); + usb_serial_port_softint(port); } /* cypress_send */ @@ -846,28 +949,13 @@ static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsi switch (cmd) { case TIOCGSERIAL: - if (copy_to_user((void __user *)arg, port->tty->termios, sizeof(struct termios))) { + 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 termios))) { - 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; - /* these are called when setting baud rate from gpsd */ - case TCGETS: - if (copy_to_user((void __user *)arg, port->tty->termios, sizeof(struct termios))) { - return -EFAULT; - } - return (0); - break; - case TCSETS: - if (copy_from_user(port->tty->termios, (void __user *)arg, sizeof(struct termios))) { + 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 */ @@ -916,7 +1004,7 @@ static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsi static void cypress_set_termios (struct usb_serial_port *port, - struct termios *old_termios) + struct ktermios *old_termios) { struct cypress_private *priv = usb_get_serial_port_data(port); struct tty_struct *tty; @@ -944,6 +1032,10 @@ static void cypress_set_termios (struct usb_serial_port *port, *(tty->termios) = tty_std_termios; tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + } else if (priv->chiptype == CT_CA42V2) { + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | + CLOCAL; } priv->termios_initialized = 1; } @@ -1152,18 +1244,23 @@ 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); + } } } -static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs) +static void cypress_read_int_callback(struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct cypress_private *priv = usb_get_serial_port_data(port); @@ -1178,9 +1275,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; } @@ -1281,25 +1391,27 @@ 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; } /* cypress_read_int_callback */ -static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs) +static void cypress_write_int_callback(struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct cypress_private *priv = usb_get_serial_port_data(port); @@ -1318,20 +1430,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; @@ -1360,7 +1478,7 @@ static struct cypress_buf *cypress_buf_alloc(unsigned int size) if (size == 0) return NULL; - cb = (struct cypress_buf *)kmalloc(sizeof(struct cypress_buf), GFP_KERNEL); + cb = kmalloc(sizeof(struct cypress_buf), GFP_KERNEL); if (cb == NULL) return NULL; @@ -1542,19 +1660,23 @@ static int __init cypress_init(void) 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_deregister(&cypress_driver); -failed_hidcom_register: + usb_serial_deregister(&cypress_ca42v2_device); +failed_ca42v2_register: usb_serial_deregister(&cypress_hidcom_device); -failed_em_register: +failed_hidcom_register: usb_serial_deregister(&cypress_earthmate_device); - +failed_em_register: return retval; } @@ -1566,6 +1688,7 @@ static void __exit cypress_exit (void) usb_deregister (&cypress_driver); usb_serial_deregister (&cypress_earthmate_device); usb_serial_deregister (&cypress_hidcom_device); + usb_serial_deregister (&cypress_ca42v2_device); }