162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci USB Driver layer for GSM modems 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci History: see the git log. 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci Work sponsored by: Sigos GmbH, Germany <info@sigos.de> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci This driver exists because the "normal" serial driver doesn't work too well 1462306a36Sopenharmony_ci with GSM modems. Issues: 1562306a36Sopenharmony_ci - data loss -- one single Receive URB is not nearly enough 1662306a36Sopenharmony_ci - controlling the baud rate doesn't make sense 1762306a36Sopenharmony_ci*/ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>" 2062306a36Sopenharmony_ci#define DRIVER_DESC "USB Driver for GSM modems" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/jiffies.h> 2462306a36Sopenharmony_ci#include <linux/errno.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/tty.h> 2762306a36Sopenharmony_ci#include <linux/tty_flip.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/bitops.h> 3062306a36Sopenharmony_ci#include <linux/uaccess.h> 3162306a36Sopenharmony_ci#include <linux/usb.h> 3262306a36Sopenharmony_ci#include <linux/usb/cdc.h> 3362306a36Sopenharmony_ci#include <linux/usb/serial.h> 3462306a36Sopenharmony_ci#include <linux/serial.h> 3562306a36Sopenharmony_ci#include "usb-wwan.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Generate DTR/RTS signals on the port using the SET_CONTROL_LINE_STATE request 3962306a36Sopenharmony_ci * in CDC ACM. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic int usb_wwan_send_setup(struct usb_serial_port *port) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 4462306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 4562306a36Sopenharmony_ci int val = 0; 4662306a36Sopenharmony_ci int ifnum; 4762306a36Sopenharmony_ci int res; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (portdata->dtr_state) 5262306a36Sopenharmony_ci val |= USB_CDC_CTRL_DTR; 5362306a36Sopenharmony_ci if (portdata->rts_state) 5462306a36Sopenharmony_ci val |= USB_CDC_CTRL_RTS; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci res = usb_autopm_get_interface(serial->interface); 5962306a36Sopenharmony_ci if (res) 6062306a36Sopenharmony_ci return res; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 6362306a36Sopenharmony_ci USB_CDC_REQ_SET_CONTROL_LINE_STATE, 6462306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 6562306a36Sopenharmony_ci val, ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci usb_autopm_put_interface(port->serial->interface); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return res; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_civoid usb_wwan_dtr_rts(struct usb_serial_port *port, int on) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 7562306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci intfdata = usb_get_serial_data(port->serial); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (!intfdata->use_send_setup) 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 8362306a36Sopenharmony_ci /* FIXME: locking */ 8462306a36Sopenharmony_ci portdata->rts_state = on; 8562306a36Sopenharmony_ci portdata->dtr_state = on; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci usb_wwan_send_setup(port); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_dtr_rts); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciint usb_wwan_tiocmget(struct tty_struct *tty) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 9462306a36Sopenharmony_ci unsigned int value; 9562306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci value = ((portdata->rts_state) ? TIOCM_RTS : 0) | 10062306a36Sopenharmony_ci ((portdata->dtr_state) ? TIOCM_DTR : 0) | 10162306a36Sopenharmony_ci ((portdata->cts_state) ? TIOCM_CTS : 0) | 10262306a36Sopenharmony_ci ((portdata->dsr_state) ? TIOCM_DSR : 0) | 10362306a36Sopenharmony_ci ((portdata->dcd_state) ? TIOCM_CAR : 0) | 10462306a36Sopenharmony_ci ((portdata->ri_state) ? TIOCM_RNG : 0); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return value; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_tiocmget); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciint usb_wwan_tiocmset(struct tty_struct *tty, 11162306a36Sopenharmony_ci unsigned int set, unsigned int clear) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 11462306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 11562306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 11862306a36Sopenharmony_ci intfdata = usb_get_serial_data(port->serial); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (!intfdata->use_send_setup) 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* FIXME: what locks portdata fields ? */ 12462306a36Sopenharmony_ci if (set & TIOCM_RTS) 12562306a36Sopenharmony_ci portdata->rts_state = 1; 12662306a36Sopenharmony_ci if (set & TIOCM_DTR) 12762306a36Sopenharmony_ci portdata->dtr_state = 1; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (clear & TIOCM_RTS) 13062306a36Sopenharmony_ci portdata->rts_state = 0; 13162306a36Sopenharmony_ci if (clear & TIOCM_DTR) 13262306a36Sopenharmony_ci portdata->dtr_state = 0; 13362306a36Sopenharmony_ci return usb_wwan_send_setup(port); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_tiocmset); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciint usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, 13862306a36Sopenharmony_ci const unsigned char *buf, int count) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 14162306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata; 14262306a36Sopenharmony_ci int i; 14362306a36Sopenharmony_ci int left, todo; 14462306a36Sopenharmony_ci struct urb *this_urb = NULL; /* spurious */ 14562306a36Sopenharmony_ci int err; 14662306a36Sopenharmony_ci unsigned long flags; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 14962306a36Sopenharmony_ci intfdata = usb_get_serial_data(port->serial); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci left = count; 15462306a36Sopenharmony_ci for (i = 0; left > 0 && i < N_OUT_URB; i++) { 15562306a36Sopenharmony_ci todo = left; 15662306a36Sopenharmony_ci if (todo > OUT_BUFLEN) 15762306a36Sopenharmony_ci todo = OUT_BUFLEN; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci this_urb = portdata->out_urbs[i]; 16062306a36Sopenharmony_ci if (test_and_set_bit(i, &portdata->out_busy)) { 16162306a36Sopenharmony_ci if (time_before(jiffies, 16262306a36Sopenharmony_ci portdata->tx_start_time[i] + 10 * HZ)) 16362306a36Sopenharmony_ci continue; 16462306a36Sopenharmony_ci usb_unlink_urb(this_urb); 16562306a36Sopenharmony_ci continue; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __func__, 16862306a36Sopenharmony_ci usb_pipeendpoint(this_urb->pipe), i); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci err = usb_autopm_get_interface_async(port->serial->interface); 17162306a36Sopenharmony_ci if (err < 0) { 17262306a36Sopenharmony_ci clear_bit(i, &portdata->out_busy); 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* send the data */ 17762306a36Sopenharmony_ci memcpy(this_urb->transfer_buffer, buf, todo); 17862306a36Sopenharmony_ci this_urb->transfer_buffer_length = todo; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci spin_lock_irqsave(&intfdata->susp_lock, flags); 18162306a36Sopenharmony_ci if (intfdata->suspended) { 18262306a36Sopenharmony_ci usb_anchor_urb(this_urb, &portdata->delayed); 18362306a36Sopenharmony_ci spin_unlock_irqrestore(&intfdata->susp_lock, flags); 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci intfdata->in_flight++; 18662306a36Sopenharmony_ci spin_unlock_irqrestore(&intfdata->susp_lock, flags); 18762306a36Sopenharmony_ci err = usb_submit_urb(this_urb, GFP_ATOMIC); 18862306a36Sopenharmony_ci if (err) { 18962306a36Sopenharmony_ci dev_err(&port->dev, 19062306a36Sopenharmony_ci "%s: submit urb %d failed: %d\n", 19162306a36Sopenharmony_ci __func__, i, err); 19262306a36Sopenharmony_ci clear_bit(i, &portdata->out_busy); 19362306a36Sopenharmony_ci spin_lock_irqsave(&intfdata->susp_lock, flags); 19462306a36Sopenharmony_ci intfdata->in_flight--; 19562306a36Sopenharmony_ci spin_unlock_irqrestore(&intfdata->susp_lock, 19662306a36Sopenharmony_ci flags); 19762306a36Sopenharmony_ci usb_autopm_put_interface_async(port->serial->interface); 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci portdata->tx_start_time[i] = jiffies; 20362306a36Sopenharmony_ci buf += todo; 20462306a36Sopenharmony_ci left -= todo; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci count -= left; 20862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s: wrote (did %d)\n", __func__, count); 20962306a36Sopenharmony_ci return count; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_write); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void usb_wwan_indat_callback(struct urb *urb) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci int err; 21662306a36Sopenharmony_ci int endpoint; 21762306a36Sopenharmony_ci struct usb_serial_port *port; 21862306a36Sopenharmony_ci struct device *dev; 21962306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 22062306a36Sopenharmony_ci int status = urb->status; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci endpoint = usb_pipeendpoint(urb->pipe); 22362306a36Sopenharmony_ci port = urb->context; 22462306a36Sopenharmony_ci dev = &port->dev; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (status) { 22762306a36Sopenharmony_ci dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n", 22862306a36Sopenharmony_ci __func__, status, endpoint); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* don't resubmit on fatal errors */ 23162306a36Sopenharmony_ci if (status == -ESHUTDOWN || status == -ENOENT) 23262306a36Sopenharmony_ci return; 23362306a36Sopenharmony_ci } else { 23462306a36Sopenharmony_ci if (urb->actual_length) { 23562306a36Sopenharmony_ci tty_insert_flip_string(&port->port, data, 23662306a36Sopenharmony_ci urb->actual_length); 23762306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 23862306a36Sopenharmony_ci } else 23962306a36Sopenharmony_ci dev_dbg(dev, "%s: empty read urb received\n", __func__); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci /* Resubmit urb so we continue receiving */ 24262306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 24362306a36Sopenharmony_ci if (err) { 24462306a36Sopenharmony_ci if (err != -EPERM && err != -ENODEV) { 24562306a36Sopenharmony_ci dev_err(dev, "%s: resubmit read urb failed. (%d)\n", 24662306a36Sopenharmony_ci __func__, err); 24762306a36Sopenharmony_ci /* busy also in error unless we are killed */ 24862306a36Sopenharmony_ci usb_mark_last_busy(port->serial->dev); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci usb_mark_last_busy(port->serial->dev); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic void usb_wwan_outdat_callback(struct urb *urb) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct usb_serial_port *port; 25862306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 25962306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata; 26062306a36Sopenharmony_ci unsigned long flags; 26162306a36Sopenharmony_ci int i; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci port = urb->context; 26462306a36Sopenharmony_ci intfdata = usb_get_serial_data(port->serial); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci usb_serial_port_softint(port); 26762306a36Sopenharmony_ci usb_autopm_put_interface_async(port->serial->interface); 26862306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 26962306a36Sopenharmony_ci spin_lock_irqsave(&intfdata->susp_lock, flags); 27062306a36Sopenharmony_ci intfdata->in_flight--; 27162306a36Sopenharmony_ci spin_unlock_irqrestore(&intfdata->susp_lock, flags); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci for (i = 0; i < N_OUT_URB; ++i) { 27462306a36Sopenharmony_ci if (portdata->out_urbs[i] == urb) { 27562306a36Sopenharmony_ci smp_mb__before_atomic(); 27662306a36Sopenharmony_ci clear_bit(i, &portdata->out_busy); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciunsigned int usb_wwan_write_room(struct tty_struct *tty) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 28562306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 28662306a36Sopenharmony_ci int i; 28762306a36Sopenharmony_ci unsigned int data_len = 0; 28862306a36Sopenharmony_ci struct urb *this_urb; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci for (i = 0; i < N_OUT_URB; i++) { 29362306a36Sopenharmony_ci this_urb = portdata->out_urbs[i]; 29462306a36Sopenharmony_ci if (this_urb && !test_bit(i, &portdata->out_busy)) 29562306a36Sopenharmony_ci data_len += OUT_BUFLEN; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s: %u\n", __func__, data_len); 29962306a36Sopenharmony_ci return data_len; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_write_room); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciunsigned int usb_wwan_chars_in_buffer(struct tty_struct *tty) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 30662306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 30762306a36Sopenharmony_ci int i; 30862306a36Sopenharmony_ci unsigned int data_len = 0; 30962306a36Sopenharmony_ci struct urb *this_urb; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci for (i = 0; i < N_OUT_URB; i++) { 31462306a36Sopenharmony_ci this_urb = portdata->out_urbs[i]; 31562306a36Sopenharmony_ci /* FIXME: This locking is insufficient as this_urb may 31662306a36Sopenharmony_ci go unused during the test */ 31762306a36Sopenharmony_ci if (this_urb && test_bit(i, &portdata->out_busy)) 31862306a36Sopenharmony_ci data_len += this_urb->transfer_buffer_length; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s: %u\n", __func__, data_len); 32162306a36Sopenharmony_ci return data_len; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_chars_in_buffer); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciint usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 32862306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata; 32962306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 33062306a36Sopenharmony_ci int i, err; 33162306a36Sopenharmony_ci struct urb *urb; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 33462306a36Sopenharmony_ci intfdata = usb_get_serial_data(serial); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (port->interrupt_in_urb) { 33762306a36Sopenharmony_ci err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 33862306a36Sopenharmony_ci if (err) { 33962306a36Sopenharmony_ci dev_err(&port->dev, "%s: submit int urb failed: %d\n", 34062306a36Sopenharmony_ci __func__, err); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Start reading from the IN endpoint */ 34562306a36Sopenharmony_ci for (i = 0; i < N_IN_URB; i++) { 34662306a36Sopenharmony_ci urb = portdata->in_urbs[i]; 34762306a36Sopenharmony_ci if (!urb) 34862306a36Sopenharmony_ci continue; 34962306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_KERNEL); 35062306a36Sopenharmony_ci if (err) { 35162306a36Sopenharmony_ci dev_err(&port->dev, 35262306a36Sopenharmony_ci "%s: submit read urb %d failed: %d\n", 35362306a36Sopenharmony_ci __func__, i, err); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci spin_lock_irq(&intfdata->susp_lock); 35862306a36Sopenharmony_ci if (++intfdata->open_ports == 1) 35962306a36Sopenharmony_ci serial->interface->needs_remote_wakeup = 1; 36062306a36Sopenharmony_ci spin_unlock_irq(&intfdata->susp_lock); 36162306a36Sopenharmony_ci /* this balances a get in the generic USB serial code */ 36262306a36Sopenharmony_ci usb_autopm_put_interface(serial->interface); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_open); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void unbusy_queued_urb(struct urb *urb, 36962306a36Sopenharmony_ci struct usb_wwan_port_private *portdata) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int i; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci for (i = 0; i < N_OUT_URB; i++) { 37462306a36Sopenharmony_ci if (urb == portdata->out_urbs[i]) { 37562306a36Sopenharmony_ci clear_bit(i, &portdata->out_busy); 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_civoid usb_wwan_close(struct usb_serial_port *port) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci int i; 38462306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 38562306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 38662306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); 38762306a36Sopenharmony_ci struct urb *urb; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci * Need to take susp_lock to make sure port is not already being 39362306a36Sopenharmony_ci * resumed, but no need to hold it due to the tty-port initialized 39462306a36Sopenharmony_ci * flag. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci spin_lock_irq(&intfdata->susp_lock); 39762306a36Sopenharmony_ci if (--intfdata->open_ports == 0) 39862306a36Sopenharmony_ci serial->interface->needs_remote_wakeup = 0; 39962306a36Sopenharmony_ci spin_unlock_irq(&intfdata->susp_lock); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci for (;;) { 40262306a36Sopenharmony_ci urb = usb_get_from_anchor(&portdata->delayed); 40362306a36Sopenharmony_ci if (!urb) 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci unbusy_queued_urb(urb, portdata); 40662306a36Sopenharmony_ci usb_autopm_put_interface_async(serial->interface); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci for (i = 0; i < N_IN_URB; i++) 41062306a36Sopenharmony_ci usb_kill_urb(portdata->in_urbs[i]); 41162306a36Sopenharmony_ci for (i = 0; i < N_OUT_URB; i++) 41262306a36Sopenharmony_ci usb_kill_urb(portdata->out_urbs[i]); 41362306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci usb_autopm_get_interface_no_resume(serial->interface); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_close); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, 42062306a36Sopenharmony_ci int endpoint, 42162306a36Sopenharmony_ci int dir, void *ctx, char *buf, int len, 42262306a36Sopenharmony_ci void (*callback) (struct urb *)) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 42562306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); 42662306a36Sopenharmony_ci struct urb *urb; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ 42962306a36Sopenharmony_ci if (!urb) 43062306a36Sopenharmony_ci return NULL; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci usb_fill_bulk_urb(urb, serial->dev, 43362306a36Sopenharmony_ci usb_sndbulkpipe(serial->dev, endpoint) | dir, 43462306a36Sopenharmony_ci buf, len, callback, ctx); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (intfdata->use_zlp && dir == USB_DIR_OUT) 43762306a36Sopenharmony_ci urb->transfer_flags |= URB_ZERO_PACKET; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return urb; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ciint usb_wwan_port_probe(struct usb_serial_port *port) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 44562306a36Sopenharmony_ci struct urb *urb; 44662306a36Sopenharmony_ci u8 *buffer; 44762306a36Sopenharmony_ci int i; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!port->bulk_in_size || !port->bulk_out_size) 45062306a36Sopenharmony_ci return -ENODEV; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); 45362306a36Sopenharmony_ci if (!portdata) 45462306a36Sopenharmony_ci return -ENOMEM; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci init_usb_anchor(&portdata->delayed); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci for (i = 0; i < N_IN_URB; i++) { 45962306a36Sopenharmony_ci buffer = (u8 *)__get_free_page(GFP_KERNEL); 46062306a36Sopenharmony_ci if (!buffer) 46162306a36Sopenharmony_ci goto bail_out_error; 46262306a36Sopenharmony_ci portdata->in_buffer[i] = buffer; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress, 46562306a36Sopenharmony_ci USB_DIR_IN, port, 46662306a36Sopenharmony_ci buffer, IN_BUFLEN, 46762306a36Sopenharmony_ci usb_wwan_indat_callback); 46862306a36Sopenharmony_ci portdata->in_urbs[i] = urb; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci for (i = 0; i < N_OUT_URB; i++) { 47262306a36Sopenharmony_ci buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); 47362306a36Sopenharmony_ci if (!buffer) 47462306a36Sopenharmony_ci goto bail_out_error2; 47562306a36Sopenharmony_ci portdata->out_buffer[i] = buffer; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress, 47862306a36Sopenharmony_ci USB_DIR_OUT, port, 47962306a36Sopenharmony_ci buffer, OUT_BUFLEN, 48062306a36Sopenharmony_ci usb_wwan_outdat_callback); 48162306a36Sopenharmony_ci portdata->out_urbs[i] = urb; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci usb_set_serial_port_data(port, portdata); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cibail_out_error2: 48962306a36Sopenharmony_ci for (i = 0; i < N_OUT_URB; i++) { 49062306a36Sopenharmony_ci usb_free_urb(portdata->out_urbs[i]); 49162306a36Sopenharmony_ci kfree(portdata->out_buffer[i]); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_cibail_out_error: 49462306a36Sopenharmony_ci for (i = 0; i < N_IN_URB; i++) { 49562306a36Sopenharmony_ci usb_free_urb(portdata->in_urbs[i]); 49662306a36Sopenharmony_ci free_page((unsigned long)portdata->in_buffer[i]); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci kfree(portdata); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return -ENOMEM; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_wwan_port_probe); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_civoid usb_wwan_port_remove(struct usb_serial_port *port) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci int i; 50762306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 51062306a36Sopenharmony_ci usb_set_serial_port_data(port, NULL); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci for (i = 0; i < N_IN_URB; i++) { 51362306a36Sopenharmony_ci usb_free_urb(portdata->in_urbs[i]); 51462306a36Sopenharmony_ci free_page((unsigned long)portdata->in_buffer[i]); 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci for (i = 0; i < N_OUT_URB; i++) { 51762306a36Sopenharmony_ci usb_free_urb(portdata->out_urbs[i]); 51862306a36Sopenharmony_ci kfree(portdata->out_buffer[i]); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci kfree(portdata); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_port_remove); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci#ifdef CONFIG_PM 52662306a36Sopenharmony_cistatic void stop_urbs(struct usb_serial *serial) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci int i, j; 52962306a36Sopenharmony_ci struct usb_serial_port *port; 53062306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci for (i = 0; i < serial->num_ports; ++i) { 53362306a36Sopenharmony_ci port = serial->port[i]; 53462306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 53562306a36Sopenharmony_ci if (!portdata) 53662306a36Sopenharmony_ci continue; 53762306a36Sopenharmony_ci for (j = 0; j < N_IN_URB; j++) 53862306a36Sopenharmony_ci usb_kill_urb(portdata->in_urbs[j]); 53962306a36Sopenharmony_ci for (j = 0; j < N_OUT_URB; j++) 54062306a36Sopenharmony_ci usb_kill_urb(portdata->out_urbs[j]); 54162306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ciint usb_wwan_suspend(struct usb_serial *serial, pm_message_t message) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci spin_lock_irq(&intfdata->susp_lock); 55062306a36Sopenharmony_ci if (PMSG_IS_AUTO(message)) { 55162306a36Sopenharmony_ci if (intfdata->in_flight) { 55262306a36Sopenharmony_ci spin_unlock_irq(&intfdata->susp_lock); 55362306a36Sopenharmony_ci return -EBUSY; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci intfdata->suspended = 1; 55762306a36Sopenharmony_ci spin_unlock_irq(&intfdata->susp_lock); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci stop_urbs(serial); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_suspend); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* Caller must hold susp_lock. */ 56662306a36Sopenharmony_cistatic int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 56962306a36Sopenharmony_ci struct usb_wwan_intf_private *data = usb_get_serial_data(serial); 57062306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 57162306a36Sopenharmony_ci struct urb *urb; 57262306a36Sopenharmony_ci int err_count = 0; 57362306a36Sopenharmony_ci int err; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci for (;;) { 57862306a36Sopenharmony_ci urb = usb_get_from_anchor(&portdata->delayed); 57962306a36Sopenharmony_ci if (!urb) 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 58362306a36Sopenharmony_ci if (err) { 58462306a36Sopenharmony_ci dev_err(&port->dev, "%s: submit urb failed: %d\n", 58562306a36Sopenharmony_ci __func__, err); 58662306a36Sopenharmony_ci err_count++; 58762306a36Sopenharmony_ci unbusy_queued_urb(urb, portdata); 58862306a36Sopenharmony_ci usb_autopm_put_interface_async(serial->interface); 58962306a36Sopenharmony_ci continue; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci data->in_flight++; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (err_count) 59562306a36Sopenharmony_ci return -EIO; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ciint usb_wwan_resume(struct usb_serial *serial) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci int i, j; 60362306a36Sopenharmony_ci struct usb_serial_port *port; 60462306a36Sopenharmony_ci struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); 60562306a36Sopenharmony_ci struct usb_wwan_port_private *portdata; 60662306a36Sopenharmony_ci struct urb *urb; 60762306a36Sopenharmony_ci int err; 60862306a36Sopenharmony_ci int err_count = 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci spin_lock_irq(&intfdata->susp_lock); 61162306a36Sopenharmony_ci for (i = 0; i < serial->num_ports; i++) { 61262306a36Sopenharmony_ci port = serial->port[i]; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (!tty_port_initialized(&port->port)) 61562306a36Sopenharmony_ci continue; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci portdata = usb_get_serial_port_data(port); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (port->interrupt_in_urb) { 62062306a36Sopenharmony_ci err = usb_submit_urb(port->interrupt_in_urb, 62162306a36Sopenharmony_ci GFP_ATOMIC); 62262306a36Sopenharmony_ci if (err) { 62362306a36Sopenharmony_ci dev_err(&port->dev, 62462306a36Sopenharmony_ci "%s: submit int urb failed: %d\n", 62562306a36Sopenharmony_ci __func__, err); 62662306a36Sopenharmony_ci err_count++; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci err = usb_wwan_submit_delayed_urbs(port); 63162306a36Sopenharmony_ci if (err) 63262306a36Sopenharmony_ci err_count++; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci for (j = 0; j < N_IN_URB; j++) { 63562306a36Sopenharmony_ci urb = portdata->in_urbs[j]; 63662306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 63762306a36Sopenharmony_ci if (err < 0) { 63862306a36Sopenharmony_ci dev_err(&port->dev, 63962306a36Sopenharmony_ci "%s: submit read urb %d failed: %d\n", 64062306a36Sopenharmony_ci __func__, i, err); 64162306a36Sopenharmony_ci err_count++; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci intfdata->suspended = 0; 64662306a36Sopenharmony_ci spin_unlock_irq(&intfdata->susp_lock); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (err_count) 64962306a36Sopenharmony_ci return -EIO; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_resume); 65462306a36Sopenharmony_ci#endif 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 65762306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 65862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 659