diff options
Diffstat (limited to 'drivers/usb/serial/sierra.c')
-rw-r--r-- | drivers/usb/serial/sierra.c | 371 |
1 files changed, 159 insertions, 212 deletions
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ac1829c6e8f..e7db20343d1 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -86,15 +86,14 @@ static int debug; #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) */ @@ -103,8 +102,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) @@ -197,61 +194,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; } @@ -262,15 +296,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) { @@ -282,30 +317,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; @@ -313,7 +338,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; @@ -344,10 +369,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) @@ -358,46 +383,42 @@ 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; @@ -413,7 +434,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__, @@ -427,24 +448,13 @@ 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 */ @@ -455,7 +465,14 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) sierra_send_setup(port); - return (0); + /* 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) @@ -475,71 +492,21 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) - usb_unlink_urb(portdata->in_urbs[i]); - for (i = 0; i < N_OUT_URB; i++) - usb_unlink_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__); + usb_kill_urb(port->interrupt_in_urb); - 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); - } - - /* 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__); @@ -550,22 +517,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) @@ -576,22 +552,6 @@ 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]; - if (!port) - continue; - portdata = usb_get_serial_port_data(port); - if (!portdata) - continue; - - for (j = 0; j < N_IN_URB; j++) - usb_unlink_urb(portdata->in_urbs[j]); - for (j = 0; j < N_OUT_URB; j++) - usb_unlink_urb(portdata->out_urbs[j]); - } - - /* Now free them */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (!port) @@ -601,25 +561,12 @@ static void sierra_shutdown(struct usb_serial *serial) 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; - } + usb_kill_urb(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; - } - } - } - - /* Now free per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - if (!port) - continue; - kfree(usb_get_serial_port_data(port)); + kfree(portdata); + usb_set_serial_port_data(port, NULL); } } |