xref: /openbmc/linux/drivers/usb/class/cdc-wdm.c (revision 12a98b2b)
1afba937eSOliver Neukum /*
2afba937eSOliver Neukum  * cdc-wdm.c
3afba937eSOliver Neukum  *
4afba937eSOliver Neukum  * This driver supports USB CDC WCM Device Management.
5afba937eSOliver Neukum  *
6052fbc0dSOliver Neukum  * Copyright (c) 2007-2009 Oliver Neukum
7afba937eSOliver Neukum  *
8afba937eSOliver Neukum  * Some code taken from cdc-acm.c
9afba937eSOliver Neukum  *
10afba937eSOliver Neukum  * Released under the GPLv2.
11afba937eSOliver Neukum  *
12afba937eSOliver Neukum  * Many thanks to Carl Nordbeck
13afba937eSOliver Neukum  */
14afba937eSOliver Neukum #include <linux/kernel.h>
15afba937eSOliver Neukum #include <linux/errno.h>
16afba937eSOliver Neukum #include <linux/slab.h>
17afba937eSOliver Neukum #include <linux/module.h>
18afba937eSOliver Neukum #include <linux/mutex.h>
19afba937eSOliver Neukum #include <linux/uaccess.h>
20afba937eSOliver Neukum #include <linux/bitops.h>
21afba937eSOliver Neukum #include <linux/poll.h>
22afba937eSOliver Neukum #include <linux/usb.h>
23afba937eSOliver Neukum #include <linux/usb/cdc.h>
24afba937eSOliver Neukum #include <asm/byteorder.h>
25afba937eSOliver Neukum #include <asm/unaligned.h>
263cc36157SBjørn Mork #include <linux/usb/cdc-wdm.h>
27afba937eSOliver Neukum 
28afba937eSOliver Neukum /*
29afba937eSOliver Neukum  * Version Information
30afba937eSOliver Neukum  */
3187d65e54SOliver Neukum #define DRIVER_VERSION "v0.03"
32afba937eSOliver Neukum #define DRIVER_AUTHOR "Oliver Neukum"
3387d65e54SOliver Neukum #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
34afba937eSOliver Neukum 
35fec67b45SBjørn Mork #define HUAWEI_VENDOR_ID	0x12D1
36fec67b45SBjørn Mork 
376ef4852bSNémeth Márton static const struct usb_device_id wdm_ids[] = {
38afba937eSOliver Neukum 	{
39afba937eSOliver Neukum 		.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
40afba937eSOliver Neukum 				 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
41afba937eSOliver Neukum 		.bInterfaceClass = USB_CLASS_COMM,
42afba937eSOliver Neukum 		.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
43afba937eSOliver Neukum 	},
44fec67b45SBjørn Mork 	{
45fec67b45SBjørn Mork 		/*
46fec67b45SBjørn Mork 		 * Huawei E392, E398 and possibly other Qualcomm based modems
47fec67b45SBjørn Mork 		 * embed the Qualcomm QMI protocol inside CDC on CDC ECM like
48fec67b45SBjørn Mork 		 * control interfaces.  Userspace access to this is required
49fec67b45SBjørn Mork 		 * to configure the accompanying data interface
50fec67b45SBjørn Mork 		 */
51fec67b45SBjørn Mork 		.match_flags        = USB_DEVICE_ID_MATCH_VENDOR |
52fec67b45SBjørn Mork 					USB_DEVICE_ID_MATCH_INT_INFO,
53fec67b45SBjørn Mork 		.idVendor           = HUAWEI_VENDOR_ID,
54fec67b45SBjørn Mork 		.bInterfaceClass    = USB_CLASS_VENDOR_SPEC,
55fec67b45SBjørn Mork 		.bInterfaceSubClass = 1,
56fec67b45SBjørn Mork 		.bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
57fec67b45SBjørn Mork 	},
58afba937eSOliver Neukum 	{ }
59afba937eSOliver Neukum };
60afba937eSOliver Neukum 
61aa5380b9SOliver Neukum MODULE_DEVICE_TABLE (usb, wdm_ids);
62aa5380b9SOliver Neukum 
63afba937eSOliver Neukum #define WDM_MINOR_BASE	176
64afba937eSOliver Neukum 
65afba937eSOliver Neukum 
66afba937eSOliver Neukum #define WDM_IN_USE		1
67afba937eSOliver Neukum #define WDM_DISCONNECTING	2
68afba937eSOliver Neukum #define WDM_RESULT		3
69afba937eSOliver Neukum #define WDM_READ		4
70afba937eSOliver Neukum #define WDM_INT_STALL		5
71afba937eSOliver Neukum #define WDM_POLL_RUNNING	6
72922a5eadSOliver Neukum #define WDM_RESPONDING		7
73beb1d35fSOliver Neukum #define WDM_SUSPENDING		8
7488044202SBjørn Mork #define WDM_RESETTING		9
75afba937eSOliver Neukum 
76afba937eSOliver Neukum #define WDM_MAX			16
77afba937eSOliver Neukum 
787e3054a0SBjørn Mork /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */
797e3054a0SBjørn Mork #define WDM_DEFAULT_BUFSIZE	256
80afba937eSOliver Neukum 
81afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex);
82b0c13860SBjørn Mork static DEFINE_SPINLOCK(wdm_device_list_lock);
83b0c13860SBjørn Mork static LIST_HEAD(wdm_device_list);
84afba937eSOliver Neukum 
85afba937eSOliver Neukum /* --- method tables --- */
86afba937eSOliver Neukum 
87afba937eSOliver Neukum struct wdm_device {
88afba937eSOliver Neukum 	u8			*inbuf; /* buffer for response */
89afba937eSOliver Neukum 	u8			*outbuf; /* buffer for command */
90afba937eSOliver Neukum 	u8			*sbuf; /* buffer for status */
91afba937eSOliver Neukum 	u8			*ubuf; /* buffer for copy to user space */
92afba937eSOliver Neukum 
93afba937eSOliver Neukum 	struct urb		*command;
94afba937eSOliver Neukum 	struct urb		*response;
95afba937eSOliver Neukum 	struct urb		*validity;
96afba937eSOliver Neukum 	struct usb_interface	*intf;
97afba937eSOliver Neukum 	struct usb_ctrlrequest	*orq;
98afba937eSOliver Neukum 	struct usb_ctrlrequest	*irq;
99afba937eSOliver Neukum 	spinlock_t		iuspin;
100afba937eSOliver Neukum 
101afba937eSOliver Neukum 	unsigned long		flags;
102afba937eSOliver Neukum 	u16			bufsize;
103afba937eSOliver Neukum 	u16			wMaxCommand;
104afba937eSOliver Neukum 	u16			wMaxPacketSize;
105afba937eSOliver Neukum 	__le16			inum;
106afba937eSOliver Neukum 	int			reslength;
107afba937eSOliver Neukum 	int			length;
108afba937eSOliver Neukum 	int			read;
109afba937eSOliver Neukum 	int			count;
110afba937eSOliver Neukum 	dma_addr_t		shandle;
111afba937eSOliver Neukum 	dma_addr_t		ihandle;
112e8537bd2SBjørn Mork 	struct mutex		wlock;
113e8537bd2SBjørn Mork 	struct mutex		rlock;
114afba937eSOliver Neukum 	wait_queue_head_t	wait;
115afba937eSOliver Neukum 	struct work_struct	rxwork;
116afba937eSOliver Neukum 	int			werr;
117afba937eSOliver Neukum 	int			rerr;
118b0c13860SBjørn Mork 
119b0c13860SBjørn Mork 	struct list_head	device_list;
1203cc36157SBjørn Mork 	int			(*manage_power)(struct usb_interface *, int);
121afba937eSOliver Neukum };
122afba937eSOliver Neukum 
123afba937eSOliver Neukum static struct usb_driver wdm_driver;
124afba937eSOliver Neukum 
125b0c13860SBjørn Mork /* return intfdata if we own the interface, else look up intf in the list */
126b0c13860SBjørn Mork static struct wdm_device *wdm_find_device(struct usb_interface *intf)
127b0c13860SBjørn Mork {
128b0c13860SBjørn Mork 	struct wdm_device *desc = NULL;
129b0c13860SBjørn Mork 
130b0c13860SBjørn Mork 	spin_lock(&wdm_device_list_lock);
131b0c13860SBjørn Mork 	list_for_each_entry(desc, &wdm_device_list, device_list)
132b0c13860SBjørn Mork 		if (desc->intf == intf)
133b0c13860SBjørn Mork 			break;
134b0c13860SBjørn Mork 	spin_unlock(&wdm_device_list_lock);
135b0c13860SBjørn Mork 
136b0c13860SBjørn Mork 	return desc;
137b0c13860SBjørn Mork }
138b0c13860SBjørn Mork 
139b0c13860SBjørn Mork static struct wdm_device *wdm_find_device_by_minor(int minor)
140b0c13860SBjørn Mork {
141b0c13860SBjørn Mork 	struct wdm_device *desc = NULL;
142b0c13860SBjørn Mork 
143b0c13860SBjørn Mork 	spin_lock(&wdm_device_list_lock);
144b0c13860SBjørn Mork 	list_for_each_entry(desc, &wdm_device_list, device_list)
145b0c13860SBjørn Mork 		if (desc->intf->minor == minor)
146b0c13860SBjørn Mork 			break;
147b0c13860SBjørn Mork 	spin_unlock(&wdm_device_list_lock);
148b0c13860SBjørn Mork 
149b0c13860SBjørn Mork 	return desc;
150b0c13860SBjørn Mork }
151b0c13860SBjørn Mork 
152afba937eSOliver Neukum /* --- callbacks --- */
153afba937eSOliver Neukum static void wdm_out_callback(struct urb *urb)
154afba937eSOliver Neukum {
155afba937eSOliver Neukum 	struct wdm_device *desc;
156afba937eSOliver Neukum 	desc = urb->context;
157afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
158afba937eSOliver Neukum 	desc->werr = urb->status;
159afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
160afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
161afba937eSOliver Neukum 	kfree(desc->outbuf);
162afba937eSOliver Neukum 	wake_up(&desc->wait);
163afba937eSOliver Neukum }
164afba937eSOliver Neukum 
165afba937eSOliver Neukum static void wdm_in_callback(struct urb *urb)
166afba937eSOliver Neukum {
167afba937eSOliver Neukum 	struct wdm_device *desc = urb->context;
168afba937eSOliver Neukum 	int status = urb->status;
169afba937eSOliver Neukum 
170afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
171922a5eadSOliver Neukum 	clear_bit(WDM_RESPONDING, &desc->flags);
172afba937eSOliver Neukum 
173afba937eSOliver Neukum 	if (status) {
174afba937eSOliver Neukum 		switch (status) {
175afba937eSOliver Neukum 		case -ENOENT:
176afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
177afba937eSOliver Neukum 				"nonzero urb status received: -ENOENT");
178922a5eadSOliver Neukum 			goto skip_error;
179afba937eSOliver Neukum 		case -ECONNRESET:
180afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
181afba937eSOliver Neukum 				"nonzero urb status received: -ECONNRESET");
182922a5eadSOliver Neukum 			goto skip_error;
183afba937eSOliver Neukum 		case -ESHUTDOWN:
184afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
185afba937eSOliver Neukum 				"nonzero urb status received: -ESHUTDOWN");
186922a5eadSOliver Neukum 			goto skip_error;
187afba937eSOliver Neukum 		case -EPIPE:
1889908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1899908a32eSGreg Kroah-Hartman 				"nonzero urb status received: -EPIPE\n");
190afba937eSOliver Neukum 			break;
191afba937eSOliver Neukum 		default:
1929908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1939908a32eSGreg Kroah-Hartman 				"Unexpected error %d\n", status);
194afba937eSOliver Neukum 			break;
195afba937eSOliver Neukum 		}
196afba937eSOliver Neukum 	}
197afba937eSOliver Neukum 
198afba937eSOliver Neukum 	desc->rerr = status;
199afba937eSOliver Neukum 	desc->reslength = urb->actual_length;
200afba937eSOliver Neukum 	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
201afba937eSOliver Neukum 	desc->length += desc->reslength;
202922a5eadSOliver Neukum skip_error:
203afba937eSOliver Neukum 	wake_up(&desc->wait);
204afba937eSOliver Neukum 
205afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
206afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
207afba937eSOliver Neukum }
208afba937eSOliver Neukum 
209afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb)
210afba937eSOliver Neukum {
211afba937eSOliver Neukum 	int rv = 0;
212afba937eSOliver Neukum 	int status = urb->status;
213afba937eSOliver Neukum 	struct wdm_device *desc;
214afba937eSOliver Neukum 	struct usb_cdc_notification *dr;
215afba937eSOliver Neukum 
216afba937eSOliver Neukum 	desc = urb->context;
217afba937eSOliver Neukum 	dr = (struct usb_cdc_notification *)desc->sbuf;
218afba937eSOliver Neukum 
219afba937eSOliver Neukum 	if (status) {
220afba937eSOliver Neukum 		switch (status) {
221afba937eSOliver Neukum 		case -ESHUTDOWN:
222afba937eSOliver Neukum 		case -ENOENT:
223afba937eSOliver Neukum 		case -ECONNRESET:
224afba937eSOliver Neukum 			return; /* unplug */
225afba937eSOliver Neukum 		case -EPIPE:
226afba937eSOliver Neukum 			set_bit(WDM_INT_STALL, &desc->flags);
2279908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev, "Stall on int endpoint\n");
228afba937eSOliver Neukum 			goto sw; /* halt is cleared in work */
229afba937eSOliver Neukum 		default:
2309908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
2319908a32eSGreg Kroah-Hartman 				"nonzero urb status received: %d\n", status);
232afba937eSOliver Neukum 			break;
233afba937eSOliver Neukum 		}
234afba937eSOliver Neukum 	}
235afba937eSOliver Neukum 
236afba937eSOliver Neukum 	if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
2379908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
2389908a32eSGreg Kroah-Hartman 			urb->actual_length);
239afba937eSOliver Neukum 		goto exit;
240afba937eSOliver Neukum 	}
241afba937eSOliver Neukum 
242afba937eSOliver Neukum 	switch (dr->bNotificationType) {
243afba937eSOliver Neukum 	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
244afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev,
245afba937eSOliver Neukum 			"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
246afba937eSOliver Neukum 			dr->wIndex, dr->wLength);
247afba937eSOliver Neukum 		break;
248afba937eSOliver Neukum 
249afba937eSOliver Neukum 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
250afba937eSOliver Neukum 
251afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev,
252afba937eSOliver Neukum 			"NOTIFY_NETWORK_CONNECTION %s network",
253afba937eSOliver Neukum 			dr->wValue ? "connected to" : "disconnected from");
254afba937eSOliver Neukum 		goto exit;
255afba937eSOliver Neukum 	default:
256afba937eSOliver Neukum 		clear_bit(WDM_POLL_RUNNING, &desc->flags);
2579908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev,
2589908a32eSGreg Kroah-Hartman 			"unknown notification %d received: index %d len %d\n",
259afba937eSOliver Neukum 			dr->bNotificationType, dr->wIndex, dr->wLength);
260afba937eSOliver Neukum 		goto exit;
261afba937eSOliver Neukum 	}
262afba937eSOliver Neukum 
263afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
264afba937eSOliver Neukum 	clear_bit(WDM_READ, &desc->flags);
265922a5eadSOliver Neukum 	set_bit(WDM_RESPONDING, &desc->flags);
266beb1d35fSOliver Neukum 	if (!test_bit(WDM_DISCONNECTING, &desc->flags)
267beb1d35fSOliver Neukum 		&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
268afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_ATOMIC);
269afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
270afba937eSOliver Neukum 			__func__, rv);
271afba937eSOliver Neukum 	}
272afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
273afba937eSOliver Neukum 	if (rv < 0) {
274922a5eadSOliver Neukum 		clear_bit(WDM_RESPONDING, &desc->flags);
275afba937eSOliver Neukum 		if (rv == -EPERM)
276afba937eSOliver Neukum 			return;
277afba937eSOliver Neukum 		if (rv == -ENOMEM) {
278afba937eSOliver Neukum sw:
279afba937eSOliver Neukum 			rv = schedule_work(&desc->rxwork);
280afba937eSOliver Neukum 			if (rv)
2819908a32eSGreg Kroah-Hartman 				dev_err(&desc->intf->dev,
2829908a32eSGreg Kroah-Hartman 					"Cannot schedule work\n");
283afba937eSOliver Neukum 		}
284afba937eSOliver Neukum 	}
285afba937eSOliver Neukum exit:
286afba937eSOliver Neukum 	rv = usb_submit_urb(urb, GFP_ATOMIC);
287afba937eSOliver Neukum 	if (rv)
2889908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev,
2899908a32eSGreg Kroah-Hartman 			"%s - usb_submit_urb failed with result %d\n",
290afba937eSOliver Neukum 			__func__, rv);
291afba937eSOliver Neukum 
292afba937eSOliver Neukum }
293afba937eSOliver Neukum 
294afba937eSOliver Neukum static void kill_urbs(struct wdm_device *desc)
295afba937eSOliver Neukum {
29617d80d56SOliver Neukum 	/* the order here is essential */
297afba937eSOliver Neukum 	usb_kill_urb(desc->command);
298afba937eSOliver Neukum 	usb_kill_urb(desc->validity);
299afba937eSOliver Neukum 	usb_kill_urb(desc->response);
300afba937eSOliver Neukum }
301afba937eSOliver Neukum 
302afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc)
303afba937eSOliver Neukum {
304afba937eSOliver Neukum 	usb_free_urb(desc->validity);
305afba937eSOliver Neukum 	usb_free_urb(desc->response);
306afba937eSOliver Neukum 	usb_free_urb(desc->command);
307afba937eSOliver Neukum }
308afba937eSOliver Neukum 
309afba937eSOliver Neukum static void cleanup(struct wdm_device *desc)
310afba937eSOliver Neukum {
311b0c13860SBjørn Mork 	spin_lock(&wdm_device_list_lock);
312b0c13860SBjørn Mork 	list_del(&desc->device_list);
313b0c13860SBjørn Mork 	spin_unlock(&wdm_device_list_lock);
3148457d99cSBjørn Mork 	kfree(desc->sbuf);
3158457d99cSBjørn Mork 	kfree(desc->inbuf);
316afba937eSOliver Neukum 	kfree(desc->orq);
317afba937eSOliver Neukum 	kfree(desc->irq);
318afba937eSOliver Neukum 	kfree(desc->ubuf);
319afba937eSOliver Neukum 	free_urbs(desc);
320afba937eSOliver Neukum 	kfree(desc);
321afba937eSOliver Neukum }
322afba937eSOliver Neukum 
323afba937eSOliver Neukum static ssize_t wdm_write
324afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
325afba937eSOliver Neukum {
326afba937eSOliver Neukum 	u8 *buf;
327afba937eSOliver Neukum 	int rv = -EMSGSIZE, r, we;
328afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
329afba937eSOliver Neukum 	struct usb_ctrlrequest *req;
330afba937eSOliver Neukum 
331afba937eSOliver Neukum 	if (count > desc->wMaxCommand)
332afba937eSOliver Neukum 		count = desc->wMaxCommand;
333afba937eSOliver Neukum 
334afba937eSOliver Neukum 	spin_lock_irq(&desc->iuspin);
335afba937eSOliver Neukum 	we = desc->werr;
336afba937eSOliver Neukum 	desc->werr = 0;
337afba937eSOliver Neukum 	spin_unlock_irq(&desc->iuspin);
338afba937eSOliver Neukum 	if (we < 0)
339afba937eSOliver Neukum 		return -EIO;
340afba937eSOliver Neukum 
341860e41a7SOliver Neukum 	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
342860e41a7SOliver Neukum 	if (!buf) {
343860e41a7SOliver Neukum 		rv = -ENOMEM;
344afba937eSOliver Neukum 		goto outnl;
345860e41a7SOliver Neukum 	}
346860e41a7SOliver Neukum 
347860e41a7SOliver Neukum 	r = copy_from_user(buf, buffer, count);
348860e41a7SOliver Neukum 	if (r > 0) {
349860e41a7SOliver Neukum 		kfree(buf);
350860e41a7SOliver Neukum 		rv = -EFAULT;
351860e41a7SOliver Neukum 		goto outnl;
352860e41a7SOliver Neukum 	}
353860e41a7SOliver Neukum 
354860e41a7SOliver Neukum 	/* concurrent writes and disconnect */
355e8537bd2SBjørn Mork 	r = mutex_lock_interruptible(&desc->wlock);
356860e41a7SOliver Neukum 	rv = -ERESTARTSYS;
357860e41a7SOliver Neukum 	if (r) {
358860e41a7SOliver Neukum 		kfree(buf);
359860e41a7SOliver Neukum 		goto outnl;
360860e41a7SOliver Neukum 	}
361860e41a7SOliver Neukum 
362860e41a7SOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
363860e41a7SOliver Neukum 		kfree(buf);
364860e41a7SOliver Neukum 		rv = -ENODEV;
365860e41a7SOliver Neukum 		goto outnp;
366860e41a7SOliver Neukum 	}
367afba937eSOliver Neukum 
36817d80d56SOliver Neukum 	r = usb_autopm_get_interface(desc->intf);
369860e41a7SOliver Neukum 	if (r < 0) {
370860e41a7SOliver Neukum 		kfree(buf);
37112a98b2bSOliver Neukum 		rv = usb_translate_errors(r);
37217d80d56SOliver Neukum 		goto outnp;
373860e41a7SOliver Neukum 	}
3747f1dc313SOliver Neukum 
3750cdfb819SDavid Sterba 	if (!(file->f_flags & O_NONBLOCK))
376afba937eSOliver Neukum 		r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
377afba937eSOliver Neukum 								&desc->flags));
3787f1dc313SOliver Neukum 	else
3797f1dc313SOliver Neukum 		if (test_bit(WDM_IN_USE, &desc->flags))
3807f1dc313SOliver Neukum 			r = -EAGAIN;
38188044202SBjørn Mork 
38288044202SBjørn Mork 	if (test_bit(WDM_RESETTING, &desc->flags))
38388044202SBjørn Mork 		r = -EIO;
38488044202SBjørn Mork 
385860e41a7SOliver Neukum 	if (r < 0) {
386afba937eSOliver Neukum 		kfree(buf);
38712a98b2bSOliver Neukum 		rv = r;
388afba937eSOliver Neukum 		goto out;
389afba937eSOliver Neukum 	}
390afba937eSOliver Neukum 
391afba937eSOliver Neukum 	req = desc->orq;
392afba937eSOliver Neukum 	usb_fill_control_urb(
393afba937eSOliver Neukum 		desc->command,
394afba937eSOliver Neukum 		interface_to_usbdev(desc->intf),
395afba937eSOliver Neukum 		/* using common endpoint 0 */
396afba937eSOliver Neukum 		usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
397afba937eSOliver Neukum 		(unsigned char *)req,
398afba937eSOliver Neukum 		buf,
399afba937eSOliver Neukum 		count,
400afba937eSOliver Neukum 		wdm_out_callback,
401afba937eSOliver Neukum 		desc
402afba937eSOliver Neukum 	);
403afba937eSOliver Neukum 
404afba937eSOliver Neukum 	req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
405afba937eSOliver Neukum 			     USB_RECIP_INTERFACE);
406afba937eSOliver Neukum 	req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
407afba937eSOliver Neukum 	req->wValue = 0;
408afba937eSOliver Neukum 	req->wIndex = desc->inum;
409afba937eSOliver Neukum 	req->wLength = cpu_to_le16(count);
410afba937eSOliver Neukum 	set_bit(WDM_IN_USE, &desc->flags);
411afba937eSOliver Neukum 
412afba937eSOliver Neukum 	rv = usb_submit_urb(desc->command, GFP_KERNEL);
413afba937eSOliver Neukum 	if (rv < 0) {
414afba937eSOliver Neukum 		kfree(buf);
415afba937eSOliver Neukum 		clear_bit(WDM_IN_USE, &desc->flags);
4169908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
41712a98b2bSOliver Neukum 		rv = usb_translate_errors(rv);
418afba937eSOliver Neukum 	} else {
419afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
420afba937eSOliver Neukum 			req->wIndex);
421afba937eSOliver Neukum 	}
422afba937eSOliver Neukum out:
42317d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
42417d80d56SOliver Neukum outnp:
425e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
426afba937eSOliver Neukum outnl:
427afba937eSOliver Neukum 	return rv < 0 ? rv : count;
428afba937eSOliver Neukum }
429afba937eSOliver Neukum 
430afba937eSOliver Neukum static ssize_t wdm_read
431afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
432afba937eSOliver Neukum {
433711c68b3SBen Hutchings 	int rv, cntr;
434afba937eSOliver Neukum 	int i = 0;
435afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
436afba937eSOliver Neukum 
437afba937eSOliver Neukum 
438e8537bd2SBjørn Mork 	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
439afba937eSOliver Neukum 	if (rv < 0)
440afba937eSOliver Neukum 		return -ERESTARTSYS;
441afba937eSOliver Neukum 
442711c68b3SBen Hutchings 	cntr = ACCESS_ONCE(desc->length);
443711c68b3SBen Hutchings 	if (cntr == 0) {
444afba937eSOliver Neukum 		desc->read = 0;
445afba937eSOliver Neukum retry:
4467f1dc313SOliver Neukum 		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
4477f1dc313SOliver Neukum 			rv = -ENODEV;
4487f1dc313SOliver Neukum 			goto err;
4497f1dc313SOliver Neukum 		}
450afba937eSOliver Neukum 		i++;
4517f1dc313SOliver Neukum 		if (file->f_flags & O_NONBLOCK) {
4527f1dc313SOliver Neukum 			if (!test_bit(WDM_READ, &desc->flags)) {
4537f1dc313SOliver Neukum 				rv = cntr ? cntr : -EAGAIN;
4547f1dc313SOliver Neukum 				goto err;
4557f1dc313SOliver Neukum 			}
4567f1dc313SOliver Neukum 			rv = 0;
4577f1dc313SOliver Neukum 		} else {
458afba937eSOliver Neukum 			rv = wait_event_interruptible(desc->wait,
459afba937eSOliver Neukum 				test_bit(WDM_READ, &desc->flags));
4607f1dc313SOliver Neukum 		}
461afba937eSOliver Neukum 
4627f1dc313SOliver Neukum 		/* may have happened while we slept */
46317d80d56SOliver Neukum 		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
46417d80d56SOliver Neukum 			rv = -ENODEV;
46517d80d56SOliver Neukum 			goto err;
46617d80d56SOliver Neukum 		}
46788044202SBjørn Mork 		if (test_bit(WDM_RESETTING, &desc->flags)) {
46888044202SBjørn Mork 			rv = -EIO;
46988044202SBjørn Mork 			goto err;
47088044202SBjørn Mork 		}
47117d80d56SOliver Neukum 		usb_mark_last_busy(interface_to_usbdev(desc->intf));
472afba937eSOliver Neukum 		if (rv < 0) {
473afba937eSOliver Neukum 			rv = -ERESTARTSYS;
474afba937eSOliver Neukum 			goto err;
475afba937eSOliver Neukum 		}
476afba937eSOliver Neukum 
477afba937eSOliver Neukum 		spin_lock_irq(&desc->iuspin);
478afba937eSOliver Neukum 
479afba937eSOliver Neukum 		if (desc->rerr) { /* read completed, error happened */
480afba937eSOliver Neukum 			desc->rerr = 0;
481afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
482afba937eSOliver Neukum 			rv = -EIO;
483afba937eSOliver Neukum 			goto err;
484afba937eSOliver Neukum 		}
485afba937eSOliver Neukum 		/*
486afba937eSOliver Neukum 		 * recheck whether we've lost the race
487afba937eSOliver Neukum 		 * against the completion handler
488afba937eSOliver Neukum 		 */
489afba937eSOliver Neukum 		if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
490afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
491afba937eSOliver Neukum 			goto retry;
492afba937eSOliver Neukum 		}
493afba937eSOliver Neukum 		if (!desc->reslength) { /* zero length read */
494afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
495afba937eSOliver Neukum 			goto retry;
496afba937eSOliver Neukum 		}
497711c68b3SBen Hutchings 		cntr = desc->length;
498afba937eSOliver Neukum 		spin_unlock_irq(&desc->iuspin);
499afba937eSOliver Neukum 	}
500afba937eSOliver Neukum 
501711c68b3SBen Hutchings 	if (cntr > count)
502711c68b3SBen Hutchings 		cntr = count;
503afba937eSOliver Neukum 	rv = copy_to_user(buffer, desc->ubuf, cntr);
504afba937eSOliver Neukum 	if (rv > 0) {
505afba937eSOliver Neukum 		rv = -EFAULT;
506afba937eSOliver Neukum 		goto err;
507afba937eSOliver Neukum 	}
508afba937eSOliver Neukum 
509711c68b3SBen Hutchings 	spin_lock_irq(&desc->iuspin);
510711c68b3SBen Hutchings 
511afba937eSOliver Neukum 	for (i = 0; i < desc->length - cntr; i++)
512afba937eSOliver Neukum 		desc->ubuf[i] = desc->ubuf[i + cntr];
513afba937eSOliver Neukum 
514afba937eSOliver Neukum 	desc->length -= cntr;
51587d65e54SOliver Neukum 	/* in case we had outstanding data */
51687d65e54SOliver Neukum 	if (!desc->length)
51787d65e54SOliver Neukum 		clear_bit(WDM_READ, &desc->flags);
518711c68b3SBen Hutchings 
519711c68b3SBen Hutchings 	spin_unlock_irq(&desc->iuspin);
520711c68b3SBen Hutchings 
521afba937eSOliver Neukum 	rv = cntr;
522afba937eSOliver Neukum 
523afba937eSOliver Neukum err:
524e8537bd2SBjørn Mork 	mutex_unlock(&desc->rlock);
525afba937eSOliver Neukum 	return rv;
526afba937eSOliver Neukum }
527afba937eSOliver Neukum 
528afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id)
529afba937eSOliver Neukum {
530afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
531afba937eSOliver Neukum 
532afba937eSOliver Neukum 	wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
533afba937eSOliver Neukum 	if (desc->werr < 0)
5349908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Error in flush path: %d\n",
5359908a32eSGreg Kroah-Hartman 			desc->werr);
536afba937eSOliver Neukum 
53724a85baeSOliver Neukum 	return usb_translate_errors(desc->werr);
538afba937eSOliver Neukum }
539afba937eSOliver Neukum 
540afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
541afba937eSOliver Neukum {
542afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
543afba937eSOliver Neukum 	unsigned long flags;
544afba937eSOliver Neukum 	unsigned int mask = 0;
545afba937eSOliver Neukum 
546afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
547afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
548afba937eSOliver Neukum 		mask = POLLERR;
549afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
550afba937eSOliver Neukum 		goto desc_out;
551afba937eSOliver Neukum 	}
552afba937eSOliver Neukum 	if (test_bit(WDM_READ, &desc->flags))
553afba937eSOliver Neukum 		mask = POLLIN | POLLRDNORM;
554afba937eSOliver Neukum 	if (desc->rerr || desc->werr)
555afba937eSOliver Neukum 		mask |= POLLERR;
556afba937eSOliver Neukum 	if (!test_bit(WDM_IN_USE, &desc->flags))
557afba937eSOliver Neukum 		mask |= POLLOUT | POLLWRNORM;
558afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
559afba937eSOliver Neukum 
560afba937eSOliver Neukum 	poll_wait(file, &desc->wait, wait);
561afba937eSOliver Neukum 
562afba937eSOliver Neukum desc_out:
563afba937eSOliver Neukum 	return mask;
564afba937eSOliver Neukum }
565afba937eSOliver Neukum 
566afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file)
567afba937eSOliver Neukum {
568afba937eSOliver Neukum 	int minor = iminor(inode);
569afba937eSOliver Neukum 	int rv = -ENODEV;
570afba937eSOliver Neukum 	struct usb_interface *intf;
571afba937eSOliver Neukum 	struct wdm_device *desc;
572afba937eSOliver Neukum 
573afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
574b0c13860SBjørn Mork 	desc = wdm_find_device_by_minor(minor);
575b0c13860SBjørn Mork 	if (!desc)
576afba937eSOliver Neukum 		goto out;
577afba937eSOliver Neukum 
578b0c13860SBjørn Mork 	intf = desc->intf;
579afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags))
580afba937eSOliver Neukum 		goto out;
581afba937eSOliver Neukum 	file->private_data = desc;
582afba937eSOliver Neukum 
58317d80d56SOliver Neukum 	rv = usb_autopm_get_interface(desc->intf);
58417d80d56SOliver Neukum 	if (rv < 0) {
5859908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
58617d80d56SOliver Neukum 		goto out;
58717d80d56SOliver Neukum 	}
588afba937eSOliver Neukum 
589e8537bd2SBjørn Mork 	/* using write lock to protect desc->count */
590e8537bd2SBjørn Mork 	mutex_lock(&desc->wlock);
59117d80d56SOliver Neukum 	if (!desc->count++) {
592d771d8aaSOliver Neukum 		desc->werr = 0;
593d771d8aaSOliver Neukum 		desc->rerr = 0;
59417d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_KERNEL);
595afba937eSOliver Neukum 		if (rv < 0) {
596afba937eSOliver Neukum 			desc->count--;
5979908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
5989908a32eSGreg Kroah-Hartman 				"Error submitting int urb - %d\n", rv);
59912a98b2bSOliver Neukum 			rv = usb_translate_errors(rv);
600afba937eSOliver Neukum 		}
60117d80d56SOliver Neukum 	} else {
602afba937eSOliver Neukum 		rv = 0;
60317d80d56SOliver Neukum 	}
604e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
6053cc36157SBjørn Mork 	if (desc->count == 1)
6063cc36157SBjørn Mork 		desc->manage_power(intf, 1);
60717d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
608afba937eSOliver Neukum out:
609afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
610afba937eSOliver Neukum 	return rv;
611afba937eSOliver Neukum }
612afba937eSOliver Neukum 
613afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file)
614afba937eSOliver Neukum {
615afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
616afba937eSOliver Neukum 
617afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
618e8537bd2SBjørn Mork 
619e8537bd2SBjørn Mork 	/* using write lock to protect desc->count */
620e8537bd2SBjørn Mork 	mutex_lock(&desc->wlock);
621afba937eSOliver Neukum 	desc->count--;
622e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
62317d80d56SOliver Neukum 
624afba937eSOliver Neukum 	if (!desc->count) {
625afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
626afba937eSOliver Neukum 		kill_urbs(desc);
627880bca3aSBjørn Mork 		if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
6283cc36157SBjørn Mork 			desc->manage_power(desc->intf, 0);
629880bca3aSBjørn Mork 		} else {
630880bca3aSBjørn Mork 			dev_dbg(&desc->intf->dev, "%s: device gone - cleaning up\n", __func__);
6312f338c8aSOliver Neukum 			cleanup(desc);
632afba937eSOliver Neukum 		}
633880bca3aSBjørn Mork 	}
634afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
635afba937eSOliver Neukum 	return 0;
636afba937eSOliver Neukum }
637afba937eSOliver Neukum 
638afba937eSOliver Neukum static const struct file_operations wdm_fops = {
639afba937eSOliver Neukum 	.owner =	THIS_MODULE,
640afba937eSOliver Neukum 	.read =		wdm_read,
641afba937eSOliver Neukum 	.write =	wdm_write,
642afba937eSOliver Neukum 	.open =		wdm_open,
643afba937eSOliver Neukum 	.flush =	wdm_flush,
644afba937eSOliver Neukum 	.release =	wdm_release,
6456038f373SArnd Bergmann 	.poll =		wdm_poll,
6466038f373SArnd Bergmann 	.llseek =	noop_llseek,
647afba937eSOliver Neukum };
648afba937eSOliver Neukum 
649afba937eSOliver Neukum static struct usb_class_driver wdm_class = {
650afba937eSOliver Neukum 	.name =		"cdc-wdm%d",
651afba937eSOliver Neukum 	.fops =		&wdm_fops,
652afba937eSOliver Neukum 	.minor_base =	WDM_MINOR_BASE,
653afba937eSOliver Neukum };
654afba937eSOliver Neukum 
655afba937eSOliver Neukum /* --- error handling --- */
656afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work)
657afba937eSOliver Neukum {
658afba937eSOliver Neukum 	struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
659afba937eSOliver Neukum 	unsigned long flags;
660afba937eSOliver Neukum 	int rv;
661afba937eSOliver Neukum 
662afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
663afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
664afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
665afba937eSOliver Neukum 	} else {
666afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
667afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_KERNEL);
668afba937eSOliver Neukum 		if (rv < 0 && rv != -EPERM) {
669afba937eSOliver Neukum 			spin_lock_irqsave(&desc->iuspin, flags);
670afba937eSOliver Neukum 			if (!test_bit(WDM_DISCONNECTING, &desc->flags))
671afba937eSOliver Neukum 				schedule_work(&desc->rxwork);
672afba937eSOliver Neukum 			spin_unlock_irqrestore(&desc->iuspin, flags);
673afba937eSOliver Neukum 		}
674afba937eSOliver Neukum 	}
675afba937eSOliver Neukum }
676afba937eSOliver Neukum 
677afba937eSOliver Neukum /* --- hotplug --- */
678afba937eSOliver Neukum 
6793cc36157SBjørn Mork static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
6803cc36157SBjørn Mork 		u16 bufsize, int (*manage_power)(struct usb_interface *, int))
681afba937eSOliver Neukum {
6820dffb486SBjørn Mork 	int rv = -ENOMEM;
683afba937eSOliver Neukum 	struct wdm_device *desc;
684afba937eSOliver Neukum 
685afba937eSOliver Neukum 	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
686afba937eSOliver Neukum 	if (!desc)
687afba937eSOliver Neukum 		goto out;
688b0c13860SBjørn Mork 	INIT_LIST_HEAD(&desc->device_list);
689e8537bd2SBjørn Mork 	mutex_init(&desc->rlock);
690e8537bd2SBjørn Mork 	mutex_init(&desc->wlock);
691afba937eSOliver Neukum 	spin_lock_init(&desc->iuspin);
692afba937eSOliver Neukum 	init_waitqueue_head(&desc->wait);
6930dffb486SBjørn Mork 	desc->wMaxCommand = bufsize;
694052fbc0dSOliver Neukum 	/* this will be expanded and needed in hardware endianness */
695afba937eSOliver Neukum 	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
696afba937eSOliver Neukum 	desc->intf = intf;
697afba937eSOliver Neukum 	INIT_WORK(&desc->rxwork, wdm_rxwork);
698afba937eSOliver Neukum 
699afba937eSOliver Neukum 	rv = -EINVAL;
7000dffb486SBjørn Mork 	if (!usb_endpoint_is_int_in(ep))
701052fbc0dSOliver Neukum 		goto err;
702afba937eSOliver Neukum 
70329cc8897SKuninori Morimoto 	desc->wMaxPacketSize = usb_endpoint_maxp(ep);
704afba937eSOliver Neukum 
705afba937eSOliver Neukum 	desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
706afba937eSOliver Neukum 	if (!desc->orq)
707afba937eSOliver Neukum 		goto err;
708afba937eSOliver Neukum 	desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
709afba937eSOliver Neukum 	if (!desc->irq)
710afba937eSOliver Neukum 		goto err;
711afba937eSOliver Neukum 
712afba937eSOliver Neukum 	desc->validity = usb_alloc_urb(0, GFP_KERNEL);
713afba937eSOliver Neukum 	if (!desc->validity)
714afba937eSOliver Neukum 		goto err;
715afba937eSOliver Neukum 
716afba937eSOliver Neukum 	desc->response = usb_alloc_urb(0, GFP_KERNEL);
717afba937eSOliver Neukum 	if (!desc->response)
718afba937eSOliver Neukum 		goto err;
719afba937eSOliver Neukum 
720afba937eSOliver Neukum 	desc->command = usb_alloc_urb(0, GFP_KERNEL);
721afba937eSOliver Neukum 	if (!desc->command)
722afba937eSOliver Neukum 		goto err;
723afba937eSOliver Neukum 
724afba937eSOliver Neukum 	desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
725afba937eSOliver Neukum 	if (!desc->ubuf)
726afba937eSOliver Neukum 		goto err;
727afba937eSOliver Neukum 
7288457d99cSBjørn Mork 	desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
729afba937eSOliver Neukum 	if (!desc->sbuf)
730afba937eSOliver Neukum 		goto err;
731afba937eSOliver Neukum 
7328457d99cSBjørn Mork 	desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
733afba937eSOliver Neukum 	if (!desc->inbuf)
7348457d99cSBjørn Mork 		goto err;
735afba937eSOliver Neukum 
736afba937eSOliver Neukum 	usb_fill_int_urb(
737afba937eSOliver Neukum 		desc->validity,
738afba937eSOliver Neukum 		interface_to_usbdev(intf),
739afba937eSOliver Neukum 		usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
740afba937eSOliver Neukum 		desc->sbuf,
741afba937eSOliver Neukum 		desc->wMaxPacketSize,
742afba937eSOliver Neukum 		wdm_int_callback,
743afba937eSOliver Neukum 		desc,
744afba937eSOliver Neukum 		ep->bInterval
745afba937eSOliver Neukum 	);
746afba937eSOliver Neukum 
74719b85b3bSBjørn Mork 	desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
74819b85b3bSBjørn Mork 	desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
74919b85b3bSBjørn Mork 	desc->irq->wValue = 0;
75019b85b3bSBjørn Mork 	desc->irq->wIndex = desc->inum;
75119b85b3bSBjørn Mork 	desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
75219b85b3bSBjørn Mork 
75319b85b3bSBjørn Mork 	usb_fill_control_urb(
75419b85b3bSBjørn Mork 		desc->response,
7558143a896SBjørn Mork 		interface_to_usbdev(intf),
75619b85b3bSBjørn Mork 		/* using common endpoint 0 */
75719b85b3bSBjørn Mork 		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
75819b85b3bSBjørn Mork 		(unsigned char *)desc->irq,
75919b85b3bSBjørn Mork 		desc->inbuf,
76019b85b3bSBjørn Mork 		desc->wMaxCommand,
76119b85b3bSBjørn Mork 		wdm_in_callback,
76219b85b3bSBjørn Mork 		desc
76319b85b3bSBjørn Mork 	);
76419b85b3bSBjørn Mork 
7653cc36157SBjørn Mork 	desc->manage_power = manage_power;
7663cc36157SBjørn Mork 
767b0c13860SBjørn Mork 	spin_lock(&wdm_device_list_lock);
768b0c13860SBjørn Mork 	list_add(&desc->device_list, &wdm_device_list);
769b0c13860SBjørn Mork 	spin_unlock(&wdm_device_list_lock);
770b0c13860SBjørn Mork 
771afba937eSOliver Neukum 	rv = usb_register_dev(intf, &wdm_class);
772052fbc0dSOliver Neukum 	if (rv < 0)
773b0c13860SBjørn Mork 		goto err;
774052fbc0dSOliver Neukum 	else
775820c629aSBjørn Mork 		dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
776afba937eSOliver Neukum out:
777afba937eSOliver Neukum 	return rv;
778afba937eSOliver Neukum err:
7790dffb486SBjørn Mork 	cleanup(desc);
7800dffb486SBjørn Mork 	return rv;
7810dffb486SBjørn Mork }
7820dffb486SBjørn Mork 
7833cc36157SBjørn Mork static int wdm_manage_power(struct usb_interface *intf, int on)
7843cc36157SBjørn Mork {
7853cc36157SBjørn Mork 	/* need autopm_get/put here to ensure the usbcore sees the new value */
7863cc36157SBjørn Mork 	int rv = usb_autopm_get_interface(intf);
7873cc36157SBjørn Mork 	if (rv < 0)
7883cc36157SBjørn Mork 		goto err;
7893cc36157SBjørn Mork 
7903cc36157SBjørn Mork 	intf->needs_remote_wakeup = on;
7913cc36157SBjørn Mork 	usb_autopm_put_interface(intf);
7923cc36157SBjørn Mork err:
7933cc36157SBjørn Mork 	return rv;
7943cc36157SBjørn Mork }
7953cc36157SBjørn Mork 
7960dffb486SBjørn Mork static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
7970dffb486SBjørn Mork {
7980dffb486SBjørn Mork 	int rv = -EINVAL;
7990dffb486SBjørn Mork 	struct usb_host_interface *iface;
8000dffb486SBjørn Mork 	struct usb_endpoint_descriptor *ep;
8010dffb486SBjørn Mork 	struct usb_cdc_dmm_desc *dmhd;
8020dffb486SBjørn Mork 	u8 *buffer = intf->altsetting->extra;
8030dffb486SBjørn Mork 	int buflen = intf->altsetting->extralen;
8040dffb486SBjørn Mork 	u16 maxcom = WDM_DEFAULT_BUFSIZE;
8050dffb486SBjørn Mork 
8060dffb486SBjørn Mork 	if (!buffer)
8070dffb486SBjørn Mork 		goto err;
8080dffb486SBjørn Mork 	while (buflen > 2) {
8090dffb486SBjørn Mork 		if (buffer[1] != USB_DT_CS_INTERFACE) {
8100dffb486SBjørn Mork 			dev_err(&intf->dev, "skipping garbage\n");
8110dffb486SBjørn Mork 			goto next_desc;
8120dffb486SBjørn Mork 		}
8130dffb486SBjørn Mork 
8140dffb486SBjørn Mork 		switch (buffer[2]) {
8150dffb486SBjørn Mork 		case USB_CDC_HEADER_TYPE:
8160dffb486SBjørn Mork 			break;
8170dffb486SBjørn Mork 		case USB_CDC_DMM_TYPE:
8180dffb486SBjørn Mork 			dmhd = (struct usb_cdc_dmm_desc *)buffer;
8190dffb486SBjørn Mork 			maxcom = le16_to_cpu(dmhd->wMaxCommand);
8200dffb486SBjørn Mork 			dev_dbg(&intf->dev,
8210dffb486SBjørn Mork 				"Finding maximum buffer length: %d", maxcom);
8220dffb486SBjørn Mork 			break;
8230dffb486SBjørn Mork 		default:
8240dffb486SBjørn Mork 			dev_err(&intf->dev,
8250dffb486SBjørn Mork 				"Ignoring extra header, type %d, length %d\n",
8260dffb486SBjørn Mork 				buffer[2], buffer[0]);
8270dffb486SBjørn Mork 			break;
8280dffb486SBjørn Mork 		}
8290dffb486SBjørn Mork next_desc:
8300dffb486SBjørn Mork 		buflen -= buffer[0];
8310dffb486SBjørn Mork 		buffer += buffer[0];
8320dffb486SBjørn Mork 	}
8330dffb486SBjørn Mork 
8340dffb486SBjørn Mork 	iface = intf->cur_altsetting;
8350dffb486SBjørn Mork 	if (iface->desc.bNumEndpoints != 1)
8360dffb486SBjørn Mork 		goto err;
8370dffb486SBjørn Mork 	ep = &iface->endpoint[0].desc;
8380dffb486SBjørn Mork 
8393cc36157SBjørn Mork 	rv = wdm_create(intf, ep, maxcom, &wdm_manage_power);
8400dffb486SBjørn Mork 
8410dffb486SBjørn Mork err:
842afba937eSOliver Neukum 	return rv;
843afba937eSOliver Neukum }
844afba937eSOliver Neukum 
8453cc36157SBjørn Mork /**
8463cc36157SBjørn Mork  * usb_cdc_wdm_register - register a WDM subdriver
8473cc36157SBjørn Mork  * @intf: usb interface the subdriver will associate with
8483cc36157SBjørn Mork  * @ep: interrupt endpoint to monitor for notifications
8493cc36157SBjørn Mork  * @bufsize: maximum message size to support for read/write
8503cc36157SBjørn Mork  *
8513cc36157SBjørn Mork  * Create WDM usb class character device and associate it with intf
8523cc36157SBjørn Mork  * without binding, allowing another driver to manage the interface.
8533cc36157SBjørn Mork  *
8543cc36157SBjørn Mork  * The subdriver will manage the given interrupt endpoint exclusively
8553cc36157SBjørn Mork  * and will issue control requests referring to the given intf. It
8563cc36157SBjørn Mork  * will otherwise avoid interferring, and in particular not do
8573cc36157SBjørn Mork  * usb_set_intfdata/usb_get_intfdata on intf.
8583cc36157SBjørn Mork  *
8593cc36157SBjørn Mork  * The return value is a pointer to the subdriver's struct usb_driver.
8603cc36157SBjørn Mork  * The registering driver is responsible for calling this subdriver's
8613cc36157SBjørn Mork  * disconnect, suspend, resume, pre_reset and post_reset methods from
8623cc36157SBjørn Mork  * its own.
8633cc36157SBjørn Mork  */
8643cc36157SBjørn Mork struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf,
8653cc36157SBjørn Mork 					struct usb_endpoint_descriptor *ep,
8663cc36157SBjørn Mork 					int bufsize,
8673cc36157SBjørn Mork 					int (*manage_power)(struct usb_interface *, int))
8683cc36157SBjørn Mork {
8693cc36157SBjørn Mork 	int rv = -EINVAL;
8703cc36157SBjørn Mork 
8713cc36157SBjørn Mork 	rv = wdm_create(intf, ep, bufsize, manage_power);
8723cc36157SBjørn Mork 	if (rv < 0)
8733cc36157SBjørn Mork 		goto err;
8743cc36157SBjørn Mork 
8753cc36157SBjørn Mork 	return &wdm_driver;
8763cc36157SBjørn Mork err:
8773cc36157SBjørn Mork 	return ERR_PTR(rv);
8783cc36157SBjørn Mork }
8793cc36157SBjørn Mork EXPORT_SYMBOL(usb_cdc_wdm_register);
8803cc36157SBjørn Mork 
881afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf)
882afba937eSOliver Neukum {
883afba937eSOliver Neukum 	struct wdm_device *desc;
884afba937eSOliver Neukum 	unsigned long flags;
885afba937eSOliver Neukum 
886afba937eSOliver Neukum 	usb_deregister_dev(intf, &wdm_class);
887b0c13860SBjørn Mork 	desc = wdm_find_device(intf);
888afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
889afba937eSOliver Neukum 
890afba937eSOliver Neukum 	/* the spinlock makes sure no new urbs are generated in the callbacks */
891afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
892afba937eSOliver Neukum 	set_bit(WDM_DISCONNECTING, &desc->flags);
893afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
89417d80d56SOliver Neukum 	/* to terminate pending flushes */
895afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
896afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
89762aaf24dSBjørn Mork 	wake_up_all(&desc->wait);
898e8537bd2SBjørn Mork 	mutex_lock(&desc->rlock);
899e8537bd2SBjørn Mork 	mutex_lock(&desc->wlock);
900afba937eSOliver Neukum 	kill_urbs(desc);
901d93d16e9SOliver Neukum 	cancel_work_sync(&desc->rxwork);
902e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
903e8537bd2SBjørn Mork 	mutex_unlock(&desc->rlock);
904afba937eSOliver Neukum 	if (!desc->count)
905afba937eSOliver Neukum 		cleanup(desc);
906880bca3aSBjørn Mork 	else
907880bca3aSBjørn Mork 		dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count);
908afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
909afba937eSOliver Neukum }
910afba937eSOliver Neukum 
911d93d16e9SOliver Neukum #ifdef CONFIG_PM
91217d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
91317d80d56SOliver Neukum {
914b0c13860SBjørn Mork 	struct wdm_device *desc = wdm_find_device(intf);
91517d80d56SOliver Neukum 	int rv = 0;
91617d80d56SOliver Neukum 
91717d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
91817d80d56SOliver Neukum 
919d93d16e9SOliver Neukum 	/* if this is an autosuspend the caller does the locking */
920e8537bd2SBjørn Mork 	if (!PMSG_IS_AUTO(message)) {
921e8537bd2SBjørn Mork 		mutex_lock(&desc->rlock);
922e8537bd2SBjørn Mork 		mutex_lock(&desc->wlock);
923e8537bd2SBjørn Mork 	}
92462e66854SOliver Neukum 	spin_lock_irq(&desc->iuspin);
925d93d16e9SOliver Neukum 
9265b1b0b81SAlan Stern 	if (PMSG_IS_AUTO(message) &&
927922a5eadSOliver Neukum 			(test_bit(WDM_IN_USE, &desc->flags)
928922a5eadSOliver Neukum 			|| test_bit(WDM_RESPONDING, &desc->flags))) {
92962e66854SOliver Neukum 		spin_unlock_irq(&desc->iuspin);
93017d80d56SOliver Neukum 		rv = -EBUSY;
93117d80d56SOliver Neukum 	} else {
932d93d16e9SOliver Neukum 
933beb1d35fSOliver Neukum 		set_bit(WDM_SUSPENDING, &desc->flags);
93462e66854SOliver Neukum 		spin_unlock_irq(&desc->iuspin);
935d93d16e9SOliver Neukum 		/* callback submits work - order is essential */
93617d80d56SOliver Neukum 		kill_urbs(desc);
937d93d16e9SOliver Neukum 		cancel_work_sync(&desc->rxwork);
93817d80d56SOliver Neukum 	}
939e8537bd2SBjørn Mork 	if (!PMSG_IS_AUTO(message)) {
940e8537bd2SBjørn Mork 		mutex_unlock(&desc->wlock);
941e8537bd2SBjørn Mork 		mutex_unlock(&desc->rlock);
942e8537bd2SBjørn Mork 	}
94317d80d56SOliver Neukum 
94417d80d56SOliver Neukum 	return rv;
94517d80d56SOliver Neukum }
946d93d16e9SOliver Neukum #endif
94717d80d56SOliver Neukum 
94817d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc)
94917d80d56SOliver Neukum {
95017d80d56SOliver Neukum 	int rv = 0;
95117d80d56SOliver Neukum 
95217d80d56SOliver Neukum 	if (desc->count) {
95317d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_NOIO);
95417d80d56SOliver Neukum 		if (rv < 0)
9559908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
9569908a32eSGreg Kroah-Hartman 				"Error resume submitting int urb - %d\n", rv);
95717d80d56SOliver Neukum 	}
95817d80d56SOliver Neukum 	return rv;
95917d80d56SOliver Neukum }
960d93d16e9SOliver Neukum 
961d93d16e9SOliver Neukum #ifdef CONFIG_PM
96217d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf)
96317d80d56SOliver Neukum {
964b0c13860SBjørn Mork 	struct wdm_device *desc = wdm_find_device(intf);
96517d80d56SOliver Neukum 	int rv;
96617d80d56SOliver Neukum 
96717d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
968338124c1SOliver Neukum 
969beb1d35fSOliver Neukum 	clear_bit(WDM_SUSPENDING, &desc->flags);
97062e66854SOliver Neukum 	rv = recover_from_urb_loss(desc);
971338124c1SOliver Neukum 
97217d80d56SOliver Neukum 	return rv;
97317d80d56SOliver Neukum }
974d93d16e9SOliver Neukum #endif
97517d80d56SOliver Neukum 
97617d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf)
97717d80d56SOliver Neukum {
978b0c13860SBjørn Mork 	struct wdm_device *desc = wdm_find_device(intf);
97917d80d56SOliver Neukum 
980d771d8aaSOliver Neukum 	/*
981d771d8aaSOliver Neukum 	 * we notify everybody using poll of
982d771d8aaSOliver Neukum 	 * an exceptional situation
983d771d8aaSOliver Neukum 	 * must be done before recovery lest a spontaneous
984d771d8aaSOliver Neukum 	 * message from the device is lost
985d771d8aaSOliver Neukum 	 */
986d771d8aaSOliver Neukum 	spin_lock_irq(&desc->iuspin);
98788044202SBjørn Mork 	set_bit(WDM_RESETTING, &desc->flags);	/* inform read/write */
98888044202SBjørn Mork 	set_bit(WDM_READ, &desc->flags);	/* unblock read */
98988044202SBjørn Mork 	clear_bit(WDM_IN_USE, &desc->flags);	/* unblock write */
990d771d8aaSOliver Neukum 	desc->rerr = -EINTR;
991d771d8aaSOliver Neukum 	spin_unlock_irq(&desc->iuspin);
992d771d8aaSOliver Neukum 	wake_up_all(&desc->wait);
99388044202SBjørn Mork 	mutex_lock(&desc->rlock);
99488044202SBjørn Mork 	mutex_lock(&desc->wlock);
99588044202SBjørn Mork 	kill_urbs(desc);
99688044202SBjørn Mork 	cancel_work_sync(&desc->rxwork);
99717d80d56SOliver Neukum 	return 0;
99817d80d56SOliver Neukum }
99917d80d56SOliver Neukum 
100017d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf)
100117d80d56SOliver Neukum {
1002b0c13860SBjørn Mork 	struct wdm_device *desc = wdm_find_device(intf);
100317d80d56SOliver Neukum 	int rv;
100417d80d56SOliver Neukum 
100588044202SBjørn Mork 	clear_bit(WDM_RESETTING, &desc->flags);
100617d80d56SOliver Neukum 	rv = recover_from_urb_loss(desc);
1007e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
1008e8537bd2SBjørn Mork 	mutex_unlock(&desc->rlock);
100917d80d56SOliver Neukum 	return 0;
101017d80d56SOliver Neukum }
101117d80d56SOliver Neukum 
1012afba937eSOliver Neukum static struct usb_driver wdm_driver = {
1013afba937eSOliver Neukum 	.name =		"cdc_wdm",
1014afba937eSOliver Neukum 	.probe =	wdm_probe,
1015afba937eSOliver Neukum 	.disconnect =	wdm_disconnect,
1016d93d16e9SOliver Neukum #ifdef CONFIG_PM
101717d80d56SOliver Neukum 	.suspend =	wdm_suspend,
101817d80d56SOliver Neukum 	.resume =	wdm_resume,
101917d80d56SOliver Neukum 	.reset_resume =	wdm_resume,
1020d93d16e9SOliver Neukum #endif
102117d80d56SOliver Neukum 	.pre_reset =	wdm_pre_reset,
102217d80d56SOliver Neukum 	.post_reset =	wdm_post_reset,
1023afba937eSOliver Neukum 	.id_table =	wdm_ids,
102417d80d56SOliver Neukum 	.supports_autosuspend = 1,
1025afba937eSOliver Neukum };
1026afba937eSOliver Neukum 
102765db4305SGreg Kroah-Hartman module_usb_driver(wdm_driver);
1028afba937eSOliver Neukum 
1029afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR);
103087d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC);
1031afba937eSOliver Neukum MODULE_LICENSE("GPL");
1032