xref: /openbmc/linux/drivers/usb/serial/sierra.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
269de51fdSKevin Lloyd /*
3033a3fb9SKevin Lloyd   USB Driver for Sierra Wireless
4033a3fb9SKevin Lloyd 
540d2ff32SElina Pasheva   Copyright (C) 2006, 2007, 2008  Kevin Lloyd <klloyd@sierrawireless.com>,
640d2ff32SElina Pasheva 
740d2ff32SElina Pasheva   Copyright (C) 2008, 2009  Elina Pasheva, Matthew Safar, Rory Filer
840d2ff32SElina Pasheva 			<linux@sierrawireless.com>
9033a3fb9SKevin Lloyd 
10033a3fb9SKevin Lloyd   IMPORTANT DISCLAIMER: This driver is not commercially supported by
11033a3fb9SKevin Lloyd   Sierra Wireless. Use at your own risk.
12033a3fb9SKevin Lloyd 
13033a3fb9SKevin Lloyd   Portions based on the option driver by Matthias Urlichs <smurf@smurf.noris.de>
14033a3fb9SKevin Lloyd   Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org>
1569de51fdSKevin Lloyd */
163a2b808eSElina Pasheva /* Uncomment to log function calls */
173a2b808eSElina Pasheva /* #define DEBUG */
18bcbec053SJohan Hovold 
1940d2ff32SElina Pasheva #define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer"
20033a3fb9SKevin Lloyd #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
21033a3fb9SKevin Lloyd 
2269de51fdSKevin Lloyd #include <linux/kernel.h>
23033a3fb9SKevin Lloyd #include <linux/jiffies.h>
24033a3fb9SKevin Lloyd #include <linux/errno.h>
2569de51fdSKevin Lloyd #include <linux/tty.h>
265a0e3ad6STejun Heo #include <linux/slab.h>
27033a3fb9SKevin Lloyd #include <linux/tty_flip.h>
2869de51fdSKevin Lloyd #include <linux/module.h>
2969de51fdSKevin Lloyd #include <linux/usb.h>
30a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h>
3169de51fdSKevin Lloyd 
32228426edSKevin Lloyd #define SWIMS_USB_REQUEST_SetPower	0x00
33228426edSKevin Lloyd #define SWIMS_USB_REQUEST_SetNmea	0x07
34112225b1SKevin Lloyd 
353a2b808eSElina Pasheva #define N_IN_URB_HM	8
363a2b808eSElina Pasheva #define N_OUT_URB_HM	64
373a2b808eSElina Pasheva #define N_IN_URB	4
383a2b808eSElina Pasheva #define N_OUT_URB	4
39112225b1SKevin Lloyd #define IN_BUFLEN	4096
40112225b1SKevin Lloyd 
4140d2ff32SElina Pasheva #define MAX_TRANSFER		(PAGE_SIZE - 512)
4240d2ff32SElina Pasheva /* MAX_TRANSFER is chosen so that the VM is not stressed by
4340d2ff32SElina Pasheva    allocations > PAGE_SIZE and the number of packets in a page
4440d2ff32SElina Pasheva    is an integer 512 is the largest possible packet on EHCI */
4540d2ff32SElina Pasheva 
4690ab5ee9SRusty Russell static bool nmea;
47112225b1SKevin Lloyd 
48e0439cd9SJohan Hovold struct sierra_iface_list {
49e0439cd9SJohan Hovold 	const u8 *nums;		/* array of interface numbers */
50e0439cd9SJohan Hovold 	size_t count;		/* number of elements in array */
514db2299dSElina Pasheva };
524db2299dSElina Pasheva 
53e6929a90SOliver Neukum struct sierra_intf_private {
54e6929a90SOliver Neukum 	spinlock_t susp_lock;
55e6929a90SOliver Neukum 	unsigned int suspended:1;
56e6929a90SOliver Neukum 	int in_flight;
5780cc0fcbSJohan Hovold 	unsigned int open_ports;
58e6929a90SOliver Neukum };
59e6929a90SOliver Neukum 
sierra_set_power_state(struct usb_device * udev,__u16 swiState)6082a24e68SAdrian Bunk static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
61112225b1SKevin Lloyd {
6264318a52SGreg Kroah-Hartman 	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
63228426edSKevin Lloyd 			SWIMS_USB_REQUEST_SetPower,	/* __u8 request      */
64f3564de4SKevin Lloyd 			USB_TYPE_VENDOR,		/* __u8 request type */
65112225b1SKevin Lloyd 			swiState,			/* __u16 value       */
66112225b1SKevin Lloyd 			0,				/* __u16 index       */
67112225b1SKevin Lloyd 			NULL,				/* void *data        */
68112225b1SKevin Lloyd 			0,				/* __u16 size 	     */
69112225b1SKevin Lloyd 			USB_CTRL_SET_TIMEOUT);		/* int timeout 	     */
70112225b1SKevin Lloyd }
71112225b1SKevin Lloyd 
sierra_vsc_set_nmea(struct usb_device * udev,__u16 enable)72228426edSKevin Lloyd static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable)
73112225b1SKevin Lloyd {
7464318a52SGreg Kroah-Hartman 	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
75228426edSKevin Lloyd 			SWIMS_USB_REQUEST_SetNmea,	/* __u8 request      */
76f3564de4SKevin Lloyd 			USB_TYPE_VENDOR,		/* __u8 request type */
77228426edSKevin Lloyd 			enable,				/* __u16 value       */
78228426edSKevin Lloyd 			0x0000,				/* __u16 index       */
79228426edSKevin Lloyd 			NULL,				/* void *data        */
80228426edSKevin Lloyd 			0,				/* __u16 size 	     */
81228426edSKevin Lloyd 			USB_CTRL_SET_TIMEOUT);		/* int timeout       */
82112225b1SKevin Lloyd }
83112225b1SKevin Lloyd 
sierra_calc_num_ports(struct usb_serial * serial,struct usb_serial_endpoints * epds)8407814246SJohan Hovold static int sierra_calc_num_ports(struct usb_serial *serial,
8507814246SJohan Hovold 					struct usb_serial_endpoints *epds)
86228426edSKevin Lloyd {
879636b683SElina Pasheva 	int num_ports = 0;
889636b683SElina Pasheva 	u8 ifnum, numendpoints;
899636b683SElina Pasheva 
909636b683SElina Pasheva 	ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
919636b683SElina Pasheva 	numendpoints = serial->interface->cur_altsetting->desc.bNumEndpoints;
92228426edSKevin Lloyd 
939636b683SElina Pasheva 	/* Dummy interface present on some SKUs should be ignored */
949636b683SElina Pasheva 	if (ifnum == 0x99)
959636b683SElina Pasheva 		num_ports = 0;
969636b683SElina Pasheva 	else if (numendpoints <= 3)
979636b683SElina Pasheva 		num_ports = 1;
989636b683SElina Pasheva 	else
999636b683SElina Pasheva 		num_ports = (numendpoints-1)/2;
1009636b683SElina Pasheva 	return num_ports;
101228426edSKevin Lloyd }
102228426edSKevin Lloyd 
is_listed(const u8 ifnum,const struct sierra_iface_list * list)103e0439cd9SJohan Hovold static bool is_listed(const u8 ifnum, const struct sierra_iface_list *list)
1044db2299dSElina Pasheva {
1054db2299dSElina Pasheva 	int i;
1064db2299dSElina Pasheva 
107e0439cd9SJohan Hovold 	if (!list)
108e0439cd9SJohan Hovold 		return false;
1094db2299dSElina Pasheva 
110e0439cd9SJohan Hovold 	for (i = 0; i < list->count; i++) {
111e0439cd9SJohan Hovold 		if (list->nums[i] == ifnum)
112e0439cd9SJohan Hovold 			return true;
1133a2b808eSElina Pasheva 	}
114e0439cd9SJohan Hovold 
115e0439cd9SJohan Hovold 	return false;
1163a2b808eSElina Pasheva }
1173a2b808eSElina Pasheva 
sierra_interface_num(struct usb_serial * serial)11816620b48SJohan Hovold static u8 sierra_interface_num(struct usb_serial *serial)
1197106967eSKevin Lloyd {
12016620b48SJohan Hovold 	return serial->interface->cur_altsetting->desc.bInterfaceNumber;
1217106967eSKevin Lloyd }
1227106967eSKevin Lloyd 
sierra_probe(struct usb_serial * serial,const struct usb_device_id * id)123228426edSKevin Lloyd static int sierra_probe(struct usb_serial *serial,
124228426edSKevin Lloyd 			const struct usb_device_id *id)
125228426edSKevin Lloyd {
126e0439cd9SJohan Hovold 	const struct sierra_iface_list *ignore_list;
127228426edSKevin Lloyd 	int result = 0;
128228426edSKevin Lloyd 	struct usb_device *udev;
129228426edSKevin Lloyd 	u8 ifnum;
130e3173b22SKevin Lloyd 
131228426edSKevin Lloyd 	udev = serial->dev;
13216620b48SJohan Hovold 	ifnum = sierra_interface_num(serial);
13364318a52SGreg Kroah-Hartman 
1347106967eSKevin Lloyd 	/*
1357106967eSKevin Lloyd 	 * If this interface supports more than 1 alternate
1367106967eSKevin Lloyd 	 * select the 2nd one
1377106967eSKevin Lloyd 	 */
1387106967eSKevin Lloyd 	if (serial->interface->num_altsetting == 2) {
139e3173b22SKevin Lloyd 		dev_dbg(&udev->dev, "Selecting alt setting for interface %d\n",
1407106967eSKevin Lloyd 			ifnum);
1417106967eSKevin Lloyd 		/* We know the alternate setting is 1 for the MC8785 */
1427106967eSKevin Lloyd 		usb_set_interface(udev, ifnum, 1);
1437106967eSKevin Lloyd 	}
1447106967eSKevin Lloyd 
145e0439cd9SJohan Hovold 	ignore_list = (const struct sierra_iface_list *)id->driver_info;
146e0439cd9SJohan Hovold 
147e0439cd9SJohan Hovold 	if (is_listed(ifnum, ignore_list)) {
148e0439cd9SJohan Hovold 		dev_dbg(&serial->dev->dev, "Ignoring interface #%d\n", ifnum);
1494db2299dSElina Pasheva 		return -ENODEV;
1504db2299dSElina Pasheva 	}
1514db2299dSElina Pasheva 
152228426edSKevin Lloyd 	return result;
153112225b1SKevin Lloyd }
154033a3fb9SKevin Lloyd 
1553a2b808eSElina Pasheva /* interfaces with higher memory requirements */
1563a2b808eSElina Pasheva static const u8 hi_memory_typeA_ifaces[] = { 0, 2 };
157e0439cd9SJohan Hovold static const struct sierra_iface_list typeA_interface_list = {
158e0439cd9SJohan Hovold 	.nums	= hi_memory_typeA_ifaces,
159e0439cd9SJohan Hovold 	.count	= ARRAY_SIZE(hi_memory_typeA_ifaces),
1603a2b808eSElina Pasheva };
1613a2b808eSElina Pasheva 
1623a2b808eSElina Pasheva static const u8 hi_memory_typeB_ifaces[] = { 3, 4, 5, 6 };
163e0439cd9SJohan Hovold static const struct sierra_iface_list typeB_interface_list = {
164e0439cd9SJohan Hovold 	.nums	= hi_memory_typeB_ifaces,
165e0439cd9SJohan Hovold 	.count	= ARRAY_SIZE(hi_memory_typeB_ifaces),
1663a2b808eSElina Pasheva };
1673a2b808eSElina Pasheva 
16866f092edSGreg Kroah-Hartman /* 'ignorelist' of interfaces not served by this driver */
169749541d1SBjørn Mork static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11, 19, 20 };
170e0439cd9SJohan Hovold static const struct sierra_iface_list direct_ip_interface_ignore = {
171e0439cd9SJohan Hovold 	.nums	= direct_ip_non_serial_ifaces,
172e0439cd9SJohan Hovold 	.count	= ARRAY_SIZE(direct_ip_non_serial_ifaces),
1734db2299dSElina Pasheva };
1744db2299dSElina Pasheva 
1757d40d7e8SNémeth Márton static const struct usb_device_id id_table[] = {
176c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */
177c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x03F0, 0x1B1D) },	/* HP ev2200 a.k.a MC5720 */
178cfbaa393SWilliam Lightning 	{ USB_DEVICE(0x03F0, 0x211D) }, /* HP ev2210 a.k.a MC5725 */
179c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x03F0, 0x1E1D) },	/* HP hs2300 a.k.a MC8775 */
180c5f3d87dSElina Pasheva 
18169de51fdSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x0017) },	/* Sierra Wireless EM5625 */
182e43062ddSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x0018) },	/* Sierra Wireless MC5720 */
18390ac3c81SGreg Kroah-Hartman 	{ USB_DEVICE(0x1199, 0x0218) },	/* Sierra Wireless MC5720 */
184e43062ddSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x0020) },	/* Sierra Wireless MC5725 */
185b9e13ac3Sagilmore@wirelessbeehive.com 	{ USB_DEVICE(0x1199, 0x0220) },	/* Sierra Wireless MC5725 */
186c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x0022) },	/* Sierra Wireless EM5725 */
187c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x0024) },	/* Sierra Wireless MC5727 */
188c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x0224) },	/* Sierra Wireless MC5727 */
189e43062ddSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x0019) },	/* Sierra Wireless AirCard 595 */
190e43062ddSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x0021) },	/* Sierra Wireless AirCard 597E */
191c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
1929454c46aSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x0120) },	/* Sierra Wireless USB Dongle 595U */
1939d72c81dSaugust huber 	{ USB_DEVICE(0x1199, 0x0301) },	/* Sierra Wireless USB Dongle 250U */
1944e0fee82SKevin Lloyd 	/* Sierra Wireless C597 */
1954e0fee82SKevin Lloyd 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0023, 0xFF, 0xFF, 0xFF) },
196c5f3d87dSElina Pasheva 	/* Sierra Wireless T598 */
197e3173b22SKevin Lloyd 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0025, 0xFF, 0xFF, 0xFF) },
198c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless T11 */
199c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x0027) }, /* Sierra Wireless AC402 */
200c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x0028) }, /* Sierra Wireless MC5728 */
201c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x0029) }, /* Sierra Wireless Device */
2029454c46aSKevin Lloyd 
20369de51fdSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6802) },	/* Sierra Wireless MC8755 */
20469de51fdSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6803) },	/* Sierra Wireless MC8765 */
205c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6804) },	/* Sierra Wireless MC8755 */
206c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6805) },	/* Sierra Wireless MC8765 */
207c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6808) },	/* Sierra Wireless MC8755 */
208c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6809) },	/* Sierra Wireless MC8765 */
2099454c46aSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6812) },	/* Sierra Wireless MC8775 & AC 875U */
210c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6813) },	/* Sierra Wireless MC8775 */
2117f170a63SKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6815) },	/* Sierra Wireless MC8775 */
212c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6816) },	/* Sierra Wireless MC8775 */
21369de51fdSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6820) },	/* Sierra Wireless AirCard 875 */
2147106967eSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6821) },	/* Sierra Wireless AirCard 875U */
215c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6822) },	/* Sierra Wireless AirCard 875E */
2169454c46aSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6832) },	/* Sierra Wireless MC8780 */
2179454c46aSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6833) },	/* Sierra Wireless MC8781 */
218c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6834) },	/* Sierra Wireless MC8780 */
219c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6835) },	/* Sierra Wireless MC8781 */
220c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6838) },	/* Sierra Wireless MC8780 */
221c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6839) },	/* Sierra Wireless MC8781 */
222b77a5c70SKevin Lloyd 	{ USB_DEVICE(0x1199, 0x683A) },	/* Sierra Wireless MC8785 */
223e3173b22SKevin Lloyd 	{ USB_DEVICE(0x1199, 0x683B) },	/* Sierra Wireless MC8785 Composite */
2244db2299dSElina Pasheva 	/* Sierra Wireless MC8790, MC8791, MC8792 Composite */
2254db2299dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x683C) },
2264db2299dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x683D) },	/* Sierra Wireless MC8791 Composite */
2274db2299dSElina Pasheva 	/* Sierra Wireless MC8790, MC8791, MC8792 */
2284db2299dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x683E) },
2299454c46aSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6850) },	/* Sierra Wireless AirCard 880 */
2309454c46aSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6851) },	/* Sierra Wireless AirCard 881 */
2319454c46aSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6852) },	/* Sierra Wireless AirCard 880 E */
2329454c46aSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6853) },	/* Sierra Wireless AirCard 881 E */
2336835b32cSKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6855) },	/* Sierra Wireless AirCard 880 U */
23462aad3abSJessica L. Blank 	{ USB_DEVICE(0x1199, 0x6856) },	/* Sierra Wireless AirCard 881 U */
235e3173b22SKevin Lloyd 	{ USB_DEVICE(0x1199, 0x6859) },	/* Sierra Wireless AirCard 885 E */
236e3173b22SKevin Lloyd 	{ USB_DEVICE(0x1199, 0x685A) },	/* Sierra Wireless AirCard 885 E */
237e3173b22SKevin Lloyd 	/* Sierra Wireless C885 */
238e3173b22SKevin Lloyd 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6880, 0xFF, 0xFF, 0xFF)},
239c5f3d87dSElina Pasheva 	/* Sierra Wireless C888, Air Card 501, USB 303, USB 304 */
240e3173b22SKevin Lloyd 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6890, 0xFF, 0xFF, 0xFF)},
241c5f3d87dSElina Pasheva 	/* Sierra Wireless C22/C33 */
24273b2c205SKevin Lloyd 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6891, 0xFF, 0xFF, 0xFF)},
243c5f3d87dSElina Pasheva 	/* Sierra Wireless HSPA Non-Composite Device */
244e3173b22SKevin Lloyd 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)},
245c5f3d87dSElina Pasheva 	{ USB_DEVICE(0x1199, 0x6893) },	/* Sierra Wireless Device */
246049255f5SBjørn Mork 	/* Sierra Wireless Direct IP modems */
247049255f5SBjørn Mork 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68A3, 0xFF, 0xFF, 0xFF),
24866f092edSGreg Kroah-Hartman 	  .driver_info = (kernel_ulong_t)&direct_ip_interface_ignore
2494db2299dSElina Pasheva 	},
2505b3da692SBjørn Mork 	{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68AA, 0xFF, 0xFF, 0xFF),
25166f092edSGreg Kroah-Hartman 	  .driver_info = (kernel_ulong_t)&direct_ip_interface_ignore
2525b3da692SBjørn Mork 	},
25374472233SDirk Behme 	{ USB_DEVICE(0x1199, 0x68AB) }, /* Sierra Wireless AR8550 */
25419a3dd15STom Cassidy 	/* AT&T Direct IP LTE modems */
25519a3dd15STom Cassidy 	{ USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68AA, 0xFF, 0xFF, 0xFF),
25666f092edSGreg Kroah-Hartman 	  .driver_info = (kernel_ulong_t)&direct_ip_interface_ignore
25719a3dd15STom Cassidy 	},
258049255f5SBjørn Mork 	/* Airprime/Sierra Wireless Direct IP modems */
259049255f5SBjørn Mork 	{ USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68A3, 0xFF, 0xFF, 0xFF),
26066f092edSGreg Kroah-Hartman 	  .driver_info = (kernel_ulong_t)&direct_ip_interface_ignore
261e1dc5157SJon Thomas 	},
2624db2299dSElina Pasheva 
26369de51fdSKevin Lloyd 	{ }
26469de51fdSKevin Lloyd };
265964ee1deSGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, id_table);
266033a3fb9SKevin Lloyd 
26769de51fdSKevin Lloyd 
268033a3fb9SKevin Lloyd struct sierra_port_private {
26917c23274SGreg Kroah-Hartman 	spinlock_t lock;	/* lock the structure */
27017c23274SGreg Kroah-Hartman 	int outstanding_urbs;	/* number of out urbs in flight */
271e6929a90SOliver Neukum 	struct usb_anchor active;
272e6929a90SOliver Neukum 	struct usb_anchor delayed;
27317c23274SGreg Kroah-Hartman 
2743a2b808eSElina Pasheva 	int num_out_urbs;
2753a2b808eSElina Pasheva 	int num_in_urbs;
2764f4f9c53SOliver Neukum 	/* Input endpoints and buffers for this port */
2773a2b808eSElina Pasheva 	struct urb *in_urbs[N_IN_URB_HM];
278033a3fb9SKevin Lloyd 
279033a3fb9SKevin Lloyd 	/* Settings for the port */
280033a3fb9SKevin Lloyd 	int rts_state;	/* Handshaking pins (outputs) */
281033a3fb9SKevin Lloyd 	int dtr_state;
282033a3fb9SKevin Lloyd 	int cts_state;	/* Handshaking pins (inputs) */
283033a3fb9SKevin Lloyd 	int dsr_state;
284033a3fb9SKevin Lloyd 	int dcd_state;
285033a3fb9SKevin Lloyd 	int ri_state;
286033a3fb9SKevin Lloyd };
287033a3fb9SKevin Lloyd 
sierra_send_setup(struct usb_serial_port * port)288335f8514SAlan Cox static int sierra_send_setup(struct usb_serial_port *port)
28969de51fdSKevin Lloyd {
290964ee1deSGreg Kroah-Hartman 	struct usb_serial *serial = port->serial;
291964ee1deSGreg Kroah-Hartman 	struct sierra_port_private *portdata;
292228426edSKevin Lloyd 	__u16 interface = 0;
293335f8514SAlan Cox 	int val = 0;
2943c77d513SElina Pasheva 	int do_send = 0;
2953c77d513SElina Pasheva 	int retval;
296033a3fb9SKevin Lloyd 
297964ee1deSGreg Kroah-Hartman 	portdata = usb_get_serial_port_data(port);
298033a3fb9SKevin Lloyd 
299964ee1deSGreg Kroah-Hartman 	if (portdata->dtr_state)
300964ee1deSGreg Kroah-Hartman 		val |= 0x01;
301964ee1deSGreg Kroah-Hartman 	if (portdata->rts_state)
302964ee1deSGreg Kroah-Hartman 		val |= 0x02;
303964ee1deSGreg Kroah-Hartman 
304e3173b22SKevin Lloyd 	/* If composite device then properly report interface */
30500b040deSAlan Cox 	if (serial->num_ports == 1) {
30616620b48SJohan Hovold 		interface = sierra_interface_num(serial);
30700b040deSAlan Cox 		/* Control message is sent only to interfaces with
30800b040deSAlan Cox 		 * interrupt_in endpoints
30900b040deSAlan Cox 		 */
31000b040deSAlan Cox 		if (port->interrupt_in_urb) {
31100b040deSAlan Cox 			/* send control message */
3123c77d513SElina Pasheva 			do_send = 1;
31300b040deSAlan Cox 		}
31400b040deSAlan Cox 	}
315e3173b22SKevin Lloyd 
316e3173b22SKevin Lloyd 	/* Otherwise the need to do non-composite mapping */
317e3173b22SKevin Lloyd 	else {
318228426edSKevin Lloyd 		if (port->bulk_out_endpointAddress == 2)
319228426edSKevin Lloyd 			interface = 0;
320228426edSKevin Lloyd 		else if (port->bulk_out_endpointAddress == 4)
321228426edSKevin Lloyd 			interface = 1;
322228426edSKevin Lloyd 		else if (port->bulk_out_endpointAddress == 5)
323228426edSKevin Lloyd 			interface = 2;
3243c77d513SElina Pasheva 
3253c77d513SElina Pasheva 		do_send = 1;
32600b040deSAlan Cox 	}
3273c77d513SElina Pasheva 	if (!do_send)
328033a3fb9SKevin Lloyd 		return 0;
3293c77d513SElina Pasheva 
3305b7c1178SOliver Neukum 	retval = usb_autopm_get_interface(serial->interface);
3315b7c1178SOliver Neukum 	if (retval < 0)
3325b7c1178SOliver Neukum 		return retval;
3335b7c1178SOliver Neukum 
334d3048898SJohan Hovold 	retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
3353c77d513SElina Pasheva 		0x22, 0x21, val, interface, NULL, 0, USB_CTRL_SET_TIMEOUT);
3363c77d513SElina Pasheva 	usb_autopm_put_interface(serial->interface);
3373c77d513SElina Pasheva 
3383c77d513SElina Pasheva 	return retval;
33969de51fdSKevin Lloyd }
34069de51fdSKevin Lloyd 
sierra_tiocmget(struct tty_struct * tty)34160b33c13SAlan Cox static int sierra_tiocmget(struct tty_struct *tty)
342033a3fb9SKevin Lloyd {
34395da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
344033a3fb9SKevin Lloyd 	unsigned int value;
345033a3fb9SKevin Lloyd 	struct sierra_port_private *portdata;
346033a3fb9SKevin Lloyd 
347033a3fb9SKevin Lloyd 	portdata = usb_get_serial_port_data(port);
348033a3fb9SKevin Lloyd 
349033a3fb9SKevin Lloyd 	value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
350033a3fb9SKevin Lloyd 		((portdata->dtr_state) ? TIOCM_DTR : 0) |
351033a3fb9SKevin Lloyd 		((portdata->cts_state) ? TIOCM_CTS : 0) |
352033a3fb9SKevin Lloyd 		((portdata->dsr_state) ? TIOCM_DSR : 0) |
353033a3fb9SKevin Lloyd 		((portdata->dcd_state) ? TIOCM_CAR : 0) |
354033a3fb9SKevin Lloyd 		((portdata->ri_state) ? TIOCM_RNG : 0);
355033a3fb9SKevin Lloyd 
356033a3fb9SKevin Lloyd 	return value;
357033a3fb9SKevin Lloyd }
358033a3fb9SKevin Lloyd 
sierra_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)35920b9d177SAlan Cox static int sierra_tiocmset(struct tty_struct *tty,
360033a3fb9SKevin Lloyd 			unsigned int set, unsigned int clear)
361033a3fb9SKevin Lloyd {
36295da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
363033a3fb9SKevin Lloyd 	struct sierra_port_private *portdata;
364033a3fb9SKevin Lloyd 
365033a3fb9SKevin Lloyd 	portdata = usb_get_serial_port_data(port);
366033a3fb9SKevin Lloyd 
367033a3fb9SKevin Lloyd 	if (set & TIOCM_RTS)
368033a3fb9SKevin Lloyd 		portdata->rts_state = 1;
369033a3fb9SKevin Lloyd 	if (set & TIOCM_DTR)
370033a3fb9SKevin Lloyd 		portdata->dtr_state = 1;
371033a3fb9SKevin Lloyd 
372033a3fb9SKevin Lloyd 	if (clear & TIOCM_RTS)
373033a3fb9SKevin Lloyd 		portdata->rts_state = 0;
374033a3fb9SKevin Lloyd 	if (clear & TIOCM_DTR)
375033a3fb9SKevin Lloyd 		portdata->dtr_state = 0;
376335f8514SAlan Cox 	return sierra_send_setup(port);
377033a3fb9SKevin Lloyd }
378033a3fb9SKevin Lloyd 
sierra_release_urb(struct urb * urb)379b9a44bc1SElina Pasheva static void sierra_release_urb(struct urb *urb)
380b9a44bc1SElina Pasheva {
381b9a44bc1SElina Pasheva 	if (urb) {
382b9a44bc1SElina Pasheva 		kfree(urb->transfer_buffer);
383b9a44bc1SElina Pasheva 		usb_free_urb(urb);
384b9a44bc1SElina Pasheva 	}
385b9a44bc1SElina Pasheva }
386b9a44bc1SElina Pasheva 
sierra_outdat_callback(struct urb * urb)38717c23274SGreg Kroah-Hartman static void sierra_outdat_callback(struct urb *urb)
38817c23274SGreg Kroah-Hartman {
38917c23274SGreg Kroah-Hartman 	struct usb_serial_port *port = urb->context;
39017c23274SGreg Kroah-Hartman 	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
391e6929a90SOliver Neukum 	struct sierra_intf_private *intfdata;
39217c23274SGreg Kroah-Hartman 	int status = urb->status;
393d4bf25b3SJohn Ogness 	unsigned long flags;
39417c23274SGreg Kroah-Hartman 
3957c80782eSJohan Hovold 	intfdata = usb_get_serial_data(port->serial);
39617c23274SGreg Kroah-Hartman 
39717c23274SGreg Kroah-Hartman 	/* free up the transfer buffer, as usb_free_urb() does not do this */
39817c23274SGreg Kroah-Hartman 	kfree(urb->transfer_buffer);
399e6929a90SOliver Neukum 	usb_autopm_put_interface_async(port->serial->interface);
40017c23274SGreg Kroah-Hartman 	if (status)
4017384a922SKevin Lloyd 		dev_dbg(&port->dev, "%s - nonzero write bulk status "
4025d44b361SElina Pasheva 		    "received: %d\n", __func__, status);
40317c23274SGreg Kroah-Hartman 
404d4bf25b3SJohn Ogness 	spin_lock_irqsave(&portdata->lock, flags);
40517c23274SGreg Kroah-Hartman 	--portdata->outstanding_urbs;
406d4bf25b3SJohn Ogness 	spin_unlock_irqrestore(&portdata->lock, flags);
407d4bf25b3SJohn Ogness 	spin_lock_irqsave(&intfdata->susp_lock, flags);
408e6929a90SOliver Neukum 	--intfdata->in_flight;
409d4bf25b3SJohn Ogness 	spin_unlock_irqrestore(&intfdata->susp_lock, flags);
41017c23274SGreg Kroah-Hartman 
41117c23274SGreg Kroah-Hartman 	usb_serial_port_softint(port);
41217c23274SGreg Kroah-Hartman }
41317c23274SGreg Kroah-Hartman 
414033a3fb9SKevin Lloyd /* Write */
sierra_write(struct tty_struct * tty,struct usb_serial_port * port,const unsigned char * buf,int count)41595da310eSAlan Cox static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
416033a3fb9SKevin Lloyd 					const unsigned char *buf, int count)
417033a3fb9SKevin Lloyd {
418a45a1e07SJulia Lawall 	struct sierra_port_private *portdata;
419e6929a90SOliver Neukum 	struct sierra_intf_private *intfdata;
42017c23274SGreg Kroah-Hartman 	struct usb_serial *serial = port->serial;
42117c23274SGreg Kroah-Hartman 	unsigned long flags;
42217c23274SGreg Kroah-Hartman 	unsigned char *buffer;
42317c23274SGreg Kroah-Hartman 	struct urb *urb;
42440d2ff32SElina Pasheva 	size_t writesize = min((size_t)count, (size_t)MAX_TRANSFER);
42540d2ff32SElina Pasheva 	int retval = 0;
42640d2ff32SElina Pasheva 
42740d2ff32SElina Pasheva 	/* verify that we actually have some data to write */
42840d2ff32SElina Pasheva 	if (count == 0)
42940d2ff32SElina Pasheva 		return 0;
430033a3fb9SKevin Lloyd 
431033a3fb9SKevin Lloyd 	portdata = usb_get_serial_port_data(port);
4327c80782eSJohan Hovold 	intfdata = usb_get_serial_data(serial);
433033a3fb9SKevin Lloyd 
4340b10395aSAndrew Morton 	dev_dbg(&port->dev, "%s: write (%zd bytes)\n", __func__, writesize);
43517c23274SGreg Kroah-Hartman 	spin_lock_irqsave(&portdata->lock, flags);
43640d2ff32SElina Pasheva 	dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__,
43740d2ff32SElina Pasheva 		portdata->outstanding_urbs);
4383a2b808eSElina Pasheva 	if (portdata->outstanding_urbs > portdata->num_out_urbs) {
43917c23274SGreg Kroah-Hartman 		spin_unlock_irqrestore(&portdata->lock, flags);
4407384a922SKevin Lloyd 		dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
44117c23274SGreg Kroah-Hartman 		return 0;
442033a3fb9SKevin Lloyd 	}
44317c23274SGreg Kroah-Hartman 	portdata->outstanding_urbs++;
44440d2ff32SElina Pasheva 	dev_dbg(&port->dev, "%s - 1, outstanding_urbs: %d\n", __func__,
44540d2ff32SElina Pasheva 		portdata->outstanding_urbs);
44617c23274SGreg Kroah-Hartman 	spin_unlock_irqrestore(&portdata->lock, flags);
447033a3fb9SKevin Lloyd 
448e6929a90SOliver Neukum 	retval = usb_autopm_get_interface_async(serial->interface);
449e6929a90SOliver Neukum 	if (retval < 0) {
450e6929a90SOliver Neukum 		spin_lock_irqsave(&portdata->lock, flags);
451e6929a90SOliver Neukum 		portdata->outstanding_urbs--;
452e6929a90SOliver Neukum 		spin_unlock_irqrestore(&portdata->lock, flags);
453e6929a90SOliver Neukum 		goto error_simple;
454e6929a90SOliver Neukum 	}
455e6929a90SOliver Neukum 
4567828466cSSlark Xiao 	buffer = kmemdup(buf, writesize, GFP_ATOMIC);
45717c23274SGreg Kroah-Hartman 	if (!buffer) {
45840d2ff32SElina Pasheva 		retval = -ENOMEM;
45917c23274SGreg Kroah-Hartman 		goto error_no_buffer;
460033a3fb9SKevin Lloyd 	}
461033a3fb9SKevin Lloyd 
46217c23274SGreg Kroah-Hartman 	urb = usb_alloc_urb(0, GFP_ATOMIC);
46317c23274SGreg Kroah-Hartman 	if (!urb) {
46440d2ff32SElina Pasheva 		retval = -ENOMEM;
46517c23274SGreg Kroah-Hartman 		goto error_no_urb;
46617c23274SGreg Kroah-Hartman 	}
46717c23274SGreg Kroah-Hartman 
46859d33f2fSGreg Kroah-Hartman 	usb_serial_debug_data(&port->dev, __func__, writesize, buffer);
46917c23274SGreg Kroah-Hartman 
47017c23274SGreg Kroah-Hartman 	usb_fill_bulk_urb(urb, serial->dev,
47117c23274SGreg Kroah-Hartman 			  usb_sndbulkpipe(serial->dev,
47217c23274SGreg Kroah-Hartman 					  port->bulk_out_endpointAddress),
47340d2ff32SElina Pasheva 			  buffer, writesize, sierra_outdat_callback, port);
47417c23274SGreg Kroah-Hartman 
475238ebd13SElina Pasheva 	/* Handle the need to send a zero length packet */
476238ebd13SElina Pasheva 	urb->transfer_flags |= URB_ZERO_PACKET;
477238ebd13SElina Pasheva 
478e6929a90SOliver Neukum 	spin_lock_irqsave(&intfdata->susp_lock, flags);
479e6929a90SOliver Neukum 
480e6929a90SOliver Neukum 	if (intfdata->suspended) {
481e6929a90SOliver Neukum 		usb_anchor_urb(urb, &portdata->delayed);
482e6929a90SOliver Neukum 		spin_unlock_irqrestore(&intfdata->susp_lock, flags);
483e6929a90SOliver Neukum 		goto skip_power;
484e6929a90SOliver Neukum 	} else {
485e6929a90SOliver Neukum 		usb_anchor_urb(urb, &portdata->active);
486e6929a90SOliver Neukum 	}
48717c23274SGreg Kroah-Hartman 	/* send it down the pipe */
48840d2ff32SElina Pasheva 	retval = usb_submit_urb(urb, GFP_ATOMIC);
48940d2ff32SElina Pasheva 	if (retval) {
490e6929a90SOliver Neukum 		usb_unanchor_urb(urb);
491e6929a90SOliver Neukum 		spin_unlock_irqrestore(&intfdata->susp_lock, flags);
49217c23274SGreg Kroah-Hartman 		dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed "
49340d2ff32SElina Pasheva 			"with status = %d\n", __func__, retval);
49417c23274SGreg Kroah-Hartman 		goto error;
495e6929a90SOliver Neukum 	} else {
496e6929a90SOliver Neukum 		intfdata->in_flight++;
497e6929a90SOliver Neukum 		spin_unlock_irqrestore(&intfdata->susp_lock, flags);
49817c23274SGreg Kroah-Hartman 	}
49917c23274SGreg Kroah-Hartman 
500e6929a90SOliver Neukum skip_power:
50117c23274SGreg Kroah-Hartman 	/* we are done with this urb, so let the host driver
50217c23274SGreg Kroah-Hartman 	 * really free it when it is finished with it */
50317c23274SGreg Kroah-Hartman 	usb_free_urb(urb);
50417c23274SGreg Kroah-Hartman 
50540d2ff32SElina Pasheva 	return writesize;
50617c23274SGreg Kroah-Hartman error:
50717c23274SGreg Kroah-Hartman 	usb_free_urb(urb);
50817c23274SGreg Kroah-Hartman error_no_urb:
50917c23274SGreg Kroah-Hartman 	kfree(buffer);
51017c23274SGreg Kroah-Hartman error_no_buffer:
51117c23274SGreg Kroah-Hartman 	spin_lock_irqsave(&portdata->lock, flags);
51217c23274SGreg Kroah-Hartman 	--portdata->outstanding_urbs;
51340d2ff32SElina Pasheva 	dev_dbg(&port->dev, "%s - 2. outstanding_urbs: %d\n", __func__,
51440d2ff32SElina Pasheva 		portdata->outstanding_urbs);
51517c23274SGreg Kroah-Hartman 	spin_unlock_irqrestore(&portdata->lock, flags);
516e6929a90SOliver Neukum 	usb_autopm_put_interface_async(serial->interface);
517e6929a90SOliver Neukum error_simple:
51840d2ff32SElina Pasheva 	return retval;
519033a3fb9SKevin Lloyd }
520033a3fb9SKevin Lloyd 
sierra_indat_callback(struct urb * urb)521033a3fb9SKevin Lloyd static void sierra_indat_callback(struct urb *urb)
522033a3fb9SKevin Lloyd {
523033a3fb9SKevin Lloyd 	int err;
524033a3fb9SKevin Lloyd 	int endpoint;
525033a3fb9SKevin Lloyd 	struct usb_serial_port *port;
526033a3fb9SKevin Lloyd 	unsigned char *data = urb->transfer_buffer;
52717dd2215SGreg Kroah-Hartman 	int status = urb->status;
528033a3fb9SKevin Lloyd 
529033a3fb9SKevin Lloyd 	endpoint = usb_pipeendpoint(urb->pipe);
530cdc97792SMing Lei 	port = urb->context;
531033a3fb9SKevin Lloyd 
53217dd2215SGreg Kroah-Hartman 	if (status) {
5337384a922SKevin Lloyd 		dev_dbg(&port->dev, "%s: nonzero status: %d on"
5345d44b361SElina Pasheva 			" endpoint %02x\n", __func__, status, endpoint);
535033a3fb9SKevin Lloyd 	} else {
536033a3fb9SKevin Lloyd 		if (urb->actual_length) {
53705c7cd39SJiri Slaby 			tty_insert_flip_string(&port->port, data,
538b87c6e86SElina Pasheva 				urb->actual_length);
5392e124b4aSJiri Slaby 			tty_flip_buffer_push(&port->port);
540b0cda8c5SElina Pasheva 
54159d33f2fSGreg Kroah-Hartman 			usb_serial_debug_data(&port->dev, __func__,
54259d33f2fSGreg Kroah-Hartman 					      urb->actual_length, data);
543b0cda8c5SElina Pasheva 		} else {
5447384a922SKevin Lloyd 			dev_dbg(&port->dev, "%s: empty read urb"
5455d44b361SElina Pasheva 				" received\n", __func__);
546b0cda8c5SElina Pasheva 		}
547b0cda8c5SElina Pasheva 	}
548033a3fb9SKevin Lloyd 
549033a3fb9SKevin Lloyd 	/* Resubmit urb so we continue receiving */
5501f87158eSAlan Stern 	if (status != -ESHUTDOWN && status != -EPERM) {
551e6929a90SOliver Neukum 		usb_mark_last_busy(port->serial->dev);
552033a3fb9SKevin Lloyd 		err = usb_submit_urb(urb, GFP_ATOMIC);
5531f87158eSAlan Stern 		if (err && err != -EPERM)
55417c23274SGreg Kroah-Hartman 			dev_err(&port->dev, "resubmit read urb failed."
555898eb71cSJoe Perches 				"(%d)\n", err);
556033a3fb9SKevin Lloyd 	}
557033a3fb9SKevin Lloyd }
558033a3fb9SKevin Lloyd 
sierra_instat_callback(struct urb * urb)559033a3fb9SKevin Lloyd static void sierra_instat_callback(struct urb *urb)
560033a3fb9SKevin Lloyd {
561033a3fb9SKevin Lloyd 	int err;
56217dd2215SGreg Kroah-Hartman 	int status = urb->status;
563cdc97792SMing Lei 	struct usb_serial_port *port =  urb->context;
564033a3fb9SKevin Lloyd 	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
565033a3fb9SKevin Lloyd 	struct usb_serial *serial = port->serial;
566033a3fb9SKevin Lloyd 
5675d44b361SElina Pasheva 	dev_dbg(&port->dev, "%s: urb %p port %p has data %p\n", __func__,
5687384a922SKevin Lloyd 		urb, port, portdata);
569033a3fb9SKevin Lloyd 
57017dd2215SGreg Kroah-Hartman 	if (status == 0) {
571eb0c68eaSJohan Hovold 		struct usb_ctrlrequest *req_pkt = urb->transfer_buffer;
572033a3fb9SKevin Lloyd 
573033a3fb9SKevin Lloyd 		if (!req_pkt) {
5747384a922SKevin Lloyd 			dev_dbg(&port->dev, "%s: NULL req_pkt\n",
5757384a922SKevin Lloyd 				__func__);
576033a3fb9SKevin Lloyd 			return;
577033a3fb9SKevin Lloyd 		}
578033a3fb9SKevin Lloyd 		if ((req_pkt->bRequestType == 0xA1) &&
579033a3fb9SKevin Lloyd 				(req_pkt->bRequest == 0x20)) {
580033a3fb9SKevin Lloyd 			int old_dcd_state;
581033a3fb9SKevin Lloyd 			unsigned char signals = *((unsigned char *)
582033a3fb9SKevin Lloyd 					urb->transfer_buffer +
583033a3fb9SKevin Lloyd 					sizeof(struct usb_ctrlrequest));
584033a3fb9SKevin Lloyd 
5855d44b361SElina Pasheva 			dev_dbg(&port->dev, "%s: signal x%x\n", __func__,
5867384a922SKevin Lloyd 				signals);
587033a3fb9SKevin Lloyd 
588033a3fb9SKevin Lloyd 			old_dcd_state = portdata->dcd_state;
589033a3fb9SKevin Lloyd 			portdata->cts_state = 1;
590033a3fb9SKevin Lloyd 			portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
591033a3fb9SKevin Lloyd 			portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
592033a3fb9SKevin Lloyd 			portdata->ri_state = ((signals & 0x08) ? 1 : 0);
593033a3fb9SKevin Lloyd 
594aa27a094SJiri Slaby 			if (old_dcd_state && !portdata->dcd_state)
595aa27a094SJiri Slaby 				tty_port_tty_hangup(&port->port, true);
596033a3fb9SKevin Lloyd 		} else {
5975d44b361SElina Pasheva 			dev_dbg(&port->dev, "%s: type %x req %x\n",
5987384a922SKevin Lloyd 				__func__, req_pkt->bRequestType,
5997384a922SKevin Lloyd 				req_pkt->bRequest);
600033a3fb9SKevin Lloyd 		}
601033a3fb9SKevin Lloyd 	} else
6025d44b361SElina Pasheva 		dev_dbg(&port->dev, "%s: error %d\n", __func__, status);
603033a3fb9SKevin Lloyd 
604033a3fb9SKevin Lloyd 	/* Resubmit urb so we continue receiving IRQ data */
6051f87158eSAlan Stern 	if (status != -ESHUTDOWN && status != -ENOENT) {
606e6929a90SOliver Neukum 		usb_mark_last_busy(serial->dev);
607033a3fb9SKevin Lloyd 		err = usb_submit_urb(urb, GFP_ATOMIC);
6081f87158eSAlan Stern 		if (err && err != -EPERM)
609c76a23daSElina Pasheva 			dev_err(&port->dev, "%s: resubmit intr urb "
6105d44b361SElina Pasheva 				"failed. (%d)\n", __func__, err);
611033a3fb9SKevin Lloyd 	}
612033a3fb9SKevin Lloyd }
613033a3fb9SKevin Lloyd 
sierra_write_room(struct tty_struct * tty)61494cc7aeaSJiri Slaby static unsigned int sierra_write_room(struct tty_struct *tty)
615033a3fb9SKevin Lloyd {
61695da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
61717c23274SGreg Kroah-Hartman 	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
61817c23274SGreg Kroah-Hartman 	unsigned long flags;
619033a3fb9SKevin Lloyd 
62017c23274SGreg Kroah-Hartman 	/* try to give a good number back based on if we have any free urbs at
62117c23274SGreg Kroah-Hartman 	 * this point in time */
62217c23274SGreg Kroah-Hartman 	spin_lock_irqsave(&portdata->lock, flags);
6233a2b808eSElina Pasheva 	if (portdata->outstanding_urbs > (portdata->num_out_urbs * 2) / 3) {
62417c23274SGreg Kroah-Hartman 		spin_unlock_irqrestore(&portdata->lock, flags);
6257384a922SKevin Lloyd 		dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
62617c23274SGreg Kroah-Hartman 		return 0;
627033a3fb9SKevin Lloyd 	}
62817c23274SGreg Kroah-Hartman 	spin_unlock_irqrestore(&portdata->lock, flags);
629033a3fb9SKevin Lloyd 
63017c23274SGreg Kroah-Hartman 	return 2048;
631033a3fb9SKevin Lloyd }
632033a3fb9SKevin Lloyd 
sierra_chars_in_buffer(struct tty_struct * tty)633155591d3SJiri Slaby static unsigned int sierra_chars_in_buffer(struct tty_struct *tty)
63493670599SJohan Hovold {
63593670599SJohan Hovold 	struct usb_serial_port *port = tty->driver_data;
63693670599SJohan Hovold 	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
63793670599SJohan Hovold 	unsigned long flags;
638155591d3SJiri Slaby 	unsigned int chars;
63993670599SJohan Hovold 
64093670599SJohan Hovold 	/* NOTE: This overcounts somewhat. */
64193670599SJohan Hovold 	spin_lock_irqsave(&portdata->lock, flags);
64293670599SJohan Hovold 	chars = portdata->outstanding_urbs * MAX_TRANSFER;
64393670599SJohan Hovold 	spin_unlock_irqrestore(&portdata->lock, flags);
64493670599SJohan Hovold 
645155591d3SJiri Slaby 	dev_dbg(&port->dev, "%s - %u\n", __func__, chars);
64693670599SJohan Hovold 
64793670599SJohan Hovold 	return chars;
64893670599SJohan Hovold }
64993670599SJohan Hovold 
sierra_stop_rx_urbs(struct usb_serial_port * port)650b9a44bc1SElina Pasheva static void sierra_stop_rx_urbs(struct usb_serial_port *port)
651b9a44bc1SElina Pasheva {
652b9a44bc1SElina Pasheva 	int i;
653b9a44bc1SElina Pasheva 	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
654b9a44bc1SElina Pasheva 
6553a2b808eSElina Pasheva 	for (i = 0; i < portdata->num_in_urbs; i++)
656b9a44bc1SElina Pasheva 		usb_kill_urb(portdata->in_urbs[i]);
657b9a44bc1SElina Pasheva 
658b9a44bc1SElina Pasheva 	usb_kill_urb(port->interrupt_in_urb);
659b9a44bc1SElina Pasheva }
660b9a44bc1SElina Pasheva 
sierra_submit_rx_urbs(struct usb_serial_port * port,gfp_t mem_flags)661b9a44bc1SElina Pasheva static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags)
662b9a44bc1SElina Pasheva {
663b9a44bc1SElina Pasheva 	int ok_cnt;
664b9a44bc1SElina Pasheva 	int err = -EINVAL;
665b9a44bc1SElina Pasheva 	int i;
666b9a44bc1SElina Pasheva 	struct urb *urb;
667b9a44bc1SElina Pasheva 	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
668b9a44bc1SElina Pasheva 
669b9a44bc1SElina Pasheva 	ok_cnt = 0;
6703a2b808eSElina Pasheva 	for (i = 0; i < portdata->num_in_urbs; i++) {
671b9a44bc1SElina Pasheva 		urb = portdata->in_urbs[i];
672b9a44bc1SElina Pasheva 		if (!urb)
673b9a44bc1SElina Pasheva 			continue;
674b9a44bc1SElina Pasheva 		err = usb_submit_urb(urb, mem_flags);
675b9a44bc1SElina Pasheva 		if (err) {
676b9a44bc1SElina Pasheva 			dev_err(&port->dev, "%s: submit urb failed: %d\n",
677b9a44bc1SElina Pasheva 				__func__, err);
678b9a44bc1SElina Pasheva 		} else {
679b9a44bc1SElina Pasheva 			ok_cnt++;
680b9a44bc1SElina Pasheva 		}
681b9a44bc1SElina Pasheva 	}
682b9a44bc1SElina Pasheva 
683b9a44bc1SElina Pasheva 	if (ok_cnt && port->interrupt_in_urb) {
684b9a44bc1SElina Pasheva 		err = usb_submit_urb(port->interrupt_in_urb, mem_flags);
685b9a44bc1SElina Pasheva 		if (err) {
686b9a44bc1SElina Pasheva 			dev_err(&port->dev, "%s: submit intr urb failed: %d\n",
687b9a44bc1SElina Pasheva 				__func__, err);
688b9a44bc1SElina Pasheva 		}
689b9a44bc1SElina Pasheva 	}
690b9a44bc1SElina Pasheva 
691b9a44bc1SElina Pasheva 	if (ok_cnt > 0) /* at least one rx urb submitted */
692b9a44bc1SElina Pasheva 		return 0;
693b9a44bc1SElina Pasheva 	else
694b9a44bc1SElina Pasheva 		return err;
695b9a44bc1SElina Pasheva }
696b9a44bc1SElina Pasheva 
sierra_setup_urb(struct usb_serial * serial,int endpoint,int dir,void * ctx,int len,gfp_t mem_flags,usb_complete_t callback)697b9a44bc1SElina Pasheva static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint,
698b9a44bc1SElina Pasheva 					int dir, void *ctx, int len,
699b9a44bc1SElina Pasheva 					gfp_t mem_flags,
700b9a44bc1SElina Pasheva 					usb_complete_t callback)
701b9a44bc1SElina Pasheva {
702b9a44bc1SElina Pasheva 	struct urb	*urb;
703b9a44bc1SElina Pasheva 	u8		*buf;
704b9a44bc1SElina Pasheva 
705b9a44bc1SElina Pasheva 	urb = usb_alloc_urb(0, mem_flags);
70610c642d0SJohan Hovold 	if (!urb)
707b9a44bc1SElina Pasheva 		return NULL;
708b9a44bc1SElina Pasheva 
709b9a44bc1SElina Pasheva 	buf = kmalloc(len, mem_flags);
710b9a44bc1SElina Pasheva 	if (buf) {
711b9a44bc1SElina Pasheva 		/* Fill URB using supplied data */
712b9a44bc1SElina Pasheva 		usb_fill_bulk_urb(urb, serial->dev,
713b9a44bc1SElina Pasheva 			usb_sndbulkpipe(serial->dev, endpoint) | dir,
714b9a44bc1SElina Pasheva 			buf, len, callback, ctx);
715b9a44bc1SElina Pasheva 
716b9a44bc1SElina Pasheva 		dev_dbg(&serial->dev->dev, "%s %c u : %p d:%p\n", __func__,
717b9a44bc1SElina Pasheva 				dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
718b9a44bc1SElina Pasheva 	} else {
719b9a44bc1SElina Pasheva 		sierra_release_urb(urb);
720b9a44bc1SElina Pasheva 		urb = NULL;
721b9a44bc1SElina Pasheva 	}
722b9a44bc1SElina Pasheva 
723b9a44bc1SElina Pasheva 	return urb;
724b9a44bc1SElina Pasheva }
725b9a44bc1SElina Pasheva 
sierra_close(struct usb_serial_port * port)726b9a44bc1SElina Pasheva static void sierra_close(struct usb_serial_port *port)
727b9a44bc1SElina Pasheva {
728b9a44bc1SElina Pasheva 	int i;
729b9a44bc1SElina Pasheva 	struct usb_serial *serial = port->serial;
730b9a44bc1SElina Pasheva 	struct sierra_port_private *portdata;
7317c80782eSJohan Hovold 	struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
732014333f7SJohan Hovold 	struct urb *urb;
733e6929a90SOliver Neukum 
734b9a44bc1SElina Pasheva 	portdata = usb_get_serial_port_data(port);
735b9a44bc1SElina Pasheva 
736c2e45d70SJohan Hovold 	/*
737c2e45d70SJohan Hovold 	 * Need to take susp_lock to make sure port is not already being
738*688ee1d1SJohan Hovold 	 * resumed, but no need to hold it due to the tty-port initialized
739*688ee1d1SJohan Hovold 	 * flag.
740c2e45d70SJohan Hovold 	 */
741e6929a90SOliver Neukum 	spin_lock_irq(&intfdata->susp_lock);
74280cc0fcbSJohan Hovold 	if (--intfdata->open_ports == 0)
74380cc0fcbSJohan Hovold 		serial->interface->needs_remote_wakeup = 0;
744e6929a90SOliver Neukum 	spin_unlock_irq(&intfdata->susp_lock);
745e6929a90SOliver Neukum 
746014333f7SJohan Hovold 	for (;;) {
747014333f7SJohan Hovold 		urb = usb_get_from_anchor(&portdata->delayed);
748014333f7SJohan Hovold 		if (!urb)
749014333f7SJohan Hovold 			break;
750014333f7SJohan Hovold 		kfree(urb->transfer_buffer);
751014333f7SJohan Hovold 		usb_free_urb(urb);
752014333f7SJohan Hovold 		usb_autopm_put_interface_async(serial->interface);
753e6087001SJohn Ogness 		spin_lock_irq(&portdata->lock);
754014333f7SJohan Hovold 		portdata->outstanding_urbs--;
755e6087001SJohn Ogness 		spin_unlock_irq(&portdata->lock);
756014333f7SJohan Hovold 	}
757014333f7SJohan Hovold 
758b9a44bc1SElina Pasheva 	sierra_stop_rx_urbs(port);
759c9d838a8SJohan Hovold 	usb_kill_anchored_urbs(&portdata->active);
760c9d838a8SJohan Hovold 
7613a2b808eSElina Pasheva 	for (i = 0; i < portdata->num_in_urbs; i++) {
762b9a44bc1SElina Pasheva 		sierra_release_urb(portdata->in_urbs[i]);
763b9a44bc1SElina Pasheva 		portdata->in_urbs[i] = NULL;
764b9a44bc1SElina Pasheva 	}
7650287d5c5SJohan Hovold 
7660287d5c5SJohan Hovold 	usb_autopm_get_interface_no_resume(serial->interface);
767b9a44bc1SElina Pasheva }
768b9a44bc1SElina Pasheva 
sierra_open(struct tty_struct * tty,struct usb_serial_port * port)769a509a7e4SAlan Cox static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
770033a3fb9SKevin Lloyd {
771033a3fb9SKevin Lloyd 	struct sierra_port_private *portdata;
772033a3fb9SKevin Lloyd 	struct usb_serial *serial = port->serial;
7737c80782eSJohan Hovold 	struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
7749e85c5f6SGreg Kroah-Hartman 	int i;
775b9a44bc1SElina Pasheva 	int err;
776b9a44bc1SElina Pasheva 	int endpoint;
777033a3fb9SKevin Lloyd 	struct urb *urb;
778033a3fb9SKevin Lloyd 
779033a3fb9SKevin Lloyd 	portdata = usb_get_serial_port_data(port);
780033a3fb9SKevin Lloyd 
781b9a44bc1SElina Pasheva 	endpoint = port->bulk_in_endpointAddress;
7823a2b808eSElina Pasheva 	for (i = 0; i < portdata->num_in_urbs; i++) {
783b9a44bc1SElina Pasheva 		urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port,
784b9a44bc1SElina Pasheva 					IN_BUFLEN, GFP_KERNEL,
785b9a44bc1SElina Pasheva 					sierra_indat_callback);
786b9a44bc1SElina Pasheva 		portdata->in_urbs[i] = urb;
787033a3fb9SKevin Lloyd 	}
788b9a44bc1SElina Pasheva 	/* clear halt condition */
789b9a44bc1SElina Pasheva 	usb_clear_halt(serial->dev,
790b9a44bc1SElina Pasheva 			usb_sndbulkpipe(serial->dev, endpoint) | USB_DIR_IN);
791033a3fb9SKevin Lloyd 
792b9a44bc1SElina Pasheva 	err = sierra_submit_rx_urbs(port, GFP_KERNEL);
793353fe198SJohan Hovold 	if (err)
794353fe198SJohan Hovold 		goto err_submit;
795353fe198SJohan Hovold 
796e6929a90SOliver Neukum 	spin_lock_irq(&intfdata->susp_lock);
79780cc0fcbSJohan Hovold 	if (++intfdata->open_ports == 1)
79880cc0fcbSJohan Hovold 		serial->interface->needs_remote_wakeup = 1;
799e6929a90SOliver Neukum 	spin_unlock_irq(&intfdata->susp_lock);
800e6929a90SOliver Neukum 	usb_autopm_put_interface(serial->interface);
801e6929a90SOliver Neukum 
8029e85c5f6SGreg Kroah-Hartman 	return 0;
803353fe198SJohan Hovold 
804353fe198SJohan Hovold err_submit:
805353fe198SJohan Hovold 	sierra_stop_rx_urbs(port);
806353fe198SJohan Hovold 
807353fe198SJohan Hovold 	for (i = 0; i < portdata->num_in_urbs; i++) {
808353fe198SJohan Hovold 		sierra_release_urb(portdata->in_urbs[i]);
809353fe198SJohan Hovold 		portdata->in_urbs[i] = NULL;
810353fe198SJohan Hovold 	}
811353fe198SJohan Hovold 
812353fe198SJohan Hovold 	return err;
813033a3fb9SKevin Lloyd }
814033a3fb9SKevin Lloyd 
815b9a44bc1SElina Pasheva 
sierra_dtr_rts(struct usb_serial_port * port,int on)816335f8514SAlan Cox static void sierra_dtr_rts(struct usb_serial_port *port, int on)
817335f8514SAlan Cox {
818335f8514SAlan Cox 	struct sierra_port_private *portdata;
819335f8514SAlan Cox 
820335f8514SAlan Cox 	portdata = usb_get_serial_port_data(port);
821335f8514SAlan Cox 	portdata->rts_state = on;
822335f8514SAlan Cox 	portdata->dtr_state = on;
823335f8514SAlan Cox 
824335f8514SAlan Cox 	sierra_send_setup(port);
825335f8514SAlan Cox }
826335f8514SAlan Cox 
sierra_startup(struct usb_serial * serial)827033a3fb9SKevin Lloyd static int sierra_startup(struct usb_serial *serial)
828033a3fb9SKevin Lloyd {
829084817d7SJohan Hovold 	struct sierra_intf_private *intfdata;
830033a3fb9SKevin Lloyd 
831084817d7SJohan Hovold 	intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL);
832084817d7SJohan Hovold 	if (!intfdata)
833084817d7SJohan Hovold 		return -ENOMEM;
834084817d7SJohan Hovold 
835084817d7SJohan Hovold 	spin_lock_init(&intfdata->susp_lock);
836084817d7SJohan Hovold 
837084817d7SJohan Hovold 	usb_set_serial_data(serial, intfdata);
838084817d7SJohan Hovold 
839112225b1SKevin Lloyd 	/* Set Device mode to D0 */
840112225b1SKevin Lloyd 	sierra_set_power_state(serial->dev, 0x0000);
841112225b1SKevin Lloyd 
842228426edSKevin Lloyd 	/* Check NMEA and set */
843228426edSKevin Lloyd 	if (nmea)
844228426edSKevin Lloyd 		sierra_vsc_set_nmea(serial->dev, 1);
845228426edSKevin Lloyd 
846f525c05bSJohan Hovold 	return 0;
847033a3fb9SKevin Lloyd }
848f525c05bSJohan Hovold 
sierra_release(struct usb_serial * serial)849f525c05bSJohan Hovold static void sierra_release(struct usb_serial *serial)
850f525c05bSJohan Hovold {
851f525c05bSJohan Hovold 	struct sierra_intf_private *intfdata;
852f525c05bSJohan Hovold 
853f525c05bSJohan Hovold 	intfdata = usb_get_serial_data(serial);
854f525c05bSJohan Hovold 	kfree(intfdata);
855f525c05bSJohan Hovold }
856f525c05bSJohan Hovold 
sierra_port_probe(struct usb_serial_port * port)857f525c05bSJohan Hovold static int sierra_port_probe(struct usb_serial_port *port)
858f525c05bSJohan Hovold {
859f525c05bSJohan Hovold 	struct usb_serial *serial = port->serial;
860f525c05bSJohan Hovold 	struct sierra_port_private *portdata;
861e0439cd9SJohan Hovold 	const struct sierra_iface_list *himemory_list;
862f525c05bSJohan Hovold 	u8 ifnum;
863f525c05bSJohan Hovold 
864f525c05bSJohan Hovold 	portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
865f525c05bSJohan Hovold 	if (!portdata)
866f525c05bSJohan Hovold 		return -ENOMEM;
867f525c05bSJohan Hovold 
86817c23274SGreg Kroah-Hartman 	spin_lock_init(&portdata->lock);
869e6929a90SOliver Neukum 	init_usb_anchor(&portdata->active);
870e6929a90SOliver Neukum 	init_usb_anchor(&portdata->delayed);
871f525c05bSJohan Hovold 
8723a2b808eSElina Pasheva 	/* Assume low memory requirements */
8733a2b808eSElina Pasheva 	portdata->num_out_urbs = N_OUT_URB;
8743a2b808eSElina Pasheva 	portdata->num_in_urbs  = N_IN_URB;
8753a2b808eSElina Pasheva 
8763a2b808eSElina Pasheva 	/* Determine actual memory requirements */
8773a2b808eSElina Pasheva 	if (serial->num_ports == 1) {
8783a2b808eSElina Pasheva 		/* Get interface number for composite device */
87916620b48SJohan Hovold 		ifnum = sierra_interface_num(serial);
880e0439cd9SJohan Hovold 		himemory_list = &typeB_interface_list;
881f525c05bSJohan Hovold 	} else {
882f525c05bSJohan Hovold 		/* This is really the usb-serial port number of the interface
883f525c05bSJohan Hovold 		 * rather than the interface number.
884f525c05bSJohan Hovold 		 */
8851143832eSGreg Kroah-Hartman 		ifnum = port->port_number;
886e0439cd9SJohan Hovold 		himemory_list = &typeA_interface_list;
887f525c05bSJohan Hovold 	}
888f525c05bSJohan Hovold 
889e0439cd9SJohan Hovold 	if (is_listed(ifnum, himemory_list)) {
8903a2b808eSElina Pasheva 		portdata->num_out_urbs = N_OUT_URB_HM;
8913a2b808eSElina Pasheva 		portdata->num_in_urbs  = N_IN_URB_HM;
8923a2b808eSElina Pasheva 	}
893f525c05bSJohan Hovold 
894f525c05bSJohan Hovold 	dev_dbg(&port->dev,
8953a2b808eSElina Pasheva 			"Memory usage (urbs) interface #%d, in=%d, out=%d\n",
8963a2b808eSElina Pasheva 			ifnum, portdata->num_in_urbs, portdata->num_out_urbs);
897f525c05bSJohan Hovold 
898033a3fb9SKevin Lloyd 	usb_set_serial_port_data(port, portdata);
899033a3fb9SKevin Lloyd 
90017c23274SGreg Kroah-Hartman 	return 0;
901033a3fb9SKevin Lloyd }
902033a3fb9SKevin Lloyd 
sierra_port_remove(struct usb_serial_port * port)903c5d1448fSUwe Kleine-König static void sierra_port_remove(struct usb_serial_port *port)
904033a3fb9SKevin Lloyd {
905033a3fb9SKevin Lloyd 	struct sierra_port_private *portdata;
906033a3fb9SKevin Lloyd 
907033a3fb9SKevin Lloyd 	portdata = usb_get_serial_port_data(port);
9088452727dSJohan Hovold 	usb_set_serial_port_data(port, NULL);
9099e85c5f6SGreg Kroah-Hartman 	kfree(portdata);
910033a3fb9SKevin Lloyd }
911033a3fb9SKevin Lloyd 
912cd604513SAndrew Morton #ifdef CONFIG_PM
stop_read_write_urbs(struct usb_serial * serial)913e6929a90SOliver Neukum static void stop_read_write_urbs(struct usb_serial *serial)
914e6929a90SOliver Neukum {
915b64dc0a5SElina Pasheva 	int i;
916e6929a90SOliver Neukum 	struct usb_serial_port *port;
917e6929a90SOliver Neukum 	struct sierra_port_private *portdata;
918e6929a90SOliver Neukum 
919e6929a90SOliver Neukum 	/* Stop reading/writing urbs */
920e6929a90SOliver Neukum 	for (i = 0; i < serial->num_ports; ++i) {
921e6929a90SOliver Neukum 		port = serial->port[i];
922e6929a90SOliver Neukum 		portdata = usb_get_serial_port_data(port);
9238452727dSJohan Hovold 		if (!portdata)
9248452727dSJohan Hovold 			continue;
925b64dc0a5SElina Pasheva 		sierra_stop_rx_urbs(port);
926e6929a90SOliver Neukum 		usb_kill_anchored_urbs(&portdata->active);
927e6929a90SOliver Neukum 	}
928e6929a90SOliver Neukum }
929e6929a90SOliver Neukum 
sierra_suspend(struct usb_serial * serial,pm_message_t message)930e6929a90SOliver Neukum static int sierra_suspend(struct usb_serial *serial, pm_message_t message)
931e6929a90SOliver Neukum {
9327d8825beSJohan Hovold 	struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
933e6929a90SOliver Neukum 
934e6929a90SOliver Neukum 	spin_lock_irq(&intfdata->susp_lock);
9357d8825beSJohan Hovold 	if (PMSG_IS_AUTO(message)) {
9367d8825beSJohan Hovold 		if (intfdata->in_flight) {
937e6929a90SOliver Neukum 			spin_unlock_irq(&intfdata->susp_lock);
938e6929a90SOliver Neukum 			return -EBUSY;
9397d8825beSJohan Hovold 		}
9407d8825beSJohan Hovold 	}
941e6929a90SOliver Neukum 	intfdata->suspended = 1;
942e6929a90SOliver Neukum 	spin_unlock_irq(&intfdata->susp_lock);
9437d8825beSJohan Hovold 
944e6929a90SOliver Neukum 	stop_read_write_urbs(serial);
945e6929a90SOliver Neukum 
946e6929a90SOliver Neukum 	return 0;
947e6929a90SOliver Neukum }
948e6929a90SOliver Neukum 
94971c149b9SJohan Hovold /* Caller must hold susp_lock. */
sierra_submit_delayed_urbs(struct usb_serial_port * port)95071c149b9SJohan Hovold static int sierra_submit_delayed_urbs(struct usb_serial_port *port)
95171c149b9SJohan Hovold {
95271c149b9SJohan Hovold 	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
95371c149b9SJohan Hovold 	struct sierra_intf_private *intfdata;
95471c149b9SJohan Hovold 	struct urb *urb;
95571c149b9SJohan Hovold 	int ec = 0;
95671c149b9SJohan Hovold 	int err;
95771c149b9SJohan Hovold 
95871c149b9SJohan Hovold 	intfdata = usb_get_serial_data(port->serial);
95971c149b9SJohan Hovold 
96071c149b9SJohan Hovold 	for (;;) {
96171c149b9SJohan Hovold 		urb = usb_get_from_anchor(&portdata->delayed);
96271c149b9SJohan Hovold 		if (!urb)
96371c149b9SJohan Hovold 			break;
96471c149b9SJohan Hovold 
96571c149b9SJohan Hovold 		usb_anchor_urb(urb, &portdata->active);
96671c149b9SJohan Hovold 		intfdata->in_flight++;
96771c149b9SJohan Hovold 		err = usb_submit_urb(urb, GFP_ATOMIC);
96871c149b9SJohan Hovold 		if (err) {
96971c149b9SJohan Hovold 			dev_err(&port->dev, "%s - submit urb failed: %d",
97071c149b9SJohan Hovold 					__func__, err);
97171c149b9SJohan Hovold 			ec++;
97271c149b9SJohan Hovold 			intfdata->in_flight--;
97371c149b9SJohan Hovold 			usb_unanchor_urb(urb);
97471c149b9SJohan Hovold 			kfree(urb->transfer_buffer);
97571c149b9SJohan Hovold 			usb_free_urb(urb);
97671c149b9SJohan Hovold 
97771c149b9SJohan Hovold 			spin_lock(&portdata->lock);
97871c149b9SJohan Hovold 			portdata->outstanding_urbs--;
97971c149b9SJohan Hovold 			spin_unlock(&portdata->lock);
98071c149b9SJohan Hovold 		}
98171c149b9SJohan Hovold 	}
98271c149b9SJohan Hovold 
98371c149b9SJohan Hovold 	if (ec)
98471c149b9SJohan Hovold 		return -EIO;
98571c149b9SJohan Hovold 
98671c149b9SJohan Hovold 	return 0;
98771c149b9SJohan Hovold }
98871c149b9SJohan Hovold 
sierra_resume(struct usb_serial * serial)989e6929a90SOliver Neukum static int sierra_resume(struct usb_serial *serial)
990e6929a90SOliver Neukum {
991e6929a90SOliver Neukum 	struct usb_serial_port *port;
9927c80782eSJohan Hovold 	struct sierra_intf_private *intfdata = usb_get_serial_data(serial);
993e6929a90SOliver Neukum 	int ec = 0;
994e6929a90SOliver Neukum 	int i, err;
995e6929a90SOliver Neukum 
996e6929a90SOliver Neukum 	spin_lock_irq(&intfdata->susp_lock);
997e6929a90SOliver Neukum 	for (i = 0; i < serial->num_ports; i++) {
998e6929a90SOliver Neukum 		port = serial->port[i];
999e6929a90SOliver Neukum 
1000d41861caSPeter Hurley 		if (!tty_port_initialized(&port->port))
10018452727dSJohan Hovold 			continue;
10028452727dSJohan Hovold 
100371c149b9SJohan Hovold 		err = sierra_submit_delayed_urbs(port);
100471c149b9SJohan Hovold 		if (err)
1005f4a2d499SJohan Hovold 			ec++;
1006e6929a90SOliver Neukum 
1007e6929a90SOliver Neukum 		err = sierra_submit_rx_urbs(port, GFP_ATOMIC);
1008e6929a90SOliver Neukum 		if (err)
1009e6929a90SOliver Neukum 			ec++;
1010e6929a90SOliver Neukum 	}
1011e6929a90SOliver Neukum 	intfdata->suspended = 0;
1012e6929a90SOliver Neukum 	spin_unlock_irq(&intfdata->susp_lock);
1013e6929a90SOliver Neukum 
1014e6929a90SOliver Neukum 	return ec ? -EIO : 0;
1015e6929a90SOliver Neukum }
10167650cd96SElina Pasheva 
1017cd604513SAndrew Morton #else
1018cd604513SAndrew Morton #define sierra_suspend NULL
1019cd604513SAndrew Morton #define sierra_resume NULL
1020cd604513SAndrew Morton #endif
1021e6929a90SOliver Neukum 
1022228426edSKevin Lloyd static struct usb_serial_driver sierra_device = {
1023964ee1deSGreg Kroah-Hartman 	.driver = {
1024964ee1deSGreg Kroah-Hartman 		.owner =	THIS_MODULE,
10254e0fee82SKevin Lloyd 		.name =		"sierra",
1026964ee1deSGreg Kroah-Hartman 	},
1027228426edSKevin Lloyd 	.description       = "Sierra USB modem",
1028228426edSKevin Lloyd 	.id_table          = id_table,
1029228426edSKevin Lloyd 	.calc_num_ports	   = sierra_calc_num_ports,
1030228426edSKevin Lloyd 	.probe		   = sierra_probe,
1031964ee1deSGreg Kroah-Hartman 	.open              = sierra_open,
1032964ee1deSGreg Kroah-Hartman 	.close             = sierra_close,
1033335f8514SAlan Cox 	.dtr_rts	   = sierra_dtr_rts,
1034964ee1deSGreg Kroah-Hartman 	.write             = sierra_write,
1035964ee1deSGreg Kroah-Hartman 	.write_room        = sierra_write_room,
103693670599SJohan Hovold 	.chars_in_buffer   = sierra_chars_in_buffer,
1037964ee1deSGreg Kroah-Hartman 	.tiocmget          = sierra_tiocmget,
1038964ee1deSGreg Kroah-Hartman 	.tiocmset          = sierra_tiocmset,
1039964ee1deSGreg Kroah-Hartman 	.attach            = sierra_startup,
10407bae0a07SAlan Stern 	.release           = sierra_release,
1041f525c05bSJohan Hovold 	.port_probe        = sierra_port_probe,
1042f525c05bSJohan Hovold 	.port_remove       = sierra_port_remove,
1043e6929a90SOliver Neukum 	.suspend	   = sierra_suspend,
1044e6929a90SOliver Neukum 	.resume		   = sierra_resume,
1045964ee1deSGreg Kroah-Hartman 	.read_int_callback = sierra_instat_callback,
1046964ee1deSGreg Kroah-Hartman };
1047964ee1deSGreg Kroah-Hartman 
1048d860322fSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
1049d860322fSAlan Stern 	&sierra_device, NULL
1050d860322fSAlan Stern };
1051d860322fSAlan Stern 
105268e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table);
1053964ee1deSGreg Kroah-Hartman 
1054033a3fb9SKevin Lloyd MODULE_AUTHOR(DRIVER_AUTHOR);
1055033a3fb9SKevin Lloyd MODULE_DESCRIPTION(DRIVER_DESC);
1056627cfa89SJohan Hovold MODULE_LICENSE("GPL v2");
1057033a3fb9SKevin Lloyd 
1058a65ab973SUtkarsh Verma module_param(nmea, bool, 0644);
1059228426edSKevin Lloyd MODULE_PARM_DESC(nmea, "NMEA streaming");
1060