X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fusb%2Fserial%2Fsierra.c;h=c137c8962b9f8f0bfbe6797fa5cddafb3e72c801;hb=112225b13cedd53dfd6455038b8843cf004ddec9;hp=ecedd833818d960ceed8751856351394893d8847;hpb=67c2b7d9d224232ee730b9c9444abed824b62e7a;p=linux-2.6-omap-h63xx.git diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ecedd833818..c137c8962b9 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -1,7 +1,7 @@ /* USB Driver for Sierra Wireless - Copyright (C) 2006 Kevin Lloyd + Copyright (C) 2006, 2007 Kevin Lloyd IMPORTANT DISCLAIMER: This driver is not commercially supported by Sierra Wireless. Use at your own risk. @@ -12,10 +12,9 @@ Portions based on the option driver by Matthias Urlichs Whom based his on the Keyspan driver by Hugh Blemings - */ -#define DRIVER_VERSION "v.1.0.6" +#define DRIVER_VERSION "v.1.2.4b" #define DRIVER_AUTHOR "Kevin Lloyd " #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems" @@ -28,6 +27,71 @@ #include #include +#define SWIMS_USB_REQUEST_SetMode 0x0B +#define SWIMS_USB_REQUEST_TYPE_SetMode 0x40 +#define SWIMS_USB_INDEX_SetMode 0x0000 +#define SWIMS_SET_MODE_Modem 0x0001 + +/* per port private data */ +#define N_IN_URB 4 +#define N_OUT_URB 4 +#define IN_BUFLEN 4096 + +static int debug; + +enum devicetype { + DEVICE_3_PORT = 0, + DEVICE_1_PORT = 1, + DEVICE_INSTALLER = 2, +}; + +int sierra_set_power_state(struct usb_device *udev, __u16 swiState) +{ + int result; + dev_dbg(&udev->dev, "%s", "SET POWER STATE"); + result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x00, /* __u8 request */ + 0x40, /* __u8 request type */ + swiState, /* __u16 value */ + 0, /* __u16 index */ + NULL, /* void *data */ + 0, /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + return result; +} + +int sierra_set_ms_mode(struct usb_device *udev, __u16 eSocMode) +{ + int result; + dev_dbg(&udev->dev, "%s", "DEVICE MODE SWITCH"); + result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SWIMS_USB_REQUEST_SetMode, /* __u8 request */ + SWIMS_USB_REQUEST_TYPE_SetMode, /* __u8 request type */ + eSocMode, /* __u16 value */ + SWIMS_USB_INDEX_SetMode, /* __u16 index */ + NULL, /* void *data */ + 0, /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + return result; +} + +int sierra_probe(struct usb_interface *iface, const struct usb_device_id *id) +{ + int result; + struct usb_device *udev; + + udev = usb_get_dev(interface_to_usbdev(iface)); + + /* Check if in installer mode */ + if (id->driver_info == DEVICE_INSTALLER) { + dev_dbg(&udev->dev, "%s", "FOUND DEVICE(SW)\n"); + result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); + /*We do not want to bind to the device when in installer mode*/ + return -EIO; + } + + return usb_serial_probe(iface, id); +} static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ @@ -35,6 +99,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ + { USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless AirCard 595U */ { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ @@ -42,8 +107,10 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 */ { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ - { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */ - { USB_DEVICE(0x0F3D, 0x0112) }, /* AirPrime/Sierra PC 5220 */ + { USB_DEVICE(0x1199, 0x0112), .driver_info = DEVICE_1_PORT }, /* Sierra Wireless AirCard 580 */ + { USB_DEVICE(0x0F3D, 0x0112), .driver_info = DEVICE_1_PORT }, /* Airprime/Sierra PC 5220 */ + + { USB_DEVICE(0x1199, 0x0FFF), .driver_info = DEVICE_INSTALLER}, { } }; MODULE_DEVICE_TABLE(usb, id_table); @@ -55,12 +122,16 @@ static struct usb_device_id id_table_1port [] = { }; static struct usb_device_id id_table_3port [] = { + /* CDMA Devices */ { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ + { USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless AirCard 595U */ { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ + + /* GSM/UMTS Devices */ { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ @@ -71,28 +142,20 @@ static struct usb_device_id id_table_3port [] = { static struct usb_driver sierra_driver = { .name = "sierra", - .probe = usb_serial_probe, + .probe = sierra_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, .no_dynamic_id = 1, }; -static int debug; - -/* per port private data */ -#define N_IN_URB 4 -#define N_OUT_URB 4 -#define IN_BUFLEN 4096 -#define OUT_BUFLEN 128 - struct sierra_port_private { + spinlock_t lock; /* lock the structure */ + int outstanding_urbs; /* number of out urbs in flight */ + /* Input endpoints and buffer for this port */ struct urb *in_urbs[N_IN_URB]; char in_buffer[N_IN_URB][IN_BUFLEN]; - /* Output endpoints and buffer for this port */ - struct urb *out_urbs[N_OUT_URB]; - char out_buffer[N_OUT_URB][OUT_BUFLEN]; /* Settings for the port */ int rts_state; /* Handshaking pins (outputs) */ @@ -101,8 +164,6 @@ struct sierra_port_private { int dsr_state; int dcd_state; int ri_state; - - unsigned long tx_start_time[N_OUT_URB]; }; static int sierra_send_setup(struct usb_serial_port *port) @@ -195,61 +256,98 @@ static int sierra_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } +static void sierra_outdat_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct sierra_port_private *portdata = usb_get_serial_port_data(port); + int status = urb->status; + 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 (status) + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); + + spin_lock_irqsave(&portdata->lock, flags); + --portdata->outstanding_urbs; + spin_unlock_irqrestore(&portdata->lock, flags); + + usb_serial_port_softint(port); +} + /* Write */ static int sierra_write(struct usb_serial_port *port, const unsigned char *buf, int count) { - struct sierra_port_private *portdata; - int i; - int left, todo; - struct urb *this_urb = NULL; /* spurious */ - int err; + struct sierra_port_private *portdata = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + unsigned long flags; + unsigned char *buffer; + struct urb *urb; + int status; portdata = usb_get_serial_port_data(port); dbg("%s: write (%d chars)", __FUNCTION__, count); - i = 0; - left = count; - for (i=0; left > 0 && i < N_OUT_URB; i++) { - todo = left; - if (todo > OUT_BUFLEN) - todo = OUT_BUFLEN; - - this_urb = portdata->out_urbs[i]; - if (this_urb->status == -EINPROGRESS) { - if (time_before(jiffies, - portdata->tx_start_time[i] + 10 * HZ)) - continue; - usb_unlink_urb(this_urb); - continue; - } - if (this_urb->status != 0) - dbg("usb_write %p failed (err=%d)", - this_urb, this_urb->status); - - dbg("%s: endpoint %d buf %d", __FUNCTION__, - usb_pipeendpoint(this_urb->pipe), i); - - /* send the data */ - memcpy (this_urb->transfer_buffer, buf, todo); - this_urb->transfer_buffer_length = todo; - - this_urb->dev = port->serial->dev; - err = usb_submit_urb(this_urb, GFP_ATOMIC); - if (err) { - dbg("usb_submit_urb %p (write bulk) failed " - "(%d, has %d)", this_urb, - err, this_urb->status); - continue; - } - portdata->tx_start_time[i] = jiffies; - buf += todo; - left -= todo; + spin_lock_irqsave(&portdata->lock, flags); + if (portdata->outstanding_urbs > N_OUT_URB) { + spin_unlock_irqrestore(&portdata->lock, flags); + dbg("%s - write limit hit\n", __FUNCTION__); + return 0; + } + portdata->outstanding_urbs++; + spin_unlock_irqrestore(&portdata->lock, flags); + + buffer = kmalloc(count, GFP_ATOMIC); + if (!buffer) { + dev_err(&port->dev, "out of memory\n"); + count = -ENOMEM; + goto error_no_buffer; + } + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(&port->dev, "no more free urbs\n"); + count = -ENOMEM; + goto error_no_urb; + } + + 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, sierra_outdat_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; + goto error; } - count -= left; - dbg("%s: wrote (did %d)", __FUNCTION__, count); + /* 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; +error: + usb_free_urb(urb); +error_no_urb: + kfree(buffer); +error_no_buffer: + spin_lock_irqsave(&portdata->lock, flags); + --portdata->outstanding_urbs; + spin_unlock_irqrestore(&portdata->lock, flags); return count; } @@ -260,15 +358,16 @@ static void sierra_indat_callback(struct urb *urb) struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; + int status = urb->status; dbg("%s: %p", __FUNCTION__, urb); endpoint = usb_pipeendpoint(urb->pipe); port = (struct usb_serial_port *) urb->context; - if (urb->status) { + if (status) { dbg("%s: nonzero status: %d on endpoint %02x.", - __FUNCTION__, urb->status, endpoint); + __FUNCTION__, status, endpoint); } else { tty = port->tty; if (urb->actual_length) { @@ -280,30 +379,20 @@ static void sierra_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && urb->status != -ESHUTDOWN) { + if (port->open_count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) - printk(KERN_ERR "%s: resubmit read urb failed. " - "(%d)", __FUNCTION__, err); + dev_err(&port->dev, "resubmit read urb failed." + "(%d)", err); } } return; } -static void sierra_outdat_callback(struct urb *urb) -{ - struct usb_serial_port *port; - - dbg("%s", __FUNCTION__); - - port = (struct usb_serial_port *) urb->context; - - usb_serial_port_softint(port); -} - static void sierra_instat_callback(struct urb *urb) { int err; + int status = urb->status; struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -311,7 +400,7 @@ static void sierra_instat_callback(struct urb *urb) dbg("%s", __FUNCTION__); dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata); - if (urb->status == 0) { + if (status == 0) { struct usb_ctrlrequest *req_pkt = (struct usb_ctrlrequest *)urb->transfer_buffer; @@ -342,10 +431,10 @@ static void sierra_instat_callback(struct urb *urb) req_pkt->bRequestType,req_pkt->bRequest); } } else - dbg("%s: error %d", __FUNCTION__, urb->status); + dbg("%s: error %d", __FUNCTION__, status); /* Resubmit urb so we continue receiving IRQ data */ - if (urb->status != -ESHUTDOWN) { + if (status != -ESHUTDOWN) { urb->dev = serial->dev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) @@ -356,49 +445,44 @@ static void sierra_instat_callback(struct urb *urb) static int sierra_write_room(struct usb_serial_port *port) { - struct sierra_port_private *portdata; - int i; - int data_len = 0; - struct urb *this_urb; + struct sierra_port_private *portdata = usb_get_serial_port_data(port); + unsigned long flags; - portdata = usb_get_serial_port_data(port); + dbg("%s - port %d", __FUNCTION__, port->number); - for (i=0; i < N_OUT_URB; i++) { - this_urb = portdata->out_urbs[i]; - if (this_urb && this_urb->status != -EINPROGRESS) - data_len += OUT_BUFLEN; + /* try to give a good number back based on if we have any free urbs at + * this point in time */ + spin_lock_irqsave(&portdata->lock, flags); + if (portdata->outstanding_urbs > N_OUT_URB * 2 / 3) { + spin_unlock_irqrestore(&portdata->lock, flags); + dbg("%s - write limit hit\n", __FUNCTION__); + return 0; } + spin_unlock_irqrestore(&portdata->lock, flags); - dbg("%s: %d", __FUNCTION__, data_len); - return data_len; + return 2048; } static int sierra_chars_in_buffer(struct usb_serial_port *port) { - struct sierra_port_private *portdata; - int i; - int data_len = 0; - struct urb *this_urb; - - portdata = usb_get_serial_port_data(port); - - for (i=0; i < N_OUT_URB; i++) { - this_urb = portdata->out_urbs[i]; - if (this_urb && this_urb->status == -EINPROGRESS) - data_len += this_urb->transfer_buffer_length; - } - dbg("%s: %d", __FUNCTION__, data_len); - return data_len; + 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 as we can't see the backend here, so just + * tell the tty layer that everything is flushed. + */ + return 0; } static int sierra_open(struct usb_serial_port *port, struct file *filp) { struct sierra_port_private *portdata; struct usb_serial *serial = port->serial; - int i, err; + int i; struct urb *urb; int result; - __u16 set_mode_dzero = 0x0000; portdata = usb_get_serial_port_data(port); @@ -411,7 +495,7 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) /* Reset low level data toggle and start reading from endpoints */ for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; - if (! urb) + if (!urb) continue; if (urb->dev != serial->dev) { dbg("%s: dev %p != %p", __FUNCTION__, @@ -425,41 +509,25 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) */ usb_clear_halt(urb->dev, urb->pipe); - err = usb_submit_urb(urb, GFP_KERNEL); - if (err) { - dbg("%s: submit urb %d failed (%d) %d", - __FUNCTION__, i, err, - urb->transfer_buffer_length); + result = usb_submit_urb(urb, GFP_KERNEL); + if (result) { + dev_err(&port->dev, "submit urb %d failed (%d) %d", + i, result, urb->transfer_buffer_length); } } - /* Reset low level data toggle on out endpoints */ - for (i = 0; i < N_OUT_URB; i++) { - urb = portdata->out_urbs[i]; - if (! urb) - continue; - urb->dev = serial->dev; - /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), 0); */ - } - port->tty->low_latency = 1; - /* set mode to D0 */ - result = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x00, 0x40, set_mode_dzero, 0, NULL, - 0, USB_CTRL_SET_TIMEOUT); - sierra_send_setup(port); - return (0); -} - -static inline void stop_urb(struct urb *urb) -{ - if (urb && urb->status == -EINPROGRESS) - usb_kill_urb(urb); + /* start up the interrupt endpoint if we have one */ + if (port->interrupt_in_urb) { + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, "submit irq_in urb failed %d", + result); + } + return 0; } static void sierra_close(struct usb_serial_port *port, struct file *filp) @@ -479,74 +547,27 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) - stop_urb(portdata->in_urbs[i]); - for (i = 0; i < N_OUT_URB; i++) - stop_urb(portdata->out_urbs[i]); + usb_kill_urb(portdata->in_urbs[i]); } - port->tty = NULL; -} - -/* Helper functions used by sierra_setup_urbs */ -static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint, - int dir, void *ctx, char *buf, int len, - usb_complete_t callback) -{ - struct urb *urb; - - if (endpoint == -1) - return NULL; /* endpoint not needed */ - - urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ - if (urb == NULL) { - dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint); - return NULL; - } - - /* Fill URB using supplied data. */ - usb_fill_bulk_urb(urb, serial->dev, - usb_sndbulkpipe(serial->dev, endpoint) | dir, - buf, len, callback, ctx); - - return urb; -} - -/* Setup urbs */ -static void sierra_setup_urbs(struct usb_serial *serial) -{ - int i,j; - struct usb_serial_port *port; - struct sierra_port_private *portdata; - - dbg("%s", __FUNCTION__); - - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - portdata = usb_get_serial_port_data(port); - /* Do indat endpoints first */ - for (j = 0; j < N_IN_URB; ++j) { - portdata->in_urbs[j] = sierra_setup_urb (serial, - port->bulk_in_endpointAddress, USB_DIR_IN, port, - portdata->in_buffer[j], IN_BUFLEN, sierra_indat_callback); - } + usb_kill_urb(port->interrupt_in_urb); - /* outdat endpoints */ - for (j = 0; j < N_OUT_URB; ++j) { - portdata->out_urbs[j] = sierra_setup_urb (serial, - port->bulk_out_endpointAddress, USB_DIR_OUT, port, - portdata->out_buffer[j], OUT_BUFLEN, sierra_outdat_callback); - } - } + port->tty = NULL; } static int sierra_startup(struct usb_serial *serial) { - int i, err; struct usb_serial_port *port; struct sierra_port_private *portdata; + struct urb *urb; + int i; + int j; dbg("%s", __FUNCTION__); + /*Set Device mode to D0 */ + sierra_set_power_state(serial->dev, 0x0000); + /* Now setup per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; @@ -554,22 +575,31 @@ static int sierra_startup(struct usb_serial *serial) if (!portdata) { dbg("%s: kmalloc for sierra_port_private (%d) failed!.", __FUNCTION__, i); - return (1); + return -ENOMEM; } + spin_lock_init(&portdata->lock); usb_set_serial_port_data(port, portdata); - if (! port->interrupt_in_urb) - continue; - err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (err) - dbg("%s: submit irq_in urb failed %d", - __FUNCTION__, err); + /* initialize the in urbs */ + for (j = 0; j < N_IN_URB; ++j) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) { + dbg("%s: alloc for in port failed.", + __FUNCTION__); + continue; + } + /* Fill URB using supplied data. */ + usb_fill_bulk_urb(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), + portdata->in_buffer[j], IN_BUFLEN, + sierra_indat_callback, port); + portdata->in_urbs[j] = urb; + } } - sierra_setup_urbs(serial); - - return (0); + return 0; } static void sierra_shutdown(struct usb_serial *serial) @@ -580,39 +610,21 @@ static void sierra_shutdown(struct usb_serial *serial) dbg("%s", __FUNCTION__); - /* Stop reading/writing urbs */ - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - portdata = usb_get_serial_port_data(port); - for (j = 0; j < N_IN_URB; j++) - stop_urb(portdata->in_urbs[j]); - for (j = 0; j < N_OUT_URB; j++) - stop_urb(portdata->out_urbs[j]); - } - - /* Now free them */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; + if (!port) + continue; portdata = usb_get_serial_port_data(port); + if (!portdata) + continue; for (j = 0; j < N_IN_URB; j++) { - if (portdata->in_urbs[j]) { - usb_free_urb(portdata->in_urbs[j]); - portdata->in_urbs[j] = NULL; - } - } - for (j = 0; j < N_OUT_URB; j++) { - if (portdata->out_urbs[j]) { - usb_free_urb(portdata->out_urbs[j]); - portdata->out_urbs[j] = NULL; - } + usb_kill_urb(portdata->in_urbs[j]); + usb_free_urb(portdata->in_urbs[j]); + portdata->in_urbs[j] = NULL; } - } - - /* Now free per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - kfree(usb_get_serial_port_data(port)); + kfree(portdata); + usb_set_serial_port_data(port, NULL); } }