Loading drivers/usb/serial/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -581,6 +581,7 @@ config USB_SERIAL_WWAN config USB_SERIAL_OPTION tristate "USB driver for GSM and CDMA modems" select USB_SERIAL_WWAN help Say Y here if you have a GSM or CDMA modem that's connected to USB. Loading drivers/usb/serial/option.c +26 −646 Original line number Diff line number Diff line Loading @@ -42,34 +42,13 @@ #include <linux/bitops.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "usb-wwan.h" /* Function prototypes */ static int option_probe(struct usb_serial *serial, const struct usb_device_id *id); static int option_open(struct tty_struct *tty, struct usb_serial_port *port); static void option_close(struct usb_serial_port *port); static void option_dtr_rts(struct usb_serial_port *port, int on); static int option_startup(struct usb_serial *serial); static void option_disconnect(struct usb_serial *serial); static void option_release(struct usb_serial *serial); static int option_write_room(struct tty_struct *tty); static void option_instat_callback(struct urb *urb); static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int option_chars_in_buffer(struct tty_struct *tty); static void option_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int option_tiocmget(struct tty_struct *tty, struct file *file); static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int option_send_setup(struct usb_serial_port *port); #ifdef CONFIG_PM static int option_suspend(struct usb_serial *serial, pm_message_t message); static int option_resume(struct usb_serial *serial); #endif static void option_instat_callback(struct urb *urb); /* Vendor and product IDs */ #define OPTION_VENDOR_ID 0x0AF0 Loading Loading @@ -757,22 +736,22 @@ static struct usb_serial_driver option_1port_device = { .id_table = option_ids, .num_ports = 1, .probe = option_probe, .open = option_open, .close = option_close, .dtr_rts = option_dtr_rts, .write = option_write, .write_room = option_write_room, .chars_in_buffer = option_chars_in_buffer, .set_termios = option_set_termios, .tiocmget = option_tiocmget, .tiocmset = option_tiocmset, .attach = option_startup, .disconnect = option_disconnect, .release = option_release, .open = usb_wwan_open, .close = usb_wwan_close, .dtr_rts = usb_wwan_dtr_rts, .write = usb_wwan_write, .write_room = usb_wwan_write_room, .chars_in_buffer = usb_wwan_chars_in_buffer, .set_termios = usb_wwan_set_termios, .tiocmget = usb_wwan_tiocmget, .tiocmset = usb_wwan_tiocmset, .attach = usb_wwan_startup, .disconnect = usb_wwan_disconnect, .release = usb_wwan_release, .read_int_callback = option_instat_callback, #ifdef CONFIG_PM .suspend = option_suspend, .resume = option_resume, .suspend = usb_wwan_suspend, .resume = usb_wwan_resume, #endif }; Loading @@ -785,13 +764,6 @@ static int debug; #define IN_BUFLEN 4096 #define OUT_BUFLEN 4096 struct option_intf_private { spinlock_t susp_lock; unsigned int suspended:1; int in_flight; struct option_blacklist_info *blacklist_info; }; struct option_port_private { /* Input endpoints and buffer for this port */ struct urb *in_urbs[N_IN_URB]; Loading Loading @@ -848,8 +820,7 @@ module_exit(option_exit); static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { struct option_intf_private *data; struct usb_wwan_intf_private *data; /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && Loading @@ -862,11 +833,13 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) return -ENODEV; data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL); data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; data->send_setup = option_send_setup; spin_lock_init(&data->susp_lock); data->blacklist_info = (struct option_blacklist_info*) id->driver_info; data->private = (void *)id->driver_info; return 0; } Loading @@ -887,205 +860,6 @@ static enum option_blacklist_reason is_blacklisted(const u8 ifnum, return OPTION_BLACKLIST_NONE; } static void option_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s", __func__); /* Doesn't support option setting */ tty_termios_copy_hw(tty->termios, old_termios); option_send_setup(port); } static int option_tiocmget(struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; unsigned int value; struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); value = ((portdata->rts_state) ? TIOCM_RTS : 0) | ((portdata->dtr_state) ? TIOCM_DTR : 0) | ((portdata->cts_state) ? TIOCM_CTS : 0) | ((portdata->dsr_state) ? TIOCM_DSR : 0) | ((portdata->dcd_state) ? TIOCM_CAR : 0) | ((portdata->ri_state) ? TIOCM_RNG : 0); return value; } static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); /* FIXME: what locks portdata fields ? */ if (set & TIOCM_RTS) portdata->rts_state = 1; if (set & TIOCM_DTR) portdata->dtr_state = 1; if (clear & TIOCM_RTS) portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; return option_send_setup(port); } /* Write */ static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct option_port_private *portdata; struct option_intf_private *intfdata; int i; int left, todo; struct urb *this_urb = NULL; /* spurious */ int err; unsigned long flags; portdata = usb_get_serial_port_data(port); intfdata = port->serial->private; dbg("%s: write (%d chars)", __func__, 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 (test_and_set_bit(i, &portdata->out_busy)) { if (time_before(jiffies, portdata->tx_start_time[i] + 10 * HZ)) continue; usb_unlink_urb(this_urb); continue; } dbg("%s: endpoint %d buf %d", __func__, usb_pipeendpoint(this_urb->pipe), i); err = usb_autopm_get_interface_async(port->serial->interface); if (err < 0) break; /* send the data */ memcpy(this_urb->transfer_buffer, buf, todo); this_urb->transfer_buffer_length = todo; spin_lock_irqsave(&intfdata->susp_lock, flags); if (intfdata->suspended) { usb_anchor_urb(this_urb, &portdata->delayed); spin_unlock_irqrestore(&intfdata->susp_lock, flags); } else { intfdata->in_flight++; spin_unlock_irqrestore(&intfdata->susp_lock, flags); err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err) { dbg("usb_submit_urb %p (write bulk) failed " "(%d)", this_urb, err); clear_bit(i, &portdata->out_busy); spin_lock_irqsave(&intfdata->susp_lock, flags); intfdata->in_flight--; spin_unlock_irqrestore(&intfdata->susp_lock, flags); continue; } } portdata->tx_start_time[i] = jiffies; buf += todo; left -= todo; } count -= left; dbg("%s: wrote (did %d)", __func__, count); return count; } static void option_indat_callback(struct urb *urb) { int err; int endpoint; struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int status = urb->status; dbg("%s: %p", __func__, urb); endpoint = usb_pipeendpoint(urb->pipe); port = urb->context; 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 on endpoint %02x.", __func__, status, endpoint); return; default: dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); goto exit; } if (urb->actual_length) { tty = tty_port_tty_get(&port->port); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); tty_kref_put(tty); } else dbg("%s: empty read urb received", __func__); exit: /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err && err != -EPERM) printk(KERN_ERR "%s: resubmit read urb failed. " "(%d)", __func__, err); else usb_mark_last_busy(port->serial->dev); return; } static void option_outdat_callback(struct urb *urb) { struct usb_serial_port *port; struct option_port_private *portdata; struct option_intf_private *intfdata; int i; dbg("%s", __func__); port = urb->context; intfdata = port->serial->private; usb_serial_port_softint(port); usb_autopm_put_interface_async(port->serial->interface); portdata = usb_get_serial_port_data(port); spin_lock(&intfdata->susp_lock); intfdata->in_flight--; spin_unlock(&intfdata->susp_lock); for (i = 0; i < N_OUT_URB; ++i) { if (portdata->out_urbs[i] == urb) { smp_mb__before_clear_bit(); clear_bit(i, &portdata->out_busy); break; } } } static void option_instat_callback(struct urb *urb) { int err; Loading Loading @@ -1142,183 +916,6 @@ static void option_instat_callback(struct urb *urb) } } static int option_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct option_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 && !test_bit(i, &portdata->out_busy)) data_len += OUT_BUFLEN; } dbg("%s: %d", __func__, data_len); return data_len; } static int option_chars_in_buffer(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct option_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]; /* FIXME: This locking is insufficient as this_urb may go unused during the test */ if (this_urb && test_bit(i, &portdata->out_busy)) data_len += this_urb->transfer_buffer_length; } dbg("%s: %d", __func__, data_len); return data_len; } static int option_open(struct tty_struct *tty, struct usb_serial_port *port) { struct option_port_private *portdata; struct option_intf_private *intfdata; struct usb_serial *serial = port->serial; int i, err; struct urb *urb; portdata = usb_get_serial_port_data(port); intfdata = serial->private; dbg("%s", __func__); /* Start reading from the IN endpoint */ for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; if (!urb) continue; err = usb_submit_urb(urb, GFP_KERNEL); if (err) { dbg("%s: submit urb %d failed (%d) %d", __func__, i, err, urb->transfer_buffer_length); } } option_send_setup(port); serial->interface->needs_remote_wakeup = 1; spin_lock_irq(&intfdata->susp_lock); portdata->opened = 1; spin_unlock_irq(&intfdata->susp_lock); usb_autopm_put_interface(serial->interface); return 0; } static void option_dtr_rts(struct usb_serial_port *port, int on) { struct usb_serial *serial = port->serial; struct option_port_private *portdata; dbg("%s", __func__); portdata = usb_get_serial_port_data(port); mutex_lock(&serial->disc_mutex); portdata->rts_state = on; portdata->dtr_state = on; if (serial->dev) option_send_setup(port); mutex_unlock(&serial->disc_mutex); } static void option_close(struct usb_serial_port *port) { int i; struct usb_serial *serial = port->serial; struct option_port_private *portdata; struct option_intf_private *intfdata = port->serial->private; dbg("%s", __func__); portdata = usb_get_serial_port_data(port); if (serial->dev) { /* Stop reading/writing urbs */ spin_lock_irq(&intfdata->susp_lock); portdata->opened = 0; spin_unlock_irq(&intfdata->susp_lock); for (i = 0; i < N_IN_URB; i++) usb_kill_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); usb_autopm_get_interface(serial->interface); serial->interface->needs_remote_wakeup = 0; } } /* Helper functions used by option_setup_urbs */ static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint, int dir, void *ctx, char *buf, int len, void (*callback)(struct urb *)) { 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.", __func__, 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 option_setup_urbs(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; struct option_port_private *portdata; dbg("%s", __func__); 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] = option_setup_urb(serial, port->bulk_in_endpointAddress, USB_DIR_IN, port, portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); } /* outdat endpoints */ for (j = 0; j < N_OUT_URB; ++j) { portdata->out_urbs[j] = option_setup_urb(serial, port->bulk_out_endpointAddress, USB_DIR_OUT, port, portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); } } } /** send RTS/DTR state to the port. * * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN Loading @@ -1327,15 +924,16 @@ static void option_setup_urbs(struct usb_serial *serial) static int option_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct option_intf_private *intfdata = (struct option_intf_private *) serial->private; struct usb_wwan_intf_private *intfdata = (struct usb_wwan_intf_private *) serial->private; struct option_port_private *portdata; int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber; int val = 0; dbg("%s", __func__); if (is_blacklisted(ifNum, intfdata->blacklist_info) == OPTION_BLACKLIST_SENDSETUP) { if (is_blacklisted(ifNum, (struct option_blacklist_info *) intfdata->private) == OPTION_BLACKLIST_SENDSETUP) { dbg("No send_setup on blacklisted interface #%d\n", ifNum); return -EIO; } Loading @@ -1352,224 +950,6 @@ static int option_send_setup(struct usb_serial_port *port) 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT); } static int option_startup(struct usb_serial *serial) { int i, j, err; struct usb_serial_port *port; struct option_port_private *portdata; u8 *buffer; dbg("%s", __func__); /* Now setup per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); if (!portdata) { dbg("%s: kmalloc for option_port_private (%d) failed!.", __func__, i); return 1; } init_usb_anchor(&portdata->delayed); for (j = 0; j < N_IN_URB; j++) { buffer = (u8 *)__get_free_page(GFP_KERNEL); if (!buffer) goto bail_out_error; portdata->in_buffer[j] = buffer; } for (j = 0; j < N_OUT_URB; j++) { buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); if (!buffer) goto bail_out_error2; portdata->out_buffer[j] = buffer; } 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", __func__, err); } option_setup_urbs(serial); return 0; bail_out_error2: for (j = 0; j < N_OUT_URB; j++) kfree(portdata->out_buffer[j]); bail_out_error: for (j = 0; j < N_IN_URB; j++) if (portdata->in_buffer[j]) free_page((unsigned long)portdata->in_buffer[j]); kfree(portdata); return 1; } static void stop_read_write_urbs(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; struct option_port_private *portdata; /* 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++) usb_kill_urb(portdata->in_urbs[j]); for (j = 0; j < N_OUT_URB; j++) usb_kill_urb(portdata->out_urbs[j]); } } static void option_disconnect(struct usb_serial *serial) { dbg("%s", __func__); stop_read_write_urbs(serial); } static void option_release(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; struct option_port_private *portdata; dbg("%s", __func__); /* Now free them */ 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++) { if (portdata->in_urbs[j]) { usb_free_urb(portdata->in_urbs[j]); free_page((unsigned long) portdata->in_buffer[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]); kfree(portdata->out_buffer[j]); portdata->out_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)); } } #ifdef CONFIG_PM static int option_suspend(struct usb_serial *serial, pm_message_t message) { struct option_intf_private *intfdata = serial->private; int b; dbg("%s entered", __func__); if (message.event & PM_EVENT_AUTO) { spin_lock_irq(&intfdata->susp_lock); b = intfdata->in_flight; spin_unlock_irq(&intfdata->susp_lock); if (b) return -EBUSY; } spin_lock_irq(&intfdata->susp_lock); intfdata->suspended = 1; spin_unlock_irq(&intfdata->susp_lock); stop_read_write_urbs(serial); return 0; } static void play_delayed(struct usb_serial_port *port) { struct option_intf_private *data; struct option_port_private *portdata; struct urb *urb; int err; portdata = usb_get_serial_port_data(port); data = port->serial->private; while ((urb = usb_get_from_anchor(&portdata->delayed))) { err = usb_submit_urb(urb, GFP_ATOMIC); if (!err) data->in_flight++; } } static int option_resume(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; struct option_intf_private *intfdata = serial->private; struct option_port_private *portdata; struct urb *urb; int err = 0; dbg("%s entered", __func__); /* get the interrupt URBs resubmitted unconditionally */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; if (!port->interrupt_in_urb) { dbg("%s: No interrupt URB for port %d", __func__, i); continue; } err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); dbg("Submitted interrupt URB for port %d (result %d)", i, err); if (err < 0) { err("%s: Error %d for interrupt URB of port%d", __func__, err, i); goto err_out; } } for (i = 0; i < serial->num_ports; i++) { /* walk all ports */ port = serial->port[i]; portdata = usb_get_serial_port_data(port); /* skip closed ports */ spin_lock_irq(&intfdata->susp_lock); if (!portdata->opened) { spin_unlock_irq(&intfdata->susp_lock); continue; } for (j = 0; j < N_IN_URB; j++) { urb = portdata->in_urbs[j]; err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { err("%s: Error %d for bulk URB %d", __func__, err, i); spin_unlock_irq(&intfdata->susp_lock); goto err_out; } } play_delayed(port); spin_unlock_irq(&intfdata->susp_lock); } spin_lock_irq(&intfdata->susp_lock); intfdata->suspended = 0; spin_unlock_irq(&intfdata->susp_lock); err_out: return err; } #endif MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); Loading Loading
drivers/usb/serial/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -581,6 +581,7 @@ config USB_SERIAL_WWAN config USB_SERIAL_OPTION tristate "USB driver for GSM and CDMA modems" select USB_SERIAL_WWAN help Say Y here if you have a GSM or CDMA modem that's connected to USB. Loading
drivers/usb/serial/option.c +26 −646 Original line number Diff line number Diff line Loading @@ -42,34 +42,13 @@ #include <linux/bitops.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "usb-wwan.h" /* Function prototypes */ static int option_probe(struct usb_serial *serial, const struct usb_device_id *id); static int option_open(struct tty_struct *tty, struct usb_serial_port *port); static void option_close(struct usb_serial_port *port); static void option_dtr_rts(struct usb_serial_port *port, int on); static int option_startup(struct usb_serial *serial); static void option_disconnect(struct usb_serial *serial); static void option_release(struct usb_serial *serial); static int option_write_room(struct tty_struct *tty); static void option_instat_callback(struct urb *urb); static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int option_chars_in_buffer(struct tty_struct *tty); static void option_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int option_tiocmget(struct tty_struct *tty, struct file *file); static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int option_send_setup(struct usb_serial_port *port); #ifdef CONFIG_PM static int option_suspend(struct usb_serial *serial, pm_message_t message); static int option_resume(struct usb_serial *serial); #endif static void option_instat_callback(struct urb *urb); /* Vendor and product IDs */ #define OPTION_VENDOR_ID 0x0AF0 Loading Loading @@ -757,22 +736,22 @@ static struct usb_serial_driver option_1port_device = { .id_table = option_ids, .num_ports = 1, .probe = option_probe, .open = option_open, .close = option_close, .dtr_rts = option_dtr_rts, .write = option_write, .write_room = option_write_room, .chars_in_buffer = option_chars_in_buffer, .set_termios = option_set_termios, .tiocmget = option_tiocmget, .tiocmset = option_tiocmset, .attach = option_startup, .disconnect = option_disconnect, .release = option_release, .open = usb_wwan_open, .close = usb_wwan_close, .dtr_rts = usb_wwan_dtr_rts, .write = usb_wwan_write, .write_room = usb_wwan_write_room, .chars_in_buffer = usb_wwan_chars_in_buffer, .set_termios = usb_wwan_set_termios, .tiocmget = usb_wwan_tiocmget, .tiocmset = usb_wwan_tiocmset, .attach = usb_wwan_startup, .disconnect = usb_wwan_disconnect, .release = usb_wwan_release, .read_int_callback = option_instat_callback, #ifdef CONFIG_PM .suspend = option_suspend, .resume = option_resume, .suspend = usb_wwan_suspend, .resume = usb_wwan_resume, #endif }; Loading @@ -785,13 +764,6 @@ static int debug; #define IN_BUFLEN 4096 #define OUT_BUFLEN 4096 struct option_intf_private { spinlock_t susp_lock; unsigned int suspended:1; int in_flight; struct option_blacklist_info *blacklist_info; }; struct option_port_private { /* Input endpoints and buffer for this port */ struct urb *in_urbs[N_IN_URB]; Loading Loading @@ -848,8 +820,7 @@ module_exit(option_exit); static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { struct option_intf_private *data; struct usb_wwan_intf_private *data; /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && Loading @@ -862,11 +833,13 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) return -ENODEV; data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL); data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; data->send_setup = option_send_setup; spin_lock_init(&data->susp_lock); data->blacklist_info = (struct option_blacklist_info*) id->driver_info; data->private = (void *)id->driver_info; return 0; } Loading @@ -887,205 +860,6 @@ static enum option_blacklist_reason is_blacklisted(const u8 ifnum, return OPTION_BLACKLIST_NONE; } static void option_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s", __func__); /* Doesn't support option setting */ tty_termios_copy_hw(tty->termios, old_termios); option_send_setup(port); } static int option_tiocmget(struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; unsigned int value; struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); value = ((portdata->rts_state) ? TIOCM_RTS : 0) | ((portdata->dtr_state) ? TIOCM_DTR : 0) | ((portdata->cts_state) ? TIOCM_CTS : 0) | ((portdata->dsr_state) ? TIOCM_DSR : 0) | ((portdata->dcd_state) ? TIOCM_CAR : 0) | ((portdata->ri_state) ? TIOCM_RNG : 0); return value; } static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); /* FIXME: what locks portdata fields ? */ if (set & TIOCM_RTS) portdata->rts_state = 1; if (set & TIOCM_DTR) portdata->dtr_state = 1; if (clear & TIOCM_RTS) portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; return option_send_setup(port); } /* Write */ static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct option_port_private *portdata; struct option_intf_private *intfdata; int i; int left, todo; struct urb *this_urb = NULL; /* spurious */ int err; unsigned long flags; portdata = usb_get_serial_port_data(port); intfdata = port->serial->private; dbg("%s: write (%d chars)", __func__, 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 (test_and_set_bit(i, &portdata->out_busy)) { if (time_before(jiffies, portdata->tx_start_time[i] + 10 * HZ)) continue; usb_unlink_urb(this_urb); continue; } dbg("%s: endpoint %d buf %d", __func__, usb_pipeendpoint(this_urb->pipe), i); err = usb_autopm_get_interface_async(port->serial->interface); if (err < 0) break; /* send the data */ memcpy(this_urb->transfer_buffer, buf, todo); this_urb->transfer_buffer_length = todo; spin_lock_irqsave(&intfdata->susp_lock, flags); if (intfdata->suspended) { usb_anchor_urb(this_urb, &portdata->delayed); spin_unlock_irqrestore(&intfdata->susp_lock, flags); } else { intfdata->in_flight++; spin_unlock_irqrestore(&intfdata->susp_lock, flags); err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err) { dbg("usb_submit_urb %p (write bulk) failed " "(%d)", this_urb, err); clear_bit(i, &portdata->out_busy); spin_lock_irqsave(&intfdata->susp_lock, flags); intfdata->in_flight--; spin_unlock_irqrestore(&intfdata->susp_lock, flags); continue; } } portdata->tx_start_time[i] = jiffies; buf += todo; left -= todo; } count -= left; dbg("%s: wrote (did %d)", __func__, count); return count; } static void option_indat_callback(struct urb *urb) { int err; int endpoint; struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int status = urb->status; dbg("%s: %p", __func__, urb); endpoint = usb_pipeendpoint(urb->pipe); port = urb->context; 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 on endpoint %02x.", __func__, status, endpoint); return; default: dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); goto exit; } if (urb->actual_length) { tty = tty_port_tty_get(&port->port); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); tty_kref_put(tty); } else dbg("%s: empty read urb received", __func__); exit: /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err && err != -EPERM) printk(KERN_ERR "%s: resubmit read urb failed. " "(%d)", __func__, err); else usb_mark_last_busy(port->serial->dev); return; } static void option_outdat_callback(struct urb *urb) { struct usb_serial_port *port; struct option_port_private *portdata; struct option_intf_private *intfdata; int i; dbg("%s", __func__); port = urb->context; intfdata = port->serial->private; usb_serial_port_softint(port); usb_autopm_put_interface_async(port->serial->interface); portdata = usb_get_serial_port_data(port); spin_lock(&intfdata->susp_lock); intfdata->in_flight--; spin_unlock(&intfdata->susp_lock); for (i = 0; i < N_OUT_URB; ++i) { if (portdata->out_urbs[i] == urb) { smp_mb__before_clear_bit(); clear_bit(i, &portdata->out_busy); break; } } } static void option_instat_callback(struct urb *urb) { int err; Loading Loading @@ -1142,183 +916,6 @@ static void option_instat_callback(struct urb *urb) } } static int option_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct option_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 && !test_bit(i, &portdata->out_busy)) data_len += OUT_BUFLEN; } dbg("%s: %d", __func__, data_len); return data_len; } static int option_chars_in_buffer(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct option_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]; /* FIXME: This locking is insufficient as this_urb may go unused during the test */ if (this_urb && test_bit(i, &portdata->out_busy)) data_len += this_urb->transfer_buffer_length; } dbg("%s: %d", __func__, data_len); return data_len; } static int option_open(struct tty_struct *tty, struct usb_serial_port *port) { struct option_port_private *portdata; struct option_intf_private *intfdata; struct usb_serial *serial = port->serial; int i, err; struct urb *urb; portdata = usb_get_serial_port_data(port); intfdata = serial->private; dbg("%s", __func__); /* Start reading from the IN endpoint */ for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; if (!urb) continue; err = usb_submit_urb(urb, GFP_KERNEL); if (err) { dbg("%s: submit urb %d failed (%d) %d", __func__, i, err, urb->transfer_buffer_length); } } option_send_setup(port); serial->interface->needs_remote_wakeup = 1; spin_lock_irq(&intfdata->susp_lock); portdata->opened = 1; spin_unlock_irq(&intfdata->susp_lock); usb_autopm_put_interface(serial->interface); return 0; } static void option_dtr_rts(struct usb_serial_port *port, int on) { struct usb_serial *serial = port->serial; struct option_port_private *portdata; dbg("%s", __func__); portdata = usb_get_serial_port_data(port); mutex_lock(&serial->disc_mutex); portdata->rts_state = on; portdata->dtr_state = on; if (serial->dev) option_send_setup(port); mutex_unlock(&serial->disc_mutex); } static void option_close(struct usb_serial_port *port) { int i; struct usb_serial *serial = port->serial; struct option_port_private *portdata; struct option_intf_private *intfdata = port->serial->private; dbg("%s", __func__); portdata = usb_get_serial_port_data(port); if (serial->dev) { /* Stop reading/writing urbs */ spin_lock_irq(&intfdata->susp_lock); portdata->opened = 0; spin_unlock_irq(&intfdata->susp_lock); for (i = 0; i < N_IN_URB; i++) usb_kill_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); usb_autopm_get_interface(serial->interface); serial->interface->needs_remote_wakeup = 0; } } /* Helper functions used by option_setup_urbs */ static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint, int dir, void *ctx, char *buf, int len, void (*callback)(struct urb *)) { 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.", __func__, 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 option_setup_urbs(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; struct option_port_private *portdata; dbg("%s", __func__); 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] = option_setup_urb(serial, port->bulk_in_endpointAddress, USB_DIR_IN, port, portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); } /* outdat endpoints */ for (j = 0; j < N_OUT_URB; ++j) { portdata->out_urbs[j] = option_setup_urb(serial, port->bulk_out_endpointAddress, USB_DIR_OUT, port, portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); } } } /** send RTS/DTR state to the port. * * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN Loading @@ -1327,15 +924,16 @@ static void option_setup_urbs(struct usb_serial *serial) static int option_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct option_intf_private *intfdata = (struct option_intf_private *) serial->private; struct usb_wwan_intf_private *intfdata = (struct usb_wwan_intf_private *) serial->private; struct option_port_private *portdata; int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber; int val = 0; dbg("%s", __func__); if (is_blacklisted(ifNum, intfdata->blacklist_info) == OPTION_BLACKLIST_SENDSETUP) { if (is_blacklisted(ifNum, (struct option_blacklist_info *) intfdata->private) == OPTION_BLACKLIST_SENDSETUP) { dbg("No send_setup on blacklisted interface #%d\n", ifNum); return -EIO; } Loading @@ -1352,224 +950,6 @@ static int option_send_setup(struct usb_serial_port *port) 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT); } static int option_startup(struct usb_serial *serial) { int i, j, err; struct usb_serial_port *port; struct option_port_private *portdata; u8 *buffer; dbg("%s", __func__); /* Now setup per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); if (!portdata) { dbg("%s: kmalloc for option_port_private (%d) failed!.", __func__, i); return 1; } init_usb_anchor(&portdata->delayed); for (j = 0; j < N_IN_URB; j++) { buffer = (u8 *)__get_free_page(GFP_KERNEL); if (!buffer) goto bail_out_error; portdata->in_buffer[j] = buffer; } for (j = 0; j < N_OUT_URB; j++) { buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); if (!buffer) goto bail_out_error2; portdata->out_buffer[j] = buffer; } 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", __func__, err); } option_setup_urbs(serial); return 0; bail_out_error2: for (j = 0; j < N_OUT_URB; j++) kfree(portdata->out_buffer[j]); bail_out_error: for (j = 0; j < N_IN_URB; j++) if (portdata->in_buffer[j]) free_page((unsigned long)portdata->in_buffer[j]); kfree(portdata); return 1; } static void stop_read_write_urbs(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; struct option_port_private *portdata; /* 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++) usb_kill_urb(portdata->in_urbs[j]); for (j = 0; j < N_OUT_URB; j++) usb_kill_urb(portdata->out_urbs[j]); } } static void option_disconnect(struct usb_serial *serial) { dbg("%s", __func__); stop_read_write_urbs(serial); } static void option_release(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; struct option_port_private *portdata; dbg("%s", __func__); /* Now free them */ 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++) { if (portdata->in_urbs[j]) { usb_free_urb(portdata->in_urbs[j]); free_page((unsigned long) portdata->in_buffer[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]); kfree(portdata->out_buffer[j]); portdata->out_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)); } } #ifdef CONFIG_PM static int option_suspend(struct usb_serial *serial, pm_message_t message) { struct option_intf_private *intfdata = serial->private; int b; dbg("%s entered", __func__); if (message.event & PM_EVENT_AUTO) { spin_lock_irq(&intfdata->susp_lock); b = intfdata->in_flight; spin_unlock_irq(&intfdata->susp_lock); if (b) return -EBUSY; } spin_lock_irq(&intfdata->susp_lock); intfdata->suspended = 1; spin_unlock_irq(&intfdata->susp_lock); stop_read_write_urbs(serial); return 0; } static void play_delayed(struct usb_serial_port *port) { struct option_intf_private *data; struct option_port_private *portdata; struct urb *urb; int err; portdata = usb_get_serial_port_data(port); data = port->serial->private; while ((urb = usb_get_from_anchor(&portdata->delayed))) { err = usb_submit_urb(urb, GFP_ATOMIC); if (!err) data->in_flight++; } } static int option_resume(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; struct option_intf_private *intfdata = serial->private; struct option_port_private *portdata; struct urb *urb; int err = 0; dbg("%s entered", __func__); /* get the interrupt URBs resubmitted unconditionally */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; if (!port->interrupt_in_urb) { dbg("%s: No interrupt URB for port %d", __func__, i); continue; } err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); dbg("Submitted interrupt URB for port %d (result %d)", i, err); if (err < 0) { err("%s: Error %d for interrupt URB of port%d", __func__, err, i); goto err_out; } } for (i = 0; i < serial->num_ports; i++) { /* walk all ports */ port = serial->port[i]; portdata = usb_get_serial_port_data(port); /* skip closed ports */ spin_lock_irq(&intfdata->susp_lock); if (!portdata->opened) { spin_unlock_irq(&intfdata->susp_lock); continue; } for (j = 0; j < N_IN_URB; j++) { urb = portdata->in_urbs[j]; err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { err("%s: Error %d for bulk URB %d", __func__, err, i); spin_unlock_irq(&intfdata->susp_lock); goto err_out; } } play_delayed(port); spin_unlock_irq(&intfdata->susp_lock); } spin_lock_irq(&intfdata->susp_lock); intfdata->suspended = 0; spin_unlock_irq(&intfdata->susp_lock); err_out: return err; } #endif MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); Loading