xref: /openbmc/linux/drivers/usb/class/cdc-wdm.c (revision 052fbc0d)
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/smp_lock.h>
19afba937eSOliver Neukum #include <linux/mutex.h>
20afba937eSOliver Neukum #include <linux/uaccess.h>
21afba937eSOliver Neukum #include <linux/bitops.h>
22afba937eSOliver Neukum #include <linux/poll.h>
23afba937eSOliver Neukum #include <linux/usb.h>
24afba937eSOliver Neukum #include <linux/usb/cdc.h>
25afba937eSOliver Neukum #include <asm/byteorder.h>
26afba937eSOliver Neukum #include <asm/unaligned.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 
35afba937eSOliver Neukum static struct usb_device_id wdm_ids[] = {
36afba937eSOliver Neukum 	{
37afba937eSOliver Neukum 		.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
38afba937eSOliver Neukum 				 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
39afba937eSOliver Neukum 		.bInterfaceClass = USB_CLASS_COMM,
40afba937eSOliver Neukum 		.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
41afba937eSOliver Neukum 	},
42afba937eSOliver Neukum 	{ }
43afba937eSOliver Neukum };
44afba937eSOliver Neukum 
45aa5380b9SOliver Neukum MODULE_DEVICE_TABLE (usb, wdm_ids);
46aa5380b9SOliver Neukum 
47afba937eSOliver Neukum #define WDM_MINOR_BASE	176
48afba937eSOliver Neukum 
49afba937eSOliver Neukum 
50afba937eSOliver Neukum #define WDM_IN_USE		1
51afba937eSOliver Neukum #define WDM_DISCONNECTING	2
52afba937eSOliver Neukum #define WDM_RESULT		3
53afba937eSOliver Neukum #define WDM_READ		4
54afba937eSOliver Neukum #define WDM_INT_STALL		5
55afba937eSOliver Neukum #define WDM_POLL_RUNNING	6
56afba937eSOliver Neukum 
57afba937eSOliver Neukum 
58afba937eSOliver Neukum #define WDM_MAX			16
59afba937eSOliver Neukum 
60afba937eSOliver Neukum 
61afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex);
62afba937eSOliver Neukum 
63afba937eSOliver Neukum /* --- method tables --- */
64afba937eSOliver Neukum 
65afba937eSOliver Neukum struct wdm_device {
66afba937eSOliver Neukum 	u8			*inbuf; /* buffer for response */
67afba937eSOliver Neukum 	u8			*outbuf; /* buffer for command */
68afba937eSOliver Neukum 	u8			*sbuf; /* buffer for status */
69afba937eSOliver Neukum 	u8			*ubuf; /* buffer for copy to user space */
70afba937eSOliver Neukum 
71afba937eSOliver Neukum 	struct urb		*command;
72afba937eSOliver Neukum 	struct urb		*response;
73afba937eSOliver Neukum 	struct urb		*validity;
74afba937eSOliver Neukum 	struct usb_interface	*intf;
75afba937eSOliver Neukum 	struct usb_ctrlrequest	*orq;
76afba937eSOliver Neukum 	struct usb_ctrlrequest	*irq;
77afba937eSOliver Neukum 	spinlock_t		iuspin;
78afba937eSOliver Neukum 
79afba937eSOliver Neukum 	unsigned long		flags;
80afba937eSOliver Neukum 	u16			bufsize;
81afba937eSOliver Neukum 	u16			wMaxCommand;
82afba937eSOliver Neukum 	u16			wMaxPacketSize;
83afba937eSOliver Neukum 	u16			bMaxPacketSize0;
84afba937eSOliver Neukum 	__le16			inum;
85afba937eSOliver Neukum 	int			reslength;
86afba937eSOliver Neukum 	int			length;
87afba937eSOliver Neukum 	int			read;
88afba937eSOliver Neukum 	int			count;
89afba937eSOliver Neukum 	dma_addr_t		shandle;
90afba937eSOliver Neukum 	dma_addr_t		ihandle;
91afba937eSOliver Neukum 	struct mutex		wlock;
92afba937eSOliver Neukum 	struct mutex		rlock;
9317d80d56SOliver Neukum 	struct mutex		plock;
94afba937eSOliver Neukum 	wait_queue_head_t	wait;
95afba937eSOliver Neukum 	struct work_struct	rxwork;
96afba937eSOliver Neukum 	int			werr;
97afba937eSOliver Neukum 	int			rerr;
98afba937eSOliver Neukum };
99afba937eSOliver Neukum 
100afba937eSOliver Neukum static struct usb_driver wdm_driver;
101afba937eSOliver Neukum 
102afba937eSOliver Neukum /* --- callbacks --- */
103afba937eSOliver Neukum static void wdm_out_callback(struct urb *urb)
104afba937eSOliver Neukum {
105afba937eSOliver Neukum 	struct wdm_device *desc;
106afba937eSOliver Neukum 	desc = urb->context;
107afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
108afba937eSOliver Neukum 	desc->werr = urb->status;
109afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
110afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
111afba937eSOliver Neukum 	kfree(desc->outbuf);
112afba937eSOliver Neukum 	wake_up(&desc->wait);
113afba937eSOliver Neukum }
114afba937eSOliver Neukum 
115afba937eSOliver Neukum static void wdm_in_callback(struct urb *urb)
116afba937eSOliver Neukum {
117afba937eSOliver Neukum 	struct wdm_device *desc = urb->context;
118afba937eSOliver Neukum 	int status = urb->status;
119afba937eSOliver Neukum 
120afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
121afba937eSOliver Neukum 
122afba937eSOliver Neukum 	if (status) {
123afba937eSOliver Neukum 		switch (status) {
124afba937eSOliver Neukum 		case -ENOENT:
125afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
126afba937eSOliver Neukum 				"nonzero urb status received: -ENOENT");
127afba937eSOliver Neukum 			break;
128afba937eSOliver Neukum 		case -ECONNRESET:
129afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
130afba937eSOliver Neukum 				"nonzero urb status received: -ECONNRESET");
131afba937eSOliver Neukum 			break;
132afba937eSOliver Neukum 		case -ESHUTDOWN:
133afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
134afba937eSOliver Neukum 				"nonzero urb status received: -ESHUTDOWN");
135afba937eSOliver Neukum 			break;
136afba937eSOliver Neukum 		case -EPIPE:
1379908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1389908a32eSGreg Kroah-Hartman 				"nonzero urb status received: -EPIPE\n");
139afba937eSOliver Neukum 			break;
140afba937eSOliver Neukum 		default:
1419908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1429908a32eSGreg Kroah-Hartman 				"Unexpected error %d\n", status);
143afba937eSOliver Neukum 			break;
144afba937eSOliver Neukum 		}
145afba937eSOliver Neukum 	}
146afba937eSOliver Neukum 
147afba937eSOliver Neukum 	desc->rerr = status;
148afba937eSOliver Neukum 	desc->reslength = urb->actual_length;
149afba937eSOliver Neukum 	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
150afba937eSOliver Neukum 	desc->length += desc->reslength;
151afba937eSOliver Neukum 	wake_up(&desc->wait);
152afba937eSOliver Neukum 
153afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
154afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
155afba937eSOliver Neukum }
156afba937eSOliver Neukum 
157afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb)
158afba937eSOliver Neukum {
159afba937eSOliver Neukum 	int rv = 0;
160afba937eSOliver Neukum 	int status = urb->status;
161afba937eSOliver Neukum 	struct wdm_device *desc;
162afba937eSOliver Neukum 	struct usb_ctrlrequest *req;
163afba937eSOliver Neukum 	struct usb_cdc_notification *dr;
164afba937eSOliver Neukum 
165afba937eSOliver Neukum 	desc = urb->context;
166afba937eSOliver Neukum 	req = desc->irq;
167afba937eSOliver Neukum 	dr = (struct usb_cdc_notification *)desc->sbuf;
168afba937eSOliver Neukum 
169afba937eSOliver Neukum 	if (status) {
170afba937eSOliver Neukum 		switch (status) {
171afba937eSOliver Neukum 		case -ESHUTDOWN:
172afba937eSOliver Neukum 		case -ENOENT:
173afba937eSOliver Neukum 		case -ECONNRESET:
174afba937eSOliver Neukum 			return; /* unplug */
175afba937eSOliver Neukum 		case -EPIPE:
176afba937eSOliver Neukum 			set_bit(WDM_INT_STALL, &desc->flags);
1779908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev, "Stall on int endpoint\n");
178afba937eSOliver Neukum 			goto sw; /* halt is cleared in work */
179afba937eSOliver Neukum 		default:
1809908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1819908a32eSGreg Kroah-Hartman 				"nonzero urb status received: %d\n", status);
182afba937eSOliver Neukum 			break;
183afba937eSOliver Neukum 		}
184afba937eSOliver Neukum 	}
185afba937eSOliver Neukum 
186afba937eSOliver Neukum 	if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
1879908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
1889908a32eSGreg Kroah-Hartman 			urb->actual_length);
189afba937eSOliver Neukum 		goto exit;
190afba937eSOliver Neukum 	}
191afba937eSOliver Neukum 
192afba937eSOliver Neukum 	switch (dr->bNotificationType) {
193afba937eSOliver Neukum 	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
194afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev,
195afba937eSOliver Neukum 			"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
196afba937eSOliver Neukum 			dr->wIndex, dr->wLength);
197afba937eSOliver Neukum 		break;
198afba937eSOliver Neukum 
199afba937eSOliver Neukum 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
200afba937eSOliver Neukum 
201afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev,
202afba937eSOliver Neukum 			"NOTIFY_NETWORK_CONNECTION %s network",
203afba937eSOliver Neukum 			dr->wValue ? "connected to" : "disconnected from");
204afba937eSOliver Neukum 		goto exit;
205afba937eSOliver Neukum 	default:
206afba937eSOliver Neukum 		clear_bit(WDM_POLL_RUNNING, &desc->flags);
2079908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev,
2089908a32eSGreg Kroah-Hartman 			"unknown notification %d received: index %d len %d\n",
209afba937eSOliver Neukum 			dr->bNotificationType, dr->wIndex, dr->wLength);
210afba937eSOliver Neukum 		goto exit;
211afba937eSOliver Neukum 	}
212afba937eSOliver Neukum 
213afba937eSOliver Neukum 	req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
214afba937eSOliver Neukum 	req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
215afba937eSOliver Neukum 	req->wValue = 0;
216afba937eSOliver Neukum 	req->wIndex = desc->inum;
21787d65e54SOliver Neukum 	req->wLength = cpu_to_le16(desc->wMaxCommand);
218afba937eSOliver Neukum 
219afba937eSOliver Neukum 	usb_fill_control_urb(
220afba937eSOliver Neukum 		desc->response,
221afba937eSOliver Neukum 		interface_to_usbdev(desc->intf),
222afba937eSOliver Neukum 		/* using common endpoint 0 */
223afba937eSOliver Neukum 		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
224afba937eSOliver Neukum 		(unsigned char *)req,
225afba937eSOliver Neukum 		desc->inbuf,
22687d65e54SOliver Neukum 		desc->wMaxCommand,
227afba937eSOliver Neukum 		wdm_in_callback,
228afba937eSOliver Neukum 		desc
229afba937eSOliver Neukum 	);
230afba937eSOliver Neukum 	desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
231afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
232afba937eSOliver Neukum 	clear_bit(WDM_READ, &desc->flags);
233afba937eSOliver Neukum 	if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
234afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_ATOMIC);
235afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
236afba937eSOliver Neukum 			__func__, rv);
237afba937eSOliver Neukum 	}
238afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
239afba937eSOliver Neukum 	if (rv < 0) {
240afba937eSOliver Neukum 		if (rv == -EPERM)
241afba937eSOliver Neukum 			return;
242afba937eSOliver Neukum 		if (rv == -ENOMEM) {
243afba937eSOliver Neukum sw:
244afba937eSOliver Neukum 			rv = schedule_work(&desc->rxwork);
245afba937eSOliver Neukum 			if (rv)
2469908a32eSGreg Kroah-Hartman 				dev_err(&desc->intf->dev,
2479908a32eSGreg Kroah-Hartman 					"Cannot schedule work\n");
248afba937eSOliver Neukum 		}
249afba937eSOliver Neukum 	}
250afba937eSOliver Neukum exit:
251afba937eSOliver Neukum 	rv = usb_submit_urb(urb, GFP_ATOMIC);
252afba937eSOliver Neukum 	if (rv)
2539908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev,
2549908a32eSGreg Kroah-Hartman 			"%s - usb_submit_urb failed with result %d\n",
255afba937eSOliver Neukum 			__func__, rv);
256afba937eSOliver Neukum 
257afba937eSOliver Neukum }
258afba937eSOliver Neukum 
259afba937eSOliver Neukum static void kill_urbs(struct wdm_device *desc)
260afba937eSOliver Neukum {
26117d80d56SOliver Neukum 	/* the order here is essential */
262afba937eSOliver Neukum 	usb_kill_urb(desc->command);
263afba937eSOliver Neukum 	usb_kill_urb(desc->validity);
264afba937eSOliver Neukum 	usb_kill_urb(desc->response);
265afba937eSOliver Neukum }
266afba937eSOliver Neukum 
267afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc)
268afba937eSOliver Neukum {
269afba937eSOliver Neukum 	usb_free_urb(desc->validity);
270afba937eSOliver Neukum 	usb_free_urb(desc->response);
271afba937eSOliver Neukum 	usb_free_urb(desc->command);
272afba937eSOliver Neukum }
273afba937eSOliver Neukum 
274afba937eSOliver Neukum static void cleanup(struct wdm_device *desc)
275afba937eSOliver Neukum {
276afba937eSOliver Neukum 	usb_buffer_free(interface_to_usbdev(desc->intf),
277afba937eSOliver Neukum 			desc->wMaxPacketSize,
278afba937eSOliver Neukum 			desc->sbuf,
279afba937eSOliver Neukum 			desc->validity->transfer_dma);
280afba937eSOliver Neukum 	usb_buffer_free(interface_to_usbdev(desc->intf),
28187d65e54SOliver Neukum 			desc->wMaxCommand,
282afba937eSOliver Neukum 			desc->inbuf,
283afba937eSOliver Neukum 			desc->response->transfer_dma);
284afba937eSOliver Neukum 	kfree(desc->orq);
285afba937eSOliver Neukum 	kfree(desc->irq);
286afba937eSOliver Neukum 	kfree(desc->ubuf);
287afba937eSOliver Neukum 	free_urbs(desc);
288afba937eSOliver Neukum 	kfree(desc);
289afba937eSOliver Neukum }
290afba937eSOliver Neukum 
291afba937eSOliver Neukum static ssize_t wdm_write
292afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
293afba937eSOliver Neukum {
294afba937eSOliver Neukum 	u8 *buf;
295afba937eSOliver Neukum 	int rv = -EMSGSIZE, r, we;
296afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
297afba937eSOliver Neukum 	struct usb_ctrlrequest *req;
298afba937eSOliver Neukum 
299afba937eSOliver Neukum 	if (count > desc->wMaxCommand)
300afba937eSOliver Neukum 		count = desc->wMaxCommand;
301afba937eSOliver Neukum 
302afba937eSOliver Neukum 	spin_lock_irq(&desc->iuspin);
303afba937eSOliver Neukum 	we = desc->werr;
304afba937eSOliver Neukum 	desc->werr = 0;
305afba937eSOliver Neukum 	spin_unlock_irq(&desc->iuspin);
306afba937eSOliver Neukum 	if (we < 0)
307afba937eSOliver Neukum 		return -EIO;
308afba937eSOliver Neukum 
309afba937eSOliver Neukum 	r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */
310afba937eSOliver Neukum 	rv = -ERESTARTSYS;
311afba937eSOliver Neukum 	if (r)
312afba937eSOliver Neukum 		goto outnl;
313afba937eSOliver Neukum 
31417d80d56SOliver Neukum 	r = usb_autopm_get_interface(desc->intf);
31517d80d56SOliver Neukum 	if (r < 0)
31617d80d56SOliver Neukum 		goto outnp;
317afba937eSOliver Neukum 	r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
318afba937eSOliver Neukum 							   &desc->flags));
319afba937eSOliver Neukum 	if (r < 0)
320afba937eSOliver Neukum 		goto out;
321afba937eSOliver Neukum 
322afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
323afba937eSOliver Neukum 		rv = -ENODEV;
324afba937eSOliver Neukum 		goto out;
325afba937eSOliver Neukum 	}
326afba937eSOliver Neukum 
327afba937eSOliver Neukum 	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
328afba937eSOliver Neukum 	if (!buf) {
329afba937eSOliver Neukum 		rv = -ENOMEM;
330afba937eSOliver Neukum 		goto out;
331afba937eSOliver Neukum 	}
332afba937eSOliver Neukum 
333afba937eSOliver Neukum 	r = copy_from_user(buf, buffer, count);
334afba937eSOliver Neukum 	if (r > 0) {
335afba937eSOliver Neukum 		kfree(buf);
336afba937eSOliver Neukum 		rv = -EFAULT;
337afba937eSOliver Neukum 		goto out;
338afba937eSOliver Neukum 	}
339afba937eSOliver Neukum 
340afba937eSOliver Neukum 	req = desc->orq;
341afba937eSOliver Neukum 	usb_fill_control_urb(
342afba937eSOliver Neukum 		desc->command,
343afba937eSOliver Neukum 		interface_to_usbdev(desc->intf),
344afba937eSOliver Neukum 		/* using common endpoint 0 */
345afba937eSOliver Neukum 		usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
346afba937eSOliver Neukum 		(unsigned char *)req,
347afba937eSOliver Neukum 		buf,
348afba937eSOliver Neukum 		count,
349afba937eSOliver Neukum 		wdm_out_callback,
350afba937eSOliver Neukum 		desc
351afba937eSOliver Neukum 	);
352afba937eSOliver Neukum 
353afba937eSOliver Neukum 	req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
354afba937eSOliver Neukum 			     USB_RECIP_INTERFACE);
355afba937eSOliver Neukum 	req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
356afba937eSOliver Neukum 	req->wValue = 0;
357afba937eSOliver Neukum 	req->wIndex = desc->inum;
358afba937eSOliver Neukum 	req->wLength = cpu_to_le16(count);
359afba937eSOliver Neukum 	set_bit(WDM_IN_USE, &desc->flags);
360afba937eSOliver Neukum 
361afba937eSOliver Neukum 	rv = usb_submit_urb(desc->command, GFP_KERNEL);
362afba937eSOliver Neukum 	if (rv < 0) {
363afba937eSOliver Neukum 		kfree(buf);
364afba937eSOliver Neukum 		clear_bit(WDM_IN_USE, &desc->flags);
3659908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
366afba937eSOliver Neukum 	} else {
367afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
368afba937eSOliver Neukum 			req->wIndex);
369afba937eSOliver Neukum 	}
370afba937eSOliver Neukum out:
37117d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
37217d80d56SOliver Neukum outnp:
373afba937eSOliver Neukum 	mutex_unlock(&desc->wlock);
374afba937eSOliver Neukum outnl:
375afba937eSOliver Neukum 	return rv < 0 ? rv : count;
376afba937eSOliver Neukum }
377afba937eSOliver Neukum 
378afba937eSOliver Neukum static ssize_t wdm_read
379afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
380afba937eSOliver Neukum {
381afba937eSOliver Neukum 	int rv, cntr;
382afba937eSOliver Neukum 	int i = 0;
383afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
384afba937eSOliver Neukum 
385afba937eSOliver Neukum 
386afba937eSOliver Neukum 	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
387afba937eSOliver Neukum 	if (rv < 0)
388afba937eSOliver Neukum 		return -ERESTARTSYS;
389afba937eSOliver Neukum 
390afba937eSOliver Neukum 	if (desc->length == 0) {
391afba937eSOliver Neukum 		desc->read = 0;
392afba937eSOliver Neukum retry:
393afba937eSOliver Neukum 		i++;
394afba937eSOliver Neukum 		rv = wait_event_interruptible(desc->wait,
395afba937eSOliver Neukum 					      test_bit(WDM_READ, &desc->flags));
396afba937eSOliver Neukum 
39717d80d56SOliver Neukum 		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
39817d80d56SOliver Neukum 			rv = -ENODEV;
39917d80d56SOliver Neukum 			goto err;
40017d80d56SOliver Neukum 		}
40117d80d56SOliver Neukum 		usb_mark_last_busy(interface_to_usbdev(desc->intf));
402afba937eSOliver Neukum 		if (rv < 0) {
403afba937eSOliver Neukum 			rv = -ERESTARTSYS;
404afba937eSOliver Neukum 			goto err;
405afba937eSOliver Neukum 		}
406afba937eSOliver Neukum 
407afba937eSOliver Neukum 		spin_lock_irq(&desc->iuspin);
408afba937eSOliver Neukum 
409afba937eSOliver Neukum 		if (desc->rerr) { /* read completed, error happened */
410afba937eSOliver Neukum 			int t = desc->rerr;
411afba937eSOliver Neukum 			desc->rerr = 0;
412afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
4139908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
4149908a32eSGreg Kroah-Hartman 				"reading had resulted in %d\n", t);
415afba937eSOliver Neukum 			rv = -EIO;
416afba937eSOliver Neukum 			goto err;
417afba937eSOliver Neukum 		}
418afba937eSOliver Neukum 		/*
419afba937eSOliver Neukum 		 * recheck whether we've lost the race
420afba937eSOliver Neukum 		 * against the completion handler
421afba937eSOliver Neukum 		 */
422afba937eSOliver Neukum 		if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
423afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
424afba937eSOliver Neukum 			goto retry;
425afba937eSOliver Neukum 		}
426afba937eSOliver Neukum 		if (!desc->reslength) { /* zero length read */
427afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
428afba937eSOliver Neukum 			goto retry;
429afba937eSOliver Neukum 		}
430afba937eSOliver Neukum 		clear_bit(WDM_READ, &desc->flags);
431afba937eSOliver Neukum 		spin_unlock_irq(&desc->iuspin);
432afba937eSOliver Neukum 	}
433afba937eSOliver Neukum 
434afba937eSOliver Neukum 	cntr = count > desc->length ? desc->length : count;
435afba937eSOliver Neukum 	rv = copy_to_user(buffer, desc->ubuf, cntr);
436afba937eSOliver Neukum 	if (rv > 0) {
437afba937eSOliver Neukum 		rv = -EFAULT;
438afba937eSOliver Neukum 		goto err;
439afba937eSOliver Neukum 	}
440afba937eSOliver Neukum 
441afba937eSOliver Neukum 	for (i = 0; i < desc->length - cntr; i++)
442afba937eSOliver Neukum 		desc->ubuf[i] = desc->ubuf[i + cntr];
443afba937eSOliver Neukum 
444afba937eSOliver Neukum 	desc->length -= cntr;
44587d65e54SOliver Neukum 	/* in case we had outstanding data */
44687d65e54SOliver Neukum 	if (!desc->length)
44787d65e54SOliver Neukum 		clear_bit(WDM_READ, &desc->flags);
448afba937eSOliver Neukum 	rv = cntr;
449afba937eSOliver Neukum 
450afba937eSOliver Neukum err:
451afba937eSOliver Neukum 	mutex_unlock(&desc->rlock);
452afba937eSOliver Neukum 	if (rv < 0)
4539908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "wdm_read: exit error\n");
454afba937eSOliver Neukum 	return rv;
455afba937eSOliver Neukum }
456afba937eSOliver Neukum 
457afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id)
458afba937eSOliver Neukum {
459afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
460afba937eSOliver Neukum 
461afba937eSOliver Neukum 	wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
462afba937eSOliver Neukum 	if (desc->werr < 0)
4639908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Error in flush path: %d\n",
4649908a32eSGreg Kroah-Hartman 			desc->werr);
465afba937eSOliver Neukum 
466afba937eSOliver Neukum 	return desc->werr;
467afba937eSOliver Neukum }
468afba937eSOliver Neukum 
469afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
470afba937eSOliver Neukum {
471afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
472afba937eSOliver Neukum 	unsigned long flags;
473afba937eSOliver Neukum 	unsigned int mask = 0;
474afba937eSOliver Neukum 
475afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
476afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
477afba937eSOliver Neukum 		mask = POLLERR;
478afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
479afba937eSOliver Neukum 		goto desc_out;
480afba937eSOliver Neukum 	}
481afba937eSOliver Neukum 	if (test_bit(WDM_READ, &desc->flags))
482afba937eSOliver Neukum 		mask = POLLIN | POLLRDNORM;
483afba937eSOliver Neukum 	if (desc->rerr || desc->werr)
484afba937eSOliver Neukum 		mask |= POLLERR;
485afba937eSOliver Neukum 	if (!test_bit(WDM_IN_USE, &desc->flags))
486afba937eSOliver Neukum 		mask |= POLLOUT | POLLWRNORM;
487afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
488afba937eSOliver Neukum 
489afba937eSOliver Neukum 	poll_wait(file, &desc->wait, wait);
490afba937eSOliver Neukum 
491afba937eSOliver Neukum desc_out:
492afba937eSOliver Neukum 	return mask;
493afba937eSOliver Neukum }
494afba937eSOliver Neukum 
495afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file)
496afba937eSOliver Neukum {
497afba937eSOliver Neukum 	int minor = iminor(inode);
498afba937eSOliver Neukum 	int rv = -ENODEV;
499afba937eSOliver Neukum 	struct usb_interface *intf;
500afba937eSOliver Neukum 	struct wdm_device *desc;
501afba937eSOliver Neukum 
502afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
503afba937eSOliver Neukum 	intf = usb_find_interface(&wdm_driver, minor);
504afba937eSOliver Neukum 	if (!intf)
505afba937eSOliver Neukum 		goto out;
506afba937eSOliver Neukum 
507afba937eSOliver Neukum 	desc = usb_get_intfdata(intf);
508afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags))
509afba937eSOliver Neukum 		goto out;
510afba937eSOliver Neukum 
51117d80d56SOliver Neukum 	;
512afba937eSOliver Neukum 	file->private_data = desc;
513afba937eSOliver Neukum 
51417d80d56SOliver Neukum 	rv = usb_autopm_get_interface(desc->intf);
51517d80d56SOliver Neukum 	if (rv < 0) {
5169908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
51717d80d56SOliver Neukum 		goto out;
51817d80d56SOliver Neukum 	}
51917d80d56SOliver Neukum 	intf->needs_remote_wakeup = 1;
520afba937eSOliver Neukum 
52117d80d56SOliver Neukum 	mutex_lock(&desc->plock);
52217d80d56SOliver Neukum 	if (!desc->count++) {
52317d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_KERNEL);
524afba937eSOliver Neukum 		if (rv < 0) {
525afba937eSOliver Neukum 			desc->count--;
5269908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
5279908a32eSGreg Kroah-Hartman 				"Error submitting int urb - %d\n", rv);
528afba937eSOliver Neukum 		}
52917d80d56SOliver Neukum 	} else {
530afba937eSOliver Neukum 		rv = 0;
53117d80d56SOliver Neukum 	}
53217d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
53317d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
534afba937eSOliver Neukum out:
535afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
536afba937eSOliver Neukum 	return rv;
537afba937eSOliver Neukum }
538afba937eSOliver Neukum 
539afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file)
540afba937eSOliver Neukum {
541afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
542afba937eSOliver Neukum 
543afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
54417d80d56SOliver Neukum 	mutex_lock(&desc->plock);
545afba937eSOliver Neukum 	desc->count--;
54617d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
54717d80d56SOliver Neukum 
548afba937eSOliver Neukum 	if (!desc->count) {
549afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
550afba937eSOliver Neukum 		kill_urbs(desc);
55117d80d56SOliver Neukum 		if (!test_bit(WDM_DISCONNECTING, &desc->flags))
55217d80d56SOliver Neukum 			desc->intf->needs_remote_wakeup = 0;
553afba937eSOliver Neukum 	}
554afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
555afba937eSOliver Neukum 	return 0;
556afba937eSOliver Neukum }
557afba937eSOliver Neukum 
558afba937eSOliver Neukum static const struct file_operations wdm_fops = {
559afba937eSOliver Neukum 	.owner =	THIS_MODULE,
560afba937eSOliver Neukum 	.read =		wdm_read,
561afba937eSOliver Neukum 	.write =	wdm_write,
562afba937eSOliver Neukum 	.open =		wdm_open,
563afba937eSOliver Neukum 	.flush =	wdm_flush,
564afba937eSOliver Neukum 	.release =	wdm_release,
565afba937eSOliver Neukum 	.poll =		wdm_poll
566afba937eSOliver Neukum };
567afba937eSOliver Neukum 
568afba937eSOliver Neukum static struct usb_class_driver wdm_class = {
569afba937eSOliver Neukum 	.name =		"cdc-wdm%d",
570afba937eSOliver Neukum 	.fops =		&wdm_fops,
571afba937eSOliver Neukum 	.minor_base =	WDM_MINOR_BASE,
572afba937eSOliver Neukum };
573afba937eSOliver Neukum 
574afba937eSOliver Neukum /* --- error handling --- */
575afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work)
576afba937eSOliver Neukum {
577afba937eSOliver Neukum 	struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
578afba937eSOliver Neukum 	unsigned long flags;
579afba937eSOliver Neukum 	int rv;
580afba937eSOliver Neukum 
581afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
582afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
583afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
584afba937eSOliver Neukum 	} else {
585afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
586afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_KERNEL);
587afba937eSOliver Neukum 		if (rv < 0 && rv != -EPERM) {
588afba937eSOliver Neukum 			spin_lock_irqsave(&desc->iuspin, flags);
589afba937eSOliver Neukum 			if (!test_bit(WDM_DISCONNECTING, &desc->flags))
590afba937eSOliver Neukum 				schedule_work(&desc->rxwork);
591afba937eSOliver Neukum 			spin_unlock_irqrestore(&desc->iuspin, flags);
592afba937eSOliver Neukum 		}
593afba937eSOliver Neukum 	}
594afba937eSOliver Neukum }
595afba937eSOliver Neukum 
596afba937eSOliver Neukum /* --- hotplug --- */
597afba937eSOliver Neukum 
598afba937eSOliver Neukum static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
599afba937eSOliver Neukum {
600afba937eSOliver Neukum 	int rv = -EINVAL;
601afba937eSOliver Neukum 	struct usb_device *udev = interface_to_usbdev(intf);
602afba937eSOliver Neukum 	struct wdm_device *desc;
603afba937eSOliver Neukum 	struct usb_host_interface *iface;
604afba937eSOliver Neukum 	struct usb_endpoint_descriptor *ep;
605afba937eSOliver Neukum 	struct usb_cdc_dmm_desc *dmhd;
606afba937eSOliver Neukum 	u8 *buffer = intf->altsetting->extra;
607afba937eSOliver Neukum 	int buflen = intf->altsetting->extralen;
608afba937eSOliver Neukum 	u16 maxcom = 0;
609afba937eSOliver Neukum 
610afba937eSOliver Neukum 	if (!buffer)
611afba937eSOliver Neukum 		goto out;
612afba937eSOliver Neukum 
613052fbc0dSOliver Neukum 	while (buflen > 2) {
614afba937eSOliver Neukum 		if (buffer [1] != USB_DT_CS_INTERFACE) {
6159908a32eSGreg Kroah-Hartman 			dev_err(&intf->dev, "skipping garbage\n");
616afba937eSOliver Neukum 			goto next_desc;
617afba937eSOliver Neukum 		}
618afba937eSOliver Neukum 
619afba937eSOliver Neukum 		switch (buffer [2]) {
620afba937eSOliver Neukum 		case USB_CDC_HEADER_TYPE:
621afba937eSOliver Neukum 			break;
622afba937eSOliver Neukum 		case USB_CDC_DMM_TYPE:
623afba937eSOliver Neukum 			dmhd = (struct usb_cdc_dmm_desc *)buffer;
624afba937eSOliver Neukum 			maxcom = le16_to_cpu(dmhd->wMaxCommand);
625afba937eSOliver Neukum 			dev_dbg(&intf->dev,
626afba937eSOliver Neukum 				"Finding maximum buffer length: %d", maxcom);
627afba937eSOliver Neukum 			break;
628afba937eSOliver Neukum 		default:
6299908a32eSGreg Kroah-Hartman 			dev_err(&intf->dev,
6309908a32eSGreg Kroah-Hartman 				"Ignoring extra header, type %d, length %d\n",
631afba937eSOliver Neukum 				buffer[2], buffer[0]);
632afba937eSOliver Neukum 			break;
633afba937eSOliver Neukum 		}
634afba937eSOliver Neukum next_desc:
635afba937eSOliver Neukum 		buflen -= buffer[0];
636afba937eSOliver Neukum 		buffer += buffer[0];
637afba937eSOliver Neukum 	}
638afba937eSOliver Neukum 
639afba937eSOliver Neukum 	rv = -ENOMEM;
640afba937eSOliver Neukum 	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
641afba937eSOliver Neukum 	if (!desc)
642afba937eSOliver Neukum 		goto out;
643afba937eSOliver Neukum 	mutex_init(&desc->wlock);
644afba937eSOliver Neukum 	mutex_init(&desc->rlock);
64517d80d56SOliver Neukum 	mutex_init(&desc->plock);
646afba937eSOliver Neukum 	spin_lock_init(&desc->iuspin);
647afba937eSOliver Neukum 	init_waitqueue_head(&desc->wait);
648afba937eSOliver Neukum 	desc->wMaxCommand = maxcom;
649052fbc0dSOliver Neukum 	/* this will be expanded and needed in hardware endianness */
650afba937eSOliver Neukum 	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
651afba937eSOliver Neukum 	desc->intf = intf;
652afba937eSOliver Neukum 	INIT_WORK(&desc->rxwork, wdm_rxwork);
653afba937eSOliver Neukum 
654afba937eSOliver Neukum 	rv = -EINVAL;
655052fbc0dSOliver Neukum 	iface = intf->cur_altsetting;
656052fbc0dSOliver Neukum 	if (iface->desc.bNumEndpoints != 1)
657afba937eSOliver Neukum 		goto err;
658052fbc0dSOliver Neukum 	ep = &iface->endpoint[0].desc;
659052fbc0dSOliver Neukum 	if (!ep || !usb_endpoint_is_int_in(ep))
660052fbc0dSOliver Neukum 		goto err;
661afba937eSOliver Neukum 
662fa4144b7SAl Viro 	desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
663fa4144b7SAl Viro 	desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
664afba937eSOliver Neukum 
665afba937eSOliver Neukum 	desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
666afba937eSOliver Neukum 	if (!desc->orq)
667afba937eSOliver Neukum 		goto err;
668afba937eSOliver Neukum 	desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
669afba937eSOliver Neukum 	if (!desc->irq)
670afba937eSOliver Neukum 		goto err;
671afba937eSOliver Neukum 
672afba937eSOliver Neukum 	desc->validity = usb_alloc_urb(0, GFP_KERNEL);
673afba937eSOliver Neukum 	if (!desc->validity)
674afba937eSOliver Neukum 		goto err;
675afba937eSOliver Neukum 
676afba937eSOliver Neukum 	desc->response = usb_alloc_urb(0, GFP_KERNEL);
677afba937eSOliver Neukum 	if (!desc->response)
678afba937eSOliver Neukum 		goto err;
679afba937eSOliver Neukum 
680afba937eSOliver Neukum 	desc->command = usb_alloc_urb(0, GFP_KERNEL);
681afba937eSOliver Neukum 	if (!desc->command)
682afba937eSOliver Neukum 		goto err;
683afba937eSOliver Neukum 
684afba937eSOliver Neukum 	desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
685afba937eSOliver Neukum 	if (!desc->ubuf)
686afba937eSOliver Neukum 		goto err;
687afba937eSOliver Neukum 
688afba937eSOliver Neukum 	desc->sbuf = usb_buffer_alloc(interface_to_usbdev(intf),
689afba937eSOliver Neukum 					desc->wMaxPacketSize,
690afba937eSOliver Neukum 					GFP_KERNEL,
691afba937eSOliver Neukum 					&desc->validity->transfer_dma);
692afba937eSOliver Neukum 	if (!desc->sbuf)
693afba937eSOliver Neukum 		goto err;
694afba937eSOliver Neukum 
695afba937eSOliver Neukum 	desc->inbuf = usb_buffer_alloc(interface_to_usbdev(intf),
696afba937eSOliver Neukum 					desc->bMaxPacketSize0,
697afba937eSOliver Neukum 					GFP_KERNEL,
698afba937eSOliver Neukum 					&desc->response->transfer_dma);
699afba937eSOliver Neukum 	if (!desc->inbuf)
700afba937eSOliver Neukum 		goto err2;
701afba937eSOliver Neukum 
702afba937eSOliver Neukum 	usb_fill_int_urb(
703afba937eSOliver Neukum 		desc->validity,
704afba937eSOliver Neukum 		interface_to_usbdev(intf),
705afba937eSOliver Neukum 		usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
706afba937eSOliver Neukum 		desc->sbuf,
707afba937eSOliver Neukum 		desc->wMaxPacketSize,
708afba937eSOliver Neukum 		wdm_int_callback,
709afba937eSOliver Neukum 		desc,
710afba937eSOliver Neukum 		ep->bInterval
711afba937eSOliver Neukum 	);
712afba937eSOliver Neukum 	desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
713afba937eSOliver Neukum 
714afba937eSOliver Neukum 	usb_set_intfdata(intf, desc);
715afba937eSOliver Neukum 	rv = usb_register_dev(intf, &wdm_class);
716052fbc0dSOliver Neukum 	if (rv < 0)
717052fbc0dSOliver Neukum 		goto err3;
718052fbc0dSOliver Neukum 	else
719afba937eSOliver Neukum 		dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
720afba937eSOliver Neukum 			intf->minor - WDM_MINOR_BASE);
721afba937eSOliver Neukum out:
722afba937eSOliver Neukum 	return rv;
723052fbc0dSOliver Neukum err3:
724052fbc0dSOliver Neukum 	usb_set_intfdata(intf, NULL);
725052fbc0dSOliver Neukum 	usb_buffer_free(interface_to_usbdev(desc->intf),
726052fbc0dSOliver Neukum 			desc->bMaxPacketSize0,
727052fbc0dSOliver Neukum 			desc->inbuf,
728052fbc0dSOliver Neukum 			desc->response->transfer_dma);
729afba937eSOliver Neukum err2:
730afba937eSOliver Neukum 	usb_buffer_free(interface_to_usbdev(desc->intf),
731afba937eSOliver Neukum 			desc->wMaxPacketSize,
732afba937eSOliver Neukum 			desc->sbuf,
733afba937eSOliver Neukum 			desc->validity->transfer_dma);
734afba937eSOliver Neukum err:
735afba937eSOliver Neukum 	free_urbs(desc);
736afba937eSOliver Neukum 	kfree(desc->ubuf);
737afba937eSOliver Neukum 	kfree(desc->orq);
738afba937eSOliver Neukum 	kfree(desc->irq);
739afba937eSOliver Neukum 	kfree(desc);
740afba937eSOliver Neukum 	return rv;
741afba937eSOliver Neukum }
742afba937eSOliver Neukum 
743afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf)
744afba937eSOliver Neukum {
745afba937eSOliver Neukum 	struct wdm_device *desc;
746afba937eSOliver Neukum 	unsigned long flags;
747afba937eSOliver Neukum 
748afba937eSOliver Neukum 	usb_deregister_dev(intf, &wdm_class);
749afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
750afba937eSOliver Neukum 	desc = usb_get_intfdata(intf);
751afba937eSOliver Neukum 
752afba937eSOliver Neukum 	/* the spinlock makes sure no new urbs are generated in the callbacks */
753afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
754afba937eSOliver Neukum 	set_bit(WDM_DISCONNECTING, &desc->flags);
755afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
75617d80d56SOliver Neukum 	/* to terminate pending flushes */
757afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
758afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
759afba937eSOliver Neukum 	cancel_work_sync(&desc->rxwork);
760afba937eSOliver Neukum 	kill_urbs(desc);
761afba937eSOliver Neukum 	wake_up_all(&desc->wait);
762afba937eSOliver Neukum 	if (!desc->count)
763afba937eSOliver Neukum 		cleanup(desc);
764afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
765afba937eSOliver Neukum }
766afba937eSOliver Neukum 
76717d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
76817d80d56SOliver Neukum {
76917d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
77017d80d56SOliver Neukum 	int rv = 0;
77117d80d56SOliver Neukum 
77217d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
77317d80d56SOliver Neukum 
77417d80d56SOliver Neukum 	mutex_lock(&desc->plock);
77535758589SOliver Neukum #ifdef CONFIG_PM
77665bfd296SAlan Stern 	if ((message.event & PM_EVENT_AUTO) &&
77765bfd296SAlan Stern 			test_bit(WDM_IN_USE, &desc->flags)) {
77817d80d56SOliver Neukum 		rv = -EBUSY;
77917d80d56SOliver Neukum 	} else {
78035758589SOliver Neukum #endif
78117d80d56SOliver Neukum 		cancel_work_sync(&desc->rxwork);
78217d80d56SOliver Neukum 		kill_urbs(desc);
78335758589SOliver Neukum #ifdef CONFIG_PM
78417d80d56SOliver Neukum 	}
78535758589SOliver Neukum #endif
78617d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
78717d80d56SOliver Neukum 
78817d80d56SOliver Neukum 	return rv;
78917d80d56SOliver Neukum }
79017d80d56SOliver Neukum 
79117d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc)
79217d80d56SOliver Neukum {
79317d80d56SOliver Neukum 	int rv = 0;
79417d80d56SOliver Neukum 
79517d80d56SOliver Neukum 	if (desc->count) {
79617d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_NOIO);
79717d80d56SOliver Neukum 		if (rv < 0)
7989908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
7999908a32eSGreg Kroah-Hartman 				"Error resume submitting int urb - %d\n", rv);
80017d80d56SOliver Neukum 	}
80117d80d56SOliver Neukum 	return rv;
80217d80d56SOliver Neukum }
80317d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf)
80417d80d56SOliver Neukum {
80517d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
80617d80d56SOliver Neukum 	int rv;
80717d80d56SOliver Neukum 
80817d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
80917d80d56SOliver Neukum 	mutex_lock(&desc->plock);
81017d80d56SOliver Neukum 	rv = recover_from_urb_loss(desc);
81117d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
81217d80d56SOliver Neukum 	return rv;
81317d80d56SOliver Neukum }
81417d80d56SOliver Neukum 
81517d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf)
81617d80d56SOliver Neukum {
81717d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
81817d80d56SOliver Neukum 
81917d80d56SOliver Neukum 	mutex_lock(&desc->plock);
82017d80d56SOliver Neukum 	return 0;
82117d80d56SOliver Neukum }
82217d80d56SOliver Neukum 
82317d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf)
82417d80d56SOliver Neukum {
82517d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
82617d80d56SOliver Neukum 	int rv;
82717d80d56SOliver Neukum 
82817d80d56SOliver Neukum 	rv = recover_from_urb_loss(desc);
82917d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
83017d80d56SOliver Neukum 	return 0;
83117d80d56SOliver Neukum }
83217d80d56SOliver Neukum 
833afba937eSOliver Neukum static struct usb_driver wdm_driver = {
834afba937eSOliver Neukum 	.name =		"cdc_wdm",
835afba937eSOliver Neukum 	.probe =	wdm_probe,
836afba937eSOliver Neukum 	.disconnect =	wdm_disconnect,
83717d80d56SOliver Neukum 	.suspend =	wdm_suspend,
83817d80d56SOliver Neukum 	.resume =	wdm_resume,
83917d80d56SOliver Neukum 	.reset_resume =	wdm_resume,
84017d80d56SOliver Neukum 	.pre_reset =	wdm_pre_reset,
84117d80d56SOliver Neukum 	.post_reset =	wdm_post_reset,
842afba937eSOliver Neukum 	.id_table =	wdm_ids,
84317d80d56SOliver Neukum 	.supports_autosuspend = 1,
844afba937eSOliver Neukum };
845afba937eSOliver Neukum 
846afba937eSOliver Neukum /* --- low level module stuff --- */
847afba937eSOliver Neukum 
848afba937eSOliver Neukum static int __init wdm_init(void)
849afba937eSOliver Neukum {
850afba937eSOliver Neukum 	int rv;
851afba937eSOliver Neukum 
852afba937eSOliver Neukum 	rv = usb_register(&wdm_driver);
853afba937eSOliver Neukum 
854afba937eSOliver Neukum 	return rv;
855afba937eSOliver Neukum }
856afba937eSOliver Neukum 
857afba937eSOliver Neukum static void __exit wdm_exit(void)
858afba937eSOliver Neukum {
859afba937eSOliver Neukum 	usb_deregister(&wdm_driver);
860afba937eSOliver Neukum }
861afba937eSOliver Neukum 
862afba937eSOliver Neukum module_init(wdm_init);
863afba937eSOliver Neukum module_exit(wdm_exit);
864afba937eSOliver Neukum 
865afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR);
86687d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC);
867afba937eSOliver Neukum MODULE_LICENSE("GPL");
868