diff options
Diffstat (limited to 'drivers/usb/serial/sierra.c')
-rw-r--r-- | drivers/usb/serial/sierra.c | 528 |
1 files changed, 319 insertions, 209 deletions
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 913225c61610..032f7aeb40a4 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -1,7 +1,10 @@ /* USB Driver for Sierra Wireless - Copyright (C) 2006, 2007, 2008 Kevin Lloyd <klloyd@sierrawireless.com> + Copyright (C) 2006, 2007, 2008 Kevin Lloyd <klloyd@sierrawireless.com>, + + Copyright (C) 2008, 2009 Elina Pasheva, Matthew Safar, Rory Filer + <linux@sierrawireless.com> IMPORTANT DISCLAIMER: This driver is not commercially supported by Sierra Wireless. Use at your own risk. @@ -14,8 +17,8 @@ Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org> */ -#define DRIVER_VERSION "v.1.3.3" -#define DRIVER_AUTHOR "Kevin Lloyd <klloyd@sierrawireless.com>" +#define DRIVER_VERSION "v.1.3.7" +#define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer" #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems" #include <linux/kernel.h> @@ -26,23 +29,32 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <linux/usb/ch9.h> #define SWIMS_USB_REQUEST_SetPower 0x00 #define SWIMS_USB_REQUEST_SetNmea 0x07 -/* per port private data */ -#define N_IN_URB 4 -#define N_OUT_URB 4 +#define N_IN_URB 8 +#define N_OUT_URB 64 #define IN_BUFLEN 4096 +#define MAX_TRANSFER (PAGE_SIZE - 512) +/* MAX_TRANSFER is chosen so that the VM is not stressed by + allocations > PAGE_SIZE and the number of packets in a page + is an integer 512 is the largest possible packet on EHCI */ + static int debug; static int nmea; +/* Used in interface blacklisting */ +struct sierra_iface_info { + const u32 infolen; /* number of interface numbers on blacklist */ + const u8 *ifaceinfo; /* pointer to the array holding the numbers */ +}; + static int sierra_set_power_state(struct usb_device *udev, __u16 swiState) { int result; - dev_dbg(&udev->dev, "%s", __func__); + dev_dbg(&udev->dev, "%s\n", __func__); result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), SWIMS_USB_REQUEST_SetPower, /* __u8 request */ USB_TYPE_VENDOR, /* __u8 request type */ @@ -57,7 +69,7 @@ static int sierra_set_power_state(struct usb_device *udev, __u16 swiState) static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable) { int result; - dev_dbg(&udev->dev, "%s", __func__); + dev_dbg(&udev->dev, "%s\n", __func__); result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), SWIMS_USB_REQUEST_SetNmea, /* __u8 request */ USB_TYPE_VENDOR, /* __u8 request type */ @@ -71,18 +83,39 @@ static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable) static int sierra_calc_num_ports(struct usb_serial *serial) { - int result; - int *num_ports = usb_get_serial_data(serial); - dev_dbg(&serial->dev->dev, "%s", __func__); + int num_ports = 0; + u8 ifnum, numendpoints; - result = *num_ports; + dev_dbg(&serial->dev->dev, "%s\n", __func__); - if (result) { - kfree(num_ports); - usb_set_serial_data(serial, NULL); - } + ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; + numendpoints = serial->interface->cur_altsetting->desc.bNumEndpoints; - return result; + /* Dummy interface present on some SKUs should be ignored */ + if (ifnum == 0x99) + num_ports = 0; + else if (numendpoints <= 3) + num_ports = 1; + else + num_ports = (numendpoints-1)/2; + return num_ports; +} + +static int is_blacklisted(const u8 ifnum, + const struct sierra_iface_info *blacklist) +{ + const u8 *info; + int i; + + if (blacklist) { + info = blacklist->ifaceinfo; + + for (i = 0; i < blacklist->infolen; i++) { + if (info[i] == ifnum) + return 1; + } + } + return 0; } static int sierra_calc_interface(struct usb_serial *serial) @@ -90,7 +123,7 @@ static int sierra_calc_interface(struct usb_serial *serial) int interface; struct usb_interface *p_interface; struct usb_host_interface *p_host_interface; - dev_dbg(&serial->dev->dev, "%s", __func__); + dev_dbg(&serial->dev->dev, "%s\n", __func__); /* Get the interface structure pointer from the serial struct */ p_interface = serial->interface; @@ -111,23 +144,12 @@ static int sierra_probe(struct usb_serial *serial, { int result = 0; struct usb_device *udev; - int *num_ports; u8 ifnum; - u8 numendpoints; - - dev_dbg(&serial->dev->dev, "%s", __func__); - - num_ports = kmalloc(sizeof(*num_ports), GFP_KERNEL); - if (!num_ports) - return -ENOMEM; - ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; - numendpoints = serial->interface->cur_altsetting->desc.bNumEndpoints; udev = serial->dev; + dev_dbg(&udev->dev, "%s\n", __func__); - /* Figure out the interface number from the serial structure */ ifnum = sierra_calc_interface(serial); - /* * If this interface supports more than 1 alternate * select the 2nd one @@ -139,23 +161,25 @@ static int sierra_probe(struct usb_serial *serial, usb_set_interface(udev, ifnum, 1); } - /* Dummy interface present on some SKUs should be ignored */ - if (ifnum == 0x99) - *num_ports = 0; - else if (numendpoints <= 3) - *num_ports = 1; - else - *num_ports = (numendpoints-1)/2; + /* ifnum could have changed - by calling usb_set_interface */ + ifnum = sierra_calc_interface(serial); - /* - * save off our num_ports info so that we can use it in the - * calc_num_ports callback - */ - usb_set_serial_data(serial, (void *)num_ports); + if (is_blacklisted(ifnum, + (struct sierra_iface_info *)id->driver_info)) { + dev_dbg(&serial->dev->dev, + "Ignoring blacklisted interface #%d\n", ifnum); + return -ENODEV; + } return result; } +static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 }; +static const struct sierra_iface_info direct_ip_interface_blacklist = { + .infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces), + .ifaceinfo = direct_ip_non_serial_ifaces, +}; + static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ @@ -188,9 +212,11 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781 */ { USB_DEVICE(0x1199, 0x683A) }, /* Sierra Wireless MC8785 */ { USB_DEVICE(0x1199, 0x683B) }, /* Sierra Wireless MC8785 Composite */ - { USB_DEVICE(0x1199, 0x683C) }, /* Sierra Wireless MC8790 */ - { USB_DEVICE(0x1199, 0x683D) }, /* Sierra Wireless MC8790 */ - { USB_DEVICE(0x1199, 0x683E) }, /* Sierra Wireless MC8790 */ + /* Sierra Wireless MC8790, MC8791, MC8792 Composite */ + { USB_DEVICE(0x1199, 0x683C) }, + { USB_DEVICE(0x1199, 0x683D) }, /* Sierra Wireless MC8791 Composite */ + /* Sierra Wireless MC8790, MC8791, MC8792 */ + { USB_DEVICE(0x1199, 0x683E) }, { USB_DEVICE(0x1199, 0x6850) }, /* Sierra Wireless AirCard 880 */ { USB_DEVICE(0x1199, 0x6851) }, /* Sierra Wireless AirCard 881 */ { USB_DEVICE(0x1199, 0x6852) }, /* Sierra Wireless AirCard 880 E */ @@ -211,6 +237,10 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */ { USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */ + { USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */ + .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist + }, + { } }; MODULE_DEVICE_TABLE(usb, id_table); @@ -229,7 +259,6 @@ struct sierra_port_private { /* Input endpoints and buffers for this port */ struct urb *in_urbs[N_IN_URB]; - char *in_buffer[N_IN_URB]; /* Settings for the port */ int rts_state; /* Handshaking pins (outputs) */ @@ -240,66 +269,59 @@ struct sierra_port_private { int ri_state; }; -static int sierra_send_setup(struct tty_struct *tty, - struct usb_serial_port *port) +static int sierra_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; __u16 interface = 0; + int val = 0; - dev_dbg(&port->dev, "%s", __func__); + dev_dbg(&port->dev, "%s\n", __func__); portdata = usb_get_serial_port_data(port); - if (tty) { - int val = 0; - if (portdata->dtr_state) - val |= 0x01; - if (portdata->rts_state) - val |= 0x02; - - /* If composite device then properly report interface */ - if (serial->num_ports == 1) { - interface = sierra_calc_interface(serial); - - /* Control message is sent only to interfaces with - * interrupt_in endpoints - */ - if (port->interrupt_in_urb) { - /* send control message */ - return usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x22, 0x21, val, interface, - NULL, 0, USB_CTRL_SET_TIMEOUT); - } - } - - /* Otherwise the need to do non-composite mapping */ - else { - if (port->bulk_out_endpointAddress == 2) - interface = 0; - else if (port->bulk_out_endpointAddress == 4) - interface = 1; - else if (port->bulk_out_endpointAddress == 5) - interface = 2; + if (portdata->dtr_state) + val |= 0x01; + if (portdata->rts_state) + val |= 0x02; + /* If composite device then properly report interface */ + if (serial->num_ports == 1) { + interface = sierra_calc_interface(serial); + /* Control message is sent only to interfaces with + * interrupt_in endpoints + */ + if (port->interrupt_in_urb) { + /* send control message */ return usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 0x22, 0x21, val, interface, NULL, 0, USB_CTRL_SET_TIMEOUT); - } } + /* Otherwise the need to do non-composite mapping */ + else { + if (port->bulk_out_endpointAddress == 2) + interface = 0; + else if (port->bulk_out_endpointAddress == 4) + interface = 1; + else if (port->bulk_out_endpointAddress == 5) + interface = 2; + return usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 0x22, 0x21, val, interface, + NULL, 0, USB_CTRL_SET_TIMEOUT); + } return 0; } static void sierra_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - dev_dbg(&port->dev, "%s", __func__); + dev_dbg(&port->dev, "%s\n", __func__); tty_termios_copy_hw(tty->termios, old_termios); - sierra_send_setup(tty, port); + sierra_send_setup(port); } static int sierra_tiocmget(struct tty_struct *tty, struct file *file) @@ -308,7 +330,7 @@ static int sierra_tiocmget(struct tty_struct *tty, struct file *file) unsigned int value; struct sierra_port_private *portdata; - dev_dbg(&port->dev, "%s", __func__); + dev_dbg(&port->dev, "%s\n", __func__); portdata = usb_get_serial_port_data(port); value = ((portdata->rts_state) ? TIOCM_RTS : 0) | @@ -338,7 +360,18 @@ static int sierra_tiocmset(struct tty_struct *tty, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return sierra_send_setup(tty, port); + return sierra_send_setup(port); +} + +static void sierra_release_urb(struct urb *urb) +{ + struct usb_serial_port *port; + if (urb) { + port = urb->context; + dev_dbg(&port->dev, "%s: %p\n", __func__, urb); + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } } static void sierra_outdat_callback(struct urb *urb) @@ -348,14 +381,14 @@ static void sierra_outdat_callback(struct urb *urb) int status = urb->status; unsigned long flags; - dev_dbg(&port->dev, "%s - port %d", __func__, port->number); + dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number); /* free up the transfer buffer, as usb_free_urb() does not do this */ kfree(urb->transfer_buffer); if (status) dev_dbg(&port->dev, "%s - nonzero write bulk status " - "received: %d", __func__, status); + "received: %d\n", __func__, status); spin_lock_irqsave(&portdata->lock, flags); --portdata->outstanding_urbs; @@ -373,50 +406,61 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, unsigned long flags; unsigned char *buffer; struct urb *urb; - int status; + size_t writesize = min((size_t)count, (size_t)MAX_TRANSFER); + int retval = 0; + + /* verify that we actually have some data to write */ + if (count == 0) + return 0; portdata = usb_get_serial_port_data(port); - dev_dbg(&port->dev, "%s: write (%d chars)", __func__, count); + dev_dbg(&port->dev, "%s: write (%zd bytes)\n", __func__, writesize); spin_lock_irqsave(&portdata->lock, flags); + dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__, + portdata->outstanding_urbs); if (portdata->outstanding_urbs > N_OUT_URB) { spin_unlock_irqrestore(&portdata->lock, flags); dev_dbg(&port->dev, "%s - write limit hit\n", __func__); return 0; } portdata->outstanding_urbs++; + dev_dbg(&port->dev, "%s - 1, outstanding_urbs: %d\n", __func__, + portdata->outstanding_urbs); spin_unlock_irqrestore(&portdata->lock, flags); - buffer = kmalloc(count, GFP_ATOMIC); + buffer = kmalloc(writesize, GFP_ATOMIC); if (!buffer) { dev_err(&port->dev, "out of memory\n"); - count = -ENOMEM; + retval = -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; + retval = -ENOMEM; goto error_no_urb; } - memcpy(buffer, buf, count); + memcpy(buffer, buf, writesize); - usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); + usb_serial_debug_data(debug, &port->dev, __func__, writesize, buffer); usb_fill_bulk_urb(urb, serial->dev, usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), - buffer, count, sierra_outdat_callback, port); + buffer, writesize, sierra_outdat_callback, port); + + /* Handle the need to send a zero length packet */ + urb->transfer_flags |= URB_ZERO_PACKET; /* send it down the pipe */ - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) { dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " - "with status = %d\n", __func__, status); - count = status; + "with status = %d\n", __func__, retval); goto error; } @@ -424,7 +468,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, * really free it when it is finished with it */ usb_free_urb(urb); - return count; + return writesize; error: usb_free_urb(urb); error_no_urb: @@ -432,8 +476,10 @@ error_no_urb: error_no_buffer: spin_lock_irqsave(&portdata->lock, flags); --portdata->outstanding_urbs; + dev_dbg(&port->dev, "%s - 2. outstanding_urbs: %d\n", __func__, + portdata->outstanding_urbs); spin_unlock_irqrestore(&portdata->lock, flags); - return count; + return retval; } static void sierra_indat_callback(struct urb *urb) @@ -445,33 +491,39 @@ static void sierra_indat_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; int status = urb->status; - dbg("%s: %p", __func__, urb); - endpoint = usb_pipeendpoint(urb->pipe); - port = urb->context; + port = urb->context; + + dev_dbg(&port->dev, "%s: %p\n", __func__, urb); if (status) { dev_dbg(&port->dev, "%s: nonzero status: %d on" - " endpoint %02x.", __func__, status, endpoint); + " endpoint %02x\n", __func__, status, endpoint); } else { if (urb->actual_length) { tty = tty_port_tty_get(&port->port); + tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); + tty_kref_put(tty); - } else + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); + } else { dev_dbg(&port->dev, "%s: empty read urb" - " received", __func__); - - /* Resubmit urb so we continue receiving */ - if (port->port.count && status != -ESHUTDOWN) { - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err) - dev_err(&port->dev, "resubmit read urb failed." - "(%d)\n", err); + " received\n", __func__); } } + + /* Resubmit urb so we continue receiving */ + if (port->port.count && status != -ESHUTDOWN && status != -EPERM) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) + dev_err(&port->dev, "resubmit read urb failed." + "(%d)\n", err); + } + return; } @@ -483,8 +535,7 @@ static void sierra_instat_callback(struct urb *urb) struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; - dev_dbg(&port->dev, "%s", __func__); - dev_dbg(&port->dev, "%s: urb %p port %p has data %p", __func__, + dev_dbg(&port->dev, "%s: urb %p port %p has data %p\n", __func__, urb, port, portdata); if (status == 0) { @@ -504,7 +555,7 @@ static void sierra_instat_callback(struct urb *urb) sizeof(struct usb_ctrlrequest)); struct tty_struct *tty; - dev_dbg(&port->dev, "%s: signal x%x", __func__, + dev_dbg(&port->dev, "%s: signal x%x\n", __func__, signals); old_dcd_state = portdata->dcd_state; @@ -519,20 +570,20 @@ static void sierra_instat_callback(struct urb *urb) tty_hangup(tty); tty_kref_put(tty); } else { - dev_dbg(&port->dev, "%s: type %x req %x", + dev_dbg(&port->dev, "%s: type %x req %x\n", __func__, req_pkt->bRequestType, req_pkt->bRequest); } } else - dev_dbg(&port->dev, "%s: error %d", __func__, status); + dev_dbg(&port->dev, "%s: error %d\n", __func__, status); /* Resubmit urb so we continue receiving IRQ data */ - if (status != -ESHUTDOWN) { + if (port->port.count && status != -ESHUTDOWN && status != -ENOENT) { urb->dev = serial->dev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) - dev_dbg(&port->dev, "%s: resubmit intr urb " - "failed. (%d)", __func__, err); + dev_err(&port->dev, "%s: resubmit intr urb " + "failed. (%d)\n", __func__, err); } } @@ -542,7 +593,7 @@ static int sierra_write_room(struct tty_struct *tty) struct sierra_port_private *portdata = usb_get_serial_port_data(port); unsigned long flags; - dev_dbg(&port->dev, "%s - port %d", __func__, port->number); + dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number); /* try to give a good number back based on if we have any free urbs at * this point in time */ @@ -557,67 +608,99 @@ static int sierra_write_room(struct tty_struct *tty) return 2048; } -static int sierra_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void sierra_stop_rx_urbs(struct usb_serial_port *port) { - struct sierra_port_private *portdata; - struct usb_serial *serial = port->serial; int i; - struct urb *urb; - int result; + struct sierra_port_private *portdata = usb_get_serial_port_data(port); - portdata = usb_get_serial_port_data(port); + for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) + usb_kill_urb(portdata->in_urbs[i]); - dev_dbg(&port->dev, "%s", __func__); + usb_kill_urb(port->interrupt_in_urb); +} - /* Set some sane defaults */ - portdata->rts_state = 1; - portdata->dtr_state = 1; +static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags) +{ + int ok_cnt; + int err = -EINVAL; + int i; + struct urb *urb; + struct sierra_port_private *portdata = usb_get_serial_port_data(port); - /* Reset low level data toggle and start reading from endpoints */ - for (i = 0; i < N_IN_URB; i++) { + ok_cnt = 0; + for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) { urb = portdata->in_urbs[i]; if (!urb) continue; - if (urb->dev != serial->dev) { - dev_dbg(&port->dev, "%s: dev %p != %p", - __func__, urb->dev, serial->dev); - continue; + err = usb_submit_urb(urb, mem_flags); + if (err) { + dev_err(&port->dev, "%s: submit urb failed: %d\n", + __func__, err); + } else { + ok_cnt++; } + } - /* - * make sure endpoint data toggle is synchronized with the - * device - */ - usb_clear_halt(urb->dev, urb->pipe); - - result = usb_submit_urb(urb, GFP_KERNEL); - if (result) { - dev_err(&port->dev, "submit urb %d failed (%d) %d\n", - i, result, urb->transfer_buffer_length); + if (ok_cnt && port->interrupt_in_urb) { + err = usb_submit_urb(port->interrupt_in_urb, mem_flags); + if (err) { + dev_err(&port->dev, "%s: submit intr urb failed: %d\n", + __func__, err); } } - sierra_send_setup(tty, port); + if (ok_cnt > 0) /* at least one rx urb submitted */ + return 0; + else + return err; +} - /* 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\n", - result); +static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint, + int dir, void *ctx, int len, + gfp_t mem_flags, + usb_complete_t callback) +{ + struct urb *urb; + u8 *buf; + + if (endpoint == -1) + return NULL; + + urb = usb_alloc_urb(0, mem_flags); + if (urb == NULL) { + dev_dbg(&serial->dev->dev, "%s: alloc for endpoint %d failed\n", + __func__, endpoint); + return NULL; } - return 0; + + buf = kmalloc(len, mem_flags); + if (buf) { + /* Fill URB using supplied data */ + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, endpoint) | dir, + buf, len, callback, ctx); + + /* debug */ + dev_dbg(&serial->dev->dev, "%s %c u : %p d:%p\n", __func__, + dir == USB_DIR_IN ? 'i' : 'o', urb, buf); + } else { + dev_dbg(&serial->dev->dev, "%s %c u:%p d:%p\n", __func__, + dir == USB_DIR_IN ? 'i' : 'o', urb, buf); + + sierra_release_urb(urb); + urb = NULL; + } + + return urb; } -static void sierra_close(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static void sierra_close(struct usb_serial_port *port) { int i; struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; - dev_dbg(&port->dev, "%s", __func__); + dev_dbg(&port->dev, "%s\n", __func__); portdata = usb_get_serial_port_data(port); portdata->rts_state = 0; @@ -626,27 +709,85 @@ static void sierra_close(struct tty_struct *tty, if (serial->dev) { mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - sierra_send_setup(tty, port); + sierra_send_setup(port); mutex_unlock(&serial->disc_mutex); - /* Stop reading/writing urbs */ - for (i = 0; i < N_IN_URB; i++) - usb_kill_urb(portdata->in_urbs[i]); + /* Stop reading urbs */ + sierra_stop_rx_urbs(port); + /* .. and release them */ + for (i = 0; i < N_IN_URB; i++) { + sierra_release_urb(portdata->in_urbs[i]); + portdata->in_urbs[i] = NULL; + } } +} - usb_kill_urb(port->interrupt_in_urb); - tty_port_tty_set(&port->port, NULL); +static int sierra_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) +{ + struct sierra_port_private *portdata; + struct usb_serial *serial = port->serial; + int i; + int err; + int endpoint; + struct urb *urb; + + portdata = usb_get_serial_port_data(port); + + dev_dbg(&port->dev, "%s\n", __func__); + + /* Set some sane defaults */ + portdata->rts_state = 1; + portdata->dtr_state = 1; + + + endpoint = port->bulk_in_endpointAddress; + for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) { + urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port, + IN_BUFLEN, GFP_KERNEL, + sierra_indat_callback); + portdata->in_urbs[i] = urb; + } + /* clear halt condition */ + usb_clear_halt(serial->dev, + usb_sndbulkpipe(serial->dev, endpoint) | USB_DIR_IN); + + err = sierra_submit_rx_urbs(port, GFP_KERNEL); + if (err) { + /* get rid of everything as in close */ + sierra_close(port); + return err; + } + sierra_send_setup(port); + + return 0; +} + + +static void sierra_dtr_rts(struct usb_serial_port *port, int on) +{ + struct usb_serial *serial = port->serial; + struct sierra_port_private *portdata; + + portdata = usb_get_serial_port_data(port); + portdata->rts_state = on; + portdata->dtr_state = on; + + if (serial->dev) { + mutex_lock(&serial->disc_mutex); + if (!serial->disconnected) + sierra_send_setup(port); + mutex_unlock(&serial->disc_mutex); + } } static int sierra_startup(struct usb_serial *serial) { struct usb_serial_port *port; struct sierra_port_private *portdata; - struct urb *urb; int i; - int j; - dev_dbg(&serial->dev->dev, "%s", __func__); + dev_dbg(&serial->dev->dev, "%s\n", __func__); /* Set Device mode to D0 */ sierra_set_power_state(serial->dev, 0x0000); @@ -661,51 +802,25 @@ static int sierra_startup(struct usb_serial *serial) portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); if (!portdata) { dev_dbg(&port->dev, "%s: kmalloc for " - "sierra_port_private (%d) failed!.", + "sierra_port_private (%d) failed!.\n", __func__, i); return -ENOMEM; } spin_lock_init(&portdata->lock); - for (j = 0; j < N_IN_URB; j++) { - portdata->in_buffer[j] = kmalloc(IN_BUFLEN, GFP_KERNEL); - if (!portdata->in_buffer[j]) { - for (--j; j >= 0; j--) - kfree(portdata->in_buffer[j]); - kfree(portdata); - return -ENOMEM; - } - } - + /* Set the port private data pointer */ usb_set_serial_port_data(port, portdata); - - /* initialize the in urbs */ - for (j = 0; j < N_IN_URB; ++j) { - urb = usb_alloc_urb(0, GFP_KERNEL); - if (urb == NULL) { - dev_dbg(&port->dev, "%s: alloc for in " - "port failed.", __func__); - 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; - } } return 0; } -static void sierra_shutdown(struct usb_serial *serial) +static void sierra_disconnect(struct usb_serial *serial) { - int i, j; + int i; struct usb_serial_port *port; struct sierra_port_private *portdata; - dev_dbg(&serial->dev->dev, "%s", __func__); + dev_dbg(&serial->dev->dev, "%s\n", __func__); for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; @@ -714,12 +829,6 @@ static void sierra_shutdown(struct usb_serial *serial) portdata = usb_get_serial_port_data(port); if (!portdata) continue; - - for (j = 0; j < N_IN_URB; j++) { - usb_kill_urb(portdata->in_urbs[j]); - usb_free_urb(portdata->in_urbs[j]); - kfree(portdata->in_buffer[j]); - } kfree(portdata); usb_set_serial_port_data(port, NULL); } @@ -737,13 +846,14 @@ static struct usb_serial_driver sierra_device = { .probe = sierra_probe, .open = sierra_open, .close = sierra_close, + .dtr_rts = sierra_dtr_rts, .write = sierra_write, .write_room = sierra_write_room, .set_termios = sierra_set_termios, .tiocmget = sierra_tiocmget, .tiocmset = sierra_tiocmset, .attach = sierra_startup, - .shutdown = sierra_shutdown, + .disconnect = sierra_disconnect, .read_int_callback = sierra_instat_callback, }; |