18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci  USB Driver layer for GSM modems
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci  Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci  Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci  History: see the git log.
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci  Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci  This driver exists because the "normal" serial driver doesn't work too well
148c2ecf20Sopenharmony_ci  with GSM modems. Issues:
158c2ecf20Sopenharmony_ci  - data loss -- one single Receive URB is not nearly enough
168c2ecf20Sopenharmony_ci  - controlling the baud rate doesn't make sense
178c2ecf20Sopenharmony_ci*/
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
208c2ecf20Sopenharmony_ci#define DRIVER_DESC "USB Driver for GSM modems"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/kernel.h>
238c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
248c2ecf20Sopenharmony_ci#include <linux/errno.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/tty.h>
278c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/bitops.h>
308c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
318c2ecf20Sopenharmony_ci#include <linux/usb.h>
328c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
338c2ecf20Sopenharmony_ci#include <linux/serial.h>
348c2ecf20Sopenharmony_ci#include "usb-wwan.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * Generate DTR/RTS signals on the port using the SET_CONTROL_LINE_STATE request
388c2ecf20Sopenharmony_ci * in CDC ACM.
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_cistatic int usb_wwan_send_setup(struct usb_serial_port *port)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
438c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
448c2ecf20Sopenharmony_ci	int val = 0;
458c2ecf20Sopenharmony_ci	int ifnum;
468c2ecf20Sopenharmony_ci	int res;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (portdata->dtr_state)
518c2ecf20Sopenharmony_ci		val |= 0x01;
528c2ecf20Sopenharmony_ci	if (portdata->rts_state)
538c2ecf20Sopenharmony_ci		val |= 0x02;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	res = usb_autopm_get_interface(serial->interface);
588c2ecf20Sopenharmony_ci	if (res)
598c2ecf20Sopenharmony_ci		return res;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
628c2ecf20Sopenharmony_ci				0x22, 0x21, val, ifnum, NULL, 0,
638c2ecf20Sopenharmony_ci				USB_CTRL_SET_TIMEOUT);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	usb_autopm_put_interface(port->serial->interface);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return res;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_civoid usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
738c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	intfdata = usb_get_serial_data(port->serial);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!intfdata->use_send_setup)
788c2ecf20Sopenharmony_ci		return;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
818c2ecf20Sopenharmony_ci	/* FIXME: locking */
828c2ecf20Sopenharmony_ci	portdata->rts_state = on;
838c2ecf20Sopenharmony_ci	portdata->dtr_state = on;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	usb_wwan_send_setup(port);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_dtr_rts);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciint usb_wwan_tiocmget(struct tty_struct *tty)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
928c2ecf20Sopenharmony_ci	unsigned int value;
938c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
988c2ecf20Sopenharmony_ci	    ((portdata->dtr_state) ? TIOCM_DTR : 0) |
998c2ecf20Sopenharmony_ci	    ((portdata->cts_state) ? TIOCM_CTS : 0) |
1008c2ecf20Sopenharmony_ci	    ((portdata->dsr_state) ? TIOCM_DSR : 0) |
1018c2ecf20Sopenharmony_ci	    ((portdata->dcd_state) ? TIOCM_CAR : 0) |
1028c2ecf20Sopenharmony_ci	    ((portdata->ri_state) ? TIOCM_RNG : 0);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return value;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_tiocmget);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ciint usb_wwan_tiocmset(struct tty_struct *tty,
1098c2ecf20Sopenharmony_ci		      unsigned int set, unsigned int clear)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
1128c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
1138c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
1168c2ecf20Sopenharmony_ci	intfdata = usb_get_serial_data(port->serial);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (!intfdata->use_send_setup)
1198c2ecf20Sopenharmony_ci		return -EINVAL;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* FIXME: what locks portdata fields ? */
1228c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
1238c2ecf20Sopenharmony_ci		portdata->rts_state = 1;
1248c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
1258c2ecf20Sopenharmony_ci		portdata->dtr_state = 1;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
1288c2ecf20Sopenharmony_ci		portdata->rts_state = 0;
1298c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
1308c2ecf20Sopenharmony_ci		portdata->dtr_state = 0;
1318c2ecf20Sopenharmony_ci	return usb_wwan_send_setup(port);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_tiocmset);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciint usb_wwan_get_serial_info(struct tty_struct *tty,
1368c2ecf20Sopenharmony_ci			   struct serial_struct *ss)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	ss->line            = port->minor;
1418c2ecf20Sopenharmony_ci	ss->port            = port->port_number;
1428c2ecf20Sopenharmony_ci	ss->baud_base       = tty_get_baud_rate(port->port.tty);
1438c2ecf20Sopenharmony_ci	ss->close_delay	    = jiffies_to_msecs(port->port.close_delay) / 10;
1448c2ecf20Sopenharmony_ci	ss->closing_wait    = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
1458c2ecf20Sopenharmony_ci				 ASYNC_CLOSING_WAIT_NONE :
1468c2ecf20Sopenharmony_ci				 jiffies_to_msecs(port->port.closing_wait) / 10;
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_get_serial_info);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ciint usb_wwan_set_serial_info(struct tty_struct *tty,
1528c2ecf20Sopenharmony_ci			   struct serial_struct *ss)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
1558c2ecf20Sopenharmony_ci	unsigned int closing_wait, close_delay;
1568c2ecf20Sopenharmony_ci	int retval = 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	close_delay = msecs_to_jiffies(ss->close_delay * 10);
1598c2ecf20Sopenharmony_ci	closing_wait = ss->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
1608c2ecf20Sopenharmony_ci			ASYNC_CLOSING_WAIT_NONE :
1618c2ecf20Sopenharmony_ci			msecs_to_jiffies(ss->closing_wait * 10);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	mutex_lock(&port->port.mutex);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN)) {
1668c2ecf20Sopenharmony_ci		if ((close_delay != port->port.close_delay) ||
1678c2ecf20Sopenharmony_ci		    (closing_wait != port->port.closing_wait))
1688c2ecf20Sopenharmony_ci			retval = -EPERM;
1698c2ecf20Sopenharmony_ci		else
1708c2ecf20Sopenharmony_ci			retval = -EOPNOTSUPP;
1718c2ecf20Sopenharmony_ci	} else {
1728c2ecf20Sopenharmony_ci		port->port.close_delay  = close_delay;
1738c2ecf20Sopenharmony_ci		port->port.closing_wait = closing_wait;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	mutex_unlock(&port->port.mutex);
1778c2ecf20Sopenharmony_ci	return retval;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_set_serial_info);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ciint usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
1828c2ecf20Sopenharmony_ci		   const unsigned char *buf, int count)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
1858c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata;
1868c2ecf20Sopenharmony_ci	int i;
1878c2ecf20Sopenharmony_ci	int left, todo;
1888c2ecf20Sopenharmony_ci	struct urb *this_urb = NULL;	/* spurious */
1898c2ecf20Sopenharmony_ci	int err;
1908c2ecf20Sopenharmony_ci	unsigned long flags;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
1938c2ecf20Sopenharmony_ci	intfdata = usb_get_serial_data(port->serial);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	i = 0;
1988c2ecf20Sopenharmony_ci	left = count;
1998c2ecf20Sopenharmony_ci	for (i = 0; left > 0 && i < N_OUT_URB; i++) {
2008c2ecf20Sopenharmony_ci		todo = left;
2018c2ecf20Sopenharmony_ci		if (todo > OUT_BUFLEN)
2028c2ecf20Sopenharmony_ci			todo = OUT_BUFLEN;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		this_urb = portdata->out_urbs[i];
2058c2ecf20Sopenharmony_ci		if (test_and_set_bit(i, &portdata->out_busy)) {
2068c2ecf20Sopenharmony_ci			if (time_before(jiffies,
2078c2ecf20Sopenharmony_ci					portdata->tx_start_time[i] + 10 * HZ))
2088c2ecf20Sopenharmony_ci				continue;
2098c2ecf20Sopenharmony_ci			usb_unlink_urb(this_urb);
2108c2ecf20Sopenharmony_ci			continue;
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __func__,
2138c2ecf20Sopenharmony_ci			usb_pipeendpoint(this_urb->pipe), i);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		err = usb_autopm_get_interface_async(port->serial->interface);
2168c2ecf20Sopenharmony_ci		if (err < 0) {
2178c2ecf20Sopenharmony_ci			clear_bit(i, &portdata->out_busy);
2188c2ecf20Sopenharmony_ci			break;
2198c2ecf20Sopenharmony_ci		}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		/* send the data */
2228c2ecf20Sopenharmony_ci		memcpy(this_urb->transfer_buffer, buf, todo);
2238c2ecf20Sopenharmony_ci		this_urb->transfer_buffer_length = todo;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intfdata->susp_lock, flags);
2268c2ecf20Sopenharmony_ci		if (intfdata->suspended) {
2278c2ecf20Sopenharmony_ci			usb_anchor_urb(this_urb, &portdata->delayed);
2288c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
2298c2ecf20Sopenharmony_ci		} else {
2308c2ecf20Sopenharmony_ci			intfdata->in_flight++;
2318c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
2328c2ecf20Sopenharmony_ci			err = usb_submit_urb(this_urb, GFP_ATOMIC);
2338c2ecf20Sopenharmony_ci			if (err) {
2348c2ecf20Sopenharmony_ci				dev_err(&port->dev,
2358c2ecf20Sopenharmony_ci					"%s: submit urb %d failed: %d\n",
2368c2ecf20Sopenharmony_ci					__func__, i, err);
2378c2ecf20Sopenharmony_ci				clear_bit(i, &portdata->out_busy);
2388c2ecf20Sopenharmony_ci				spin_lock_irqsave(&intfdata->susp_lock, flags);
2398c2ecf20Sopenharmony_ci				intfdata->in_flight--;
2408c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&intfdata->susp_lock,
2418c2ecf20Sopenharmony_ci						       flags);
2428c2ecf20Sopenharmony_ci				usb_autopm_put_interface_async(port->serial->interface);
2438c2ecf20Sopenharmony_ci				break;
2448c2ecf20Sopenharmony_ci			}
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		portdata->tx_start_time[i] = jiffies;
2488c2ecf20Sopenharmony_ci		buf += todo;
2498c2ecf20Sopenharmony_ci		left -= todo;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	count -= left;
2538c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s: wrote (did %d)\n", __func__, count);
2548c2ecf20Sopenharmony_ci	return count;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_write);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void usb_wwan_indat_callback(struct urb *urb)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	int err;
2618c2ecf20Sopenharmony_ci	int endpoint;
2628c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
2638c2ecf20Sopenharmony_ci	struct device *dev;
2648c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
2658c2ecf20Sopenharmony_ci	int status = urb->status;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	endpoint = usb_pipeendpoint(urb->pipe);
2688c2ecf20Sopenharmony_ci	port = urb->context;
2698c2ecf20Sopenharmony_ci	dev = &port->dev;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (status) {
2728c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n",
2738c2ecf20Sopenharmony_ci			__func__, status, endpoint);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		/* don't resubmit on fatal errors */
2768c2ecf20Sopenharmony_ci		if (status == -ESHUTDOWN || status == -ENOENT)
2778c2ecf20Sopenharmony_ci			return;
2788c2ecf20Sopenharmony_ci	} else {
2798c2ecf20Sopenharmony_ci		if (urb->actual_length) {
2808c2ecf20Sopenharmony_ci			tty_insert_flip_string(&port->port, data,
2818c2ecf20Sopenharmony_ci					urb->actual_length);
2828c2ecf20Sopenharmony_ci			tty_flip_buffer_push(&port->port);
2838c2ecf20Sopenharmony_ci		} else
2848c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s: empty read urb received\n", __func__);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci	/* Resubmit urb so we continue receiving */
2878c2ecf20Sopenharmony_ci	err = usb_submit_urb(urb, GFP_ATOMIC);
2888c2ecf20Sopenharmony_ci	if (err) {
2898c2ecf20Sopenharmony_ci		if (err != -EPERM && err != -ENODEV) {
2908c2ecf20Sopenharmony_ci			dev_err(dev, "%s: resubmit read urb failed. (%d)\n",
2918c2ecf20Sopenharmony_ci				__func__, err);
2928c2ecf20Sopenharmony_ci			/* busy also in error unless we are killed */
2938c2ecf20Sopenharmony_ci			usb_mark_last_busy(port->serial->dev);
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci	} else {
2968c2ecf20Sopenharmony_ci		usb_mark_last_busy(port->serial->dev);
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic void usb_wwan_outdat_callback(struct urb *urb)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
3038c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
3048c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata;
3058c2ecf20Sopenharmony_ci	unsigned long flags;
3068c2ecf20Sopenharmony_ci	int i;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	port = urb->context;
3098c2ecf20Sopenharmony_ci	intfdata = usb_get_serial_data(port->serial);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	usb_serial_port_softint(port);
3128c2ecf20Sopenharmony_ci	usb_autopm_put_interface_async(port->serial->interface);
3138c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
3148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intfdata->susp_lock, flags);
3158c2ecf20Sopenharmony_ci	intfdata->in_flight--;
3168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intfdata->susp_lock, flags);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	for (i = 0; i < N_OUT_URB; ++i) {
3198c2ecf20Sopenharmony_ci		if (portdata->out_urbs[i] == urb) {
3208c2ecf20Sopenharmony_ci			smp_mb__before_atomic();
3218c2ecf20Sopenharmony_ci			clear_bit(i, &portdata->out_busy);
3228c2ecf20Sopenharmony_ci			break;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ciint usb_wwan_write_room(struct tty_struct *tty)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
3308c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
3318c2ecf20Sopenharmony_ci	int i;
3328c2ecf20Sopenharmony_ci	int data_len = 0;
3338c2ecf20Sopenharmony_ci	struct urb *this_urb;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	for (i = 0; i < N_OUT_URB; i++) {
3388c2ecf20Sopenharmony_ci		this_urb = portdata->out_urbs[i];
3398c2ecf20Sopenharmony_ci		if (this_urb && !test_bit(i, &portdata->out_busy))
3408c2ecf20Sopenharmony_ci			data_len += OUT_BUFLEN;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
3448c2ecf20Sopenharmony_ci	return data_len;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_write_room);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ciint usb_wwan_chars_in_buffer(struct tty_struct *tty)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
3518c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
3528c2ecf20Sopenharmony_ci	int i;
3538c2ecf20Sopenharmony_ci	int data_len = 0;
3548c2ecf20Sopenharmony_ci	struct urb *this_urb;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	for (i = 0; i < N_OUT_URB; i++) {
3598c2ecf20Sopenharmony_ci		this_urb = portdata->out_urbs[i];
3608c2ecf20Sopenharmony_ci		/* FIXME: This locking is insufficient as this_urb may
3618c2ecf20Sopenharmony_ci		   go unused during the test */
3628c2ecf20Sopenharmony_ci		if (this_urb && test_bit(i, &portdata->out_busy))
3638c2ecf20Sopenharmony_ci			data_len += this_urb->transfer_buffer_length;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
3668c2ecf20Sopenharmony_ci	return data_len;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_chars_in_buffer);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ciint usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
3738c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata;
3748c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
3758c2ecf20Sopenharmony_ci	int i, err;
3768c2ecf20Sopenharmony_ci	struct urb *urb;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
3798c2ecf20Sopenharmony_ci	intfdata = usb_get_serial_data(serial);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (port->interrupt_in_urb) {
3828c2ecf20Sopenharmony_ci		err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
3838c2ecf20Sopenharmony_ci		if (err) {
3848c2ecf20Sopenharmony_ci			dev_err(&port->dev, "%s: submit int urb failed: %d\n",
3858c2ecf20Sopenharmony_ci				__func__, err);
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Start reading from the IN endpoint */
3908c2ecf20Sopenharmony_ci	for (i = 0; i < N_IN_URB; i++) {
3918c2ecf20Sopenharmony_ci		urb = portdata->in_urbs[i];
3928c2ecf20Sopenharmony_ci		if (!urb)
3938c2ecf20Sopenharmony_ci			continue;
3948c2ecf20Sopenharmony_ci		err = usb_submit_urb(urb, GFP_KERNEL);
3958c2ecf20Sopenharmony_ci		if (err) {
3968c2ecf20Sopenharmony_ci			dev_err(&port->dev,
3978c2ecf20Sopenharmony_ci				"%s: submit read urb %d failed: %d\n",
3988c2ecf20Sopenharmony_ci				__func__, i, err);
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	spin_lock_irq(&intfdata->susp_lock);
4038c2ecf20Sopenharmony_ci	if (++intfdata->open_ports == 1)
4048c2ecf20Sopenharmony_ci		serial->interface->needs_remote_wakeup = 1;
4058c2ecf20Sopenharmony_ci	spin_unlock_irq(&intfdata->susp_lock);
4068c2ecf20Sopenharmony_ci	/* this balances a get in the generic USB serial code */
4078c2ecf20Sopenharmony_ci	usb_autopm_put_interface(serial->interface);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_open);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void unbusy_queued_urb(struct urb *urb,
4148c2ecf20Sopenharmony_ci					struct usb_wwan_port_private *portdata)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	int i;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	for (i = 0; i < N_OUT_URB; i++) {
4198c2ecf20Sopenharmony_ci		if (urb == portdata->out_urbs[i]) {
4208c2ecf20Sopenharmony_ci			clear_bit(i, &portdata->out_busy);
4218c2ecf20Sopenharmony_ci			break;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_civoid usb_wwan_close(struct usb_serial_port *port)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	int i;
4298c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
4308c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
4318c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
4328c2ecf20Sopenharmony_ci	struct urb *urb;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	/*
4378c2ecf20Sopenharmony_ci	 * Need to take susp_lock to make sure port is not already being
4388c2ecf20Sopenharmony_ci	 * resumed, but no need to hold it due to the tty-port initialized
4398c2ecf20Sopenharmony_ci	 * flag.
4408c2ecf20Sopenharmony_ci	 */
4418c2ecf20Sopenharmony_ci	spin_lock_irq(&intfdata->susp_lock);
4428c2ecf20Sopenharmony_ci	if (--intfdata->open_ports == 0)
4438c2ecf20Sopenharmony_ci		serial->interface->needs_remote_wakeup = 0;
4448c2ecf20Sopenharmony_ci	spin_unlock_irq(&intfdata->susp_lock);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	for (;;) {
4478c2ecf20Sopenharmony_ci		urb = usb_get_from_anchor(&portdata->delayed);
4488c2ecf20Sopenharmony_ci		if (!urb)
4498c2ecf20Sopenharmony_ci			break;
4508c2ecf20Sopenharmony_ci		unbusy_queued_urb(urb, portdata);
4518c2ecf20Sopenharmony_ci		usb_autopm_put_interface_async(serial->interface);
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	for (i = 0; i < N_IN_URB; i++)
4558c2ecf20Sopenharmony_ci		usb_kill_urb(portdata->in_urbs[i]);
4568c2ecf20Sopenharmony_ci	for (i = 0; i < N_OUT_URB; i++)
4578c2ecf20Sopenharmony_ci		usb_kill_urb(portdata->out_urbs[i]);
4588c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	usb_autopm_get_interface_no_resume(serial->interface);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_close);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
4658c2ecf20Sopenharmony_ci				      int endpoint,
4668c2ecf20Sopenharmony_ci				      int dir, void *ctx, char *buf, int len,
4678c2ecf20Sopenharmony_ci				      void (*callback) (struct urb *))
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
4708c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
4718c2ecf20Sopenharmony_ci	struct urb *urb;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_KERNEL);	/* No ISO */
4748c2ecf20Sopenharmony_ci	if (!urb)
4758c2ecf20Sopenharmony_ci		return NULL;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, serial->dev,
4788c2ecf20Sopenharmony_ci			  usb_sndbulkpipe(serial->dev, endpoint) | dir,
4798c2ecf20Sopenharmony_ci			  buf, len, callback, ctx);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (intfdata->use_zlp && dir == USB_DIR_OUT)
4828c2ecf20Sopenharmony_ci		urb->transfer_flags |= URB_ZERO_PACKET;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	return urb;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ciint usb_wwan_port_probe(struct usb_serial_port *port)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
4908c2ecf20Sopenharmony_ci	struct urb *urb;
4918c2ecf20Sopenharmony_ci	u8 *buffer;
4928c2ecf20Sopenharmony_ci	int i;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (!port->bulk_in_size || !port->bulk_out_size)
4958c2ecf20Sopenharmony_ci		return -ENODEV;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
4988c2ecf20Sopenharmony_ci	if (!portdata)
4998c2ecf20Sopenharmony_ci		return -ENOMEM;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	init_usb_anchor(&portdata->delayed);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	for (i = 0; i < N_IN_URB; i++) {
5048c2ecf20Sopenharmony_ci		buffer = (u8 *)__get_free_page(GFP_KERNEL);
5058c2ecf20Sopenharmony_ci		if (!buffer)
5068c2ecf20Sopenharmony_ci			goto bail_out_error;
5078c2ecf20Sopenharmony_ci		portdata->in_buffer[i] = buffer;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress,
5108c2ecf20Sopenharmony_ci						USB_DIR_IN, port,
5118c2ecf20Sopenharmony_ci						buffer, IN_BUFLEN,
5128c2ecf20Sopenharmony_ci						usb_wwan_indat_callback);
5138c2ecf20Sopenharmony_ci		portdata->in_urbs[i] = urb;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	for (i = 0; i < N_OUT_URB; i++) {
5178c2ecf20Sopenharmony_ci		buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
5188c2ecf20Sopenharmony_ci		if (!buffer)
5198c2ecf20Sopenharmony_ci			goto bail_out_error2;
5208c2ecf20Sopenharmony_ci		portdata->out_buffer[i] = buffer;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress,
5238c2ecf20Sopenharmony_ci						USB_DIR_OUT, port,
5248c2ecf20Sopenharmony_ci						buffer, OUT_BUFLEN,
5258c2ecf20Sopenharmony_ci						usb_wwan_outdat_callback);
5268c2ecf20Sopenharmony_ci		portdata->out_urbs[i] = urb;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, portdata);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	return 0;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cibail_out_error2:
5348c2ecf20Sopenharmony_ci	for (i = 0; i < N_OUT_URB; i++) {
5358c2ecf20Sopenharmony_ci		usb_free_urb(portdata->out_urbs[i]);
5368c2ecf20Sopenharmony_ci		kfree(portdata->out_buffer[i]);
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_cibail_out_error:
5398c2ecf20Sopenharmony_ci	for (i = 0; i < N_IN_URB; i++) {
5408c2ecf20Sopenharmony_ci		usb_free_urb(portdata->in_urbs[i]);
5418c2ecf20Sopenharmony_ci		free_page((unsigned long)portdata->in_buffer[i]);
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci	kfree(portdata);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	return -ENOMEM;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_wwan_port_probe);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ciint usb_wwan_port_remove(struct usb_serial_port *port)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	int i;
5528c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
5558c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, NULL);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	for (i = 0; i < N_IN_URB; i++) {
5588c2ecf20Sopenharmony_ci		usb_free_urb(portdata->in_urbs[i]);
5598c2ecf20Sopenharmony_ci		free_page((unsigned long)portdata->in_buffer[i]);
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci	for (i = 0; i < N_OUT_URB; i++) {
5628c2ecf20Sopenharmony_ci		usb_free_urb(portdata->out_urbs[i]);
5638c2ecf20Sopenharmony_ci		kfree(portdata->out_buffer[i]);
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	kfree(portdata);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_port_remove);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
5738c2ecf20Sopenharmony_cistatic void stop_urbs(struct usb_serial *serial)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	int i, j;
5768c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
5778c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	for (i = 0; i < serial->num_ports; ++i) {
5808c2ecf20Sopenharmony_ci		port = serial->port[i];
5818c2ecf20Sopenharmony_ci		portdata = usb_get_serial_port_data(port);
5828c2ecf20Sopenharmony_ci		if (!portdata)
5838c2ecf20Sopenharmony_ci			continue;
5848c2ecf20Sopenharmony_ci		for (j = 0; j < N_IN_URB; j++)
5858c2ecf20Sopenharmony_ci			usb_kill_urb(portdata->in_urbs[j]);
5868c2ecf20Sopenharmony_ci		for (j = 0; j < N_OUT_URB; j++)
5878c2ecf20Sopenharmony_ci			usb_kill_urb(portdata->out_urbs[j]);
5888c2ecf20Sopenharmony_ci		usb_kill_urb(port->interrupt_in_urb);
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ciint usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	spin_lock_irq(&intfdata->susp_lock);
5978c2ecf20Sopenharmony_ci	if (PMSG_IS_AUTO(message)) {
5988c2ecf20Sopenharmony_ci		if (intfdata->in_flight) {
5998c2ecf20Sopenharmony_ci			spin_unlock_irq(&intfdata->susp_lock);
6008c2ecf20Sopenharmony_ci			return -EBUSY;
6018c2ecf20Sopenharmony_ci		}
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci	intfdata->suspended = 1;
6048c2ecf20Sopenharmony_ci	spin_unlock_irq(&intfdata->susp_lock);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	stop_urbs(serial);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	return 0;
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_suspend);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/* Caller must hold susp_lock. */
6138c2ecf20Sopenharmony_cistatic int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
6168c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
6178c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
6188c2ecf20Sopenharmony_ci	struct urb *urb;
6198c2ecf20Sopenharmony_ci	int err_count = 0;
6208c2ecf20Sopenharmony_ci	int err;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	portdata = usb_get_serial_port_data(port);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	for (;;) {
6258c2ecf20Sopenharmony_ci		urb = usb_get_from_anchor(&portdata->delayed);
6268c2ecf20Sopenharmony_ci		if (!urb)
6278c2ecf20Sopenharmony_ci			break;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		err = usb_submit_urb(urb, GFP_ATOMIC);
6308c2ecf20Sopenharmony_ci		if (err) {
6318c2ecf20Sopenharmony_ci			dev_err(&port->dev, "%s: submit urb failed: %d\n",
6328c2ecf20Sopenharmony_ci					__func__, err);
6338c2ecf20Sopenharmony_ci			err_count++;
6348c2ecf20Sopenharmony_ci			unbusy_queued_urb(urb, portdata);
6358c2ecf20Sopenharmony_ci			usb_autopm_put_interface_async(serial->interface);
6368c2ecf20Sopenharmony_ci			continue;
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci		data->in_flight++;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (err_count)
6428c2ecf20Sopenharmony_ci		return -EIO;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	return 0;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ciint usb_wwan_resume(struct usb_serial *serial)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	int i, j;
6508c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
6518c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
6528c2ecf20Sopenharmony_ci	struct usb_wwan_port_private *portdata;
6538c2ecf20Sopenharmony_ci	struct urb *urb;
6548c2ecf20Sopenharmony_ci	int err;
6558c2ecf20Sopenharmony_ci	int err_count = 0;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	spin_lock_irq(&intfdata->susp_lock);
6588c2ecf20Sopenharmony_ci	for (i = 0; i < serial->num_ports; i++) {
6598c2ecf20Sopenharmony_ci		port = serial->port[i];
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		if (!tty_port_initialized(&port->port))
6628c2ecf20Sopenharmony_ci			continue;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci		portdata = usb_get_serial_port_data(port);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		if (port->interrupt_in_urb) {
6678c2ecf20Sopenharmony_ci			err = usb_submit_urb(port->interrupt_in_urb,
6688c2ecf20Sopenharmony_ci					GFP_ATOMIC);
6698c2ecf20Sopenharmony_ci			if (err) {
6708c2ecf20Sopenharmony_ci				dev_err(&port->dev,
6718c2ecf20Sopenharmony_ci					"%s: submit int urb failed: %d\n",
6728c2ecf20Sopenharmony_ci					__func__, err);
6738c2ecf20Sopenharmony_ci				err_count++;
6748c2ecf20Sopenharmony_ci			}
6758c2ecf20Sopenharmony_ci		}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		err = usb_wwan_submit_delayed_urbs(port);
6788c2ecf20Sopenharmony_ci		if (err)
6798c2ecf20Sopenharmony_ci			err_count++;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		for (j = 0; j < N_IN_URB; j++) {
6828c2ecf20Sopenharmony_ci			urb = portdata->in_urbs[j];
6838c2ecf20Sopenharmony_ci			err = usb_submit_urb(urb, GFP_ATOMIC);
6848c2ecf20Sopenharmony_ci			if (err < 0) {
6858c2ecf20Sopenharmony_ci				dev_err(&port->dev,
6868c2ecf20Sopenharmony_ci					"%s: submit read urb %d failed: %d\n",
6878c2ecf20Sopenharmony_ci					__func__, i, err);
6888c2ecf20Sopenharmony_ci				err_count++;
6898c2ecf20Sopenharmony_ci			}
6908c2ecf20Sopenharmony_ci		}
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci	intfdata->suspended = 0;
6938c2ecf20Sopenharmony_ci	spin_unlock_irq(&intfdata->susp_lock);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (err_count)
6968c2ecf20Sopenharmony_ci		return -EIO;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return 0;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(usb_wwan_resume);
7018c2ecf20Sopenharmony_ci#endif
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
7048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
7058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
706