xref: /openbmc/linux/drivers/usb/class/cdc-wdm.c (revision 35758589)
1afba937eSOliver Neukum /*
2afba937eSOliver Neukum  * cdc-wdm.c
3afba937eSOliver Neukum  *
4afba937eSOliver Neukum  * This driver supports USB CDC WCM Device Management.
5afba937eSOliver Neukum  *
6afba937eSOliver Neukum  * Copyright (c) 2007-2008 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 
45afba937eSOliver Neukum #define WDM_MINOR_BASE	176
46afba937eSOliver Neukum 
47afba937eSOliver Neukum 
48afba937eSOliver Neukum #define WDM_IN_USE		1
49afba937eSOliver Neukum #define WDM_DISCONNECTING	2
50afba937eSOliver Neukum #define WDM_RESULT		3
51afba937eSOliver Neukum #define WDM_READ		4
52afba937eSOliver Neukum #define WDM_INT_STALL		5
53afba937eSOliver Neukum #define WDM_POLL_RUNNING	6
54afba937eSOliver Neukum 
55afba937eSOliver Neukum 
56afba937eSOliver Neukum #define WDM_MAX			16
57afba937eSOliver Neukum 
58afba937eSOliver Neukum 
59afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex);
60afba937eSOliver Neukum 
61afba937eSOliver Neukum /* --- method tables --- */
62afba937eSOliver Neukum 
63afba937eSOliver Neukum struct wdm_device {
64afba937eSOliver Neukum 	u8			*inbuf; /* buffer for response */
65afba937eSOliver Neukum 	u8			*outbuf; /* buffer for command */
66afba937eSOliver Neukum 	u8			*sbuf; /* buffer for status */
67afba937eSOliver Neukum 	u8			*ubuf; /* buffer for copy to user space */
68afba937eSOliver Neukum 
69afba937eSOliver Neukum 	struct urb		*command;
70afba937eSOliver Neukum 	struct urb		*response;
71afba937eSOliver Neukum 	struct urb		*validity;
72afba937eSOliver Neukum 	struct usb_interface	*intf;
73afba937eSOliver Neukum 	struct usb_ctrlrequest	*orq;
74afba937eSOliver Neukum 	struct usb_ctrlrequest	*irq;
75afba937eSOliver Neukum 	spinlock_t		iuspin;
76afba937eSOliver Neukum 
77afba937eSOliver Neukum 	unsigned long		flags;
78afba937eSOliver Neukum 	u16			bufsize;
79afba937eSOliver Neukum 	u16			wMaxCommand;
80afba937eSOliver Neukum 	u16			wMaxPacketSize;
81afba937eSOliver Neukum 	u16			bMaxPacketSize0;
82afba937eSOliver Neukum 	__le16			inum;
83afba937eSOliver Neukum 	int			reslength;
84afba937eSOliver Neukum 	int			length;
85afba937eSOliver Neukum 	int			read;
86afba937eSOliver Neukum 	int			count;
87afba937eSOliver Neukum 	dma_addr_t		shandle;
88afba937eSOliver Neukum 	dma_addr_t		ihandle;
89afba937eSOliver Neukum 	struct mutex		wlock;
90afba937eSOliver Neukum 	struct mutex		rlock;
9117d80d56SOliver Neukum 	struct mutex		plock;
92afba937eSOliver Neukum 	wait_queue_head_t	wait;
93afba937eSOliver Neukum 	struct work_struct	rxwork;
94afba937eSOliver Neukum 	int			werr;
95afba937eSOliver Neukum 	int			rerr;
96afba937eSOliver Neukum };
97afba937eSOliver Neukum 
98afba937eSOliver Neukum static struct usb_driver wdm_driver;
99afba937eSOliver Neukum 
100afba937eSOliver Neukum /* --- callbacks --- */
101afba937eSOliver Neukum static void wdm_out_callback(struct urb *urb)
102afba937eSOliver Neukum {
103afba937eSOliver Neukum 	struct wdm_device *desc;
104afba937eSOliver Neukum 	desc = urb->context;
105afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
106afba937eSOliver Neukum 	desc->werr = urb->status;
107afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
108afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
109afba937eSOliver Neukum 	kfree(desc->outbuf);
110afba937eSOliver Neukum 	wake_up(&desc->wait);
111afba937eSOliver Neukum }
112afba937eSOliver Neukum 
113afba937eSOliver Neukum static void wdm_in_callback(struct urb *urb)
114afba937eSOliver Neukum {
115afba937eSOliver Neukum 	struct wdm_device *desc = urb->context;
116afba937eSOliver Neukum 	int status = urb->status;
117afba937eSOliver Neukum 
118afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
119afba937eSOliver Neukum 
120afba937eSOliver Neukum 	if (status) {
121afba937eSOliver Neukum 		switch (status) {
122afba937eSOliver Neukum 		case -ENOENT:
123afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
124afba937eSOliver Neukum 				"nonzero urb status received: -ENOENT");
125afba937eSOliver Neukum 			break;
126afba937eSOliver Neukum 		case -ECONNRESET:
127afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
128afba937eSOliver Neukum 				"nonzero urb status received: -ECONNRESET");
129afba937eSOliver Neukum 			break;
130afba937eSOliver Neukum 		case -ESHUTDOWN:
131afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
132afba937eSOliver Neukum 				"nonzero urb status received: -ESHUTDOWN");
133afba937eSOliver Neukum 			break;
134afba937eSOliver Neukum 		case -EPIPE:
135afba937eSOliver Neukum 			err("nonzero urb status received: -EPIPE");
136afba937eSOliver Neukum 			break;
137afba937eSOliver Neukum 		default:
138afba937eSOliver Neukum 			err("Unexpected error %d", status);
139afba937eSOliver Neukum 			break;
140afba937eSOliver Neukum 		}
141afba937eSOliver Neukum 	}
142afba937eSOliver Neukum 
143afba937eSOliver Neukum 	desc->rerr = status;
144afba937eSOliver Neukum 	desc->reslength = urb->actual_length;
145afba937eSOliver Neukum 	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
146afba937eSOliver Neukum 	desc->length += desc->reslength;
147afba937eSOliver Neukum 	wake_up(&desc->wait);
148afba937eSOliver Neukum 
149afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
150afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
151afba937eSOliver Neukum }
152afba937eSOliver Neukum 
153afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb)
154afba937eSOliver Neukum {
155afba937eSOliver Neukum 	int rv = 0;
156afba937eSOliver Neukum 	int status = urb->status;
157afba937eSOliver Neukum 	struct wdm_device *desc;
158afba937eSOliver Neukum 	struct usb_ctrlrequest *req;
159afba937eSOliver Neukum 	struct usb_cdc_notification *dr;
160afba937eSOliver Neukum 
161afba937eSOliver Neukum 	desc = urb->context;
162afba937eSOliver Neukum 	req = desc->irq;
163afba937eSOliver Neukum 	dr = (struct usb_cdc_notification *)desc->sbuf;
164afba937eSOliver Neukum 
165afba937eSOliver Neukum 	if (status) {
166afba937eSOliver Neukum 		switch (status) {
167afba937eSOliver Neukum 		case -ESHUTDOWN:
168afba937eSOliver Neukum 		case -ENOENT:
169afba937eSOliver Neukum 		case -ECONNRESET:
170afba937eSOliver Neukum 			return; /* unplug */
171afba937eSOliver Neukum 		case -EPIPE:
172afba937eSOliver Neukum 			set_bit(WDM_INT_STALL, &desc->flags);
173afba937eSOliver Neukum 			err("Stall on int endpoint");
174afba937eSOliver Neukum 			goto sw; /* halt is cleared in work */
175afba937eSOliver Neukum 		default:
176afba937eSOliver Neukum 			err("nonzero urb status received: %d", status);
177afba937eSOliver Neukum 			break;
178afba937eSOliver Neukum 		}
179afba937eSOliver Neukum 	}
180afba937eSOliver Neukum 
181afba937eSOliver Neukum 	if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
182afba937eSOliver Neukum 		err("wdm_int_callback - %d bytes", urb->actual_length);
183afba937eSOliver Neukum 		goto exit;
184afba937eSOliver Neukum 	}
185afba937eSOliver Neukum 
186afba937eSOliver Neukum 	switch (dr->bNotificationType) {
187afba937eSOliver Neukum 	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
188afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev,
189afba937eSOliver Neukum 			"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
190afba937eSOliver Neukum 			dr->wIndex, dr->wLength);
191afba937eSOliver Neukum 		break;
192afba937eSOliver Neukum 
193afba937eSOliver Neukum 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
194afba937eSOliver Neukum 
195afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev,
196afba937eSOliver Neukum 			"NOTIFY_NETWORK_CONNECTION %s network",
197afba937eSOliver Neukum 			dr->wValue ? "connected to" : "disconnected from");
198afba937eSOliver Neukum 		goto exit;
199afba937eSOliver Neukum 	default:
200afba937eSOliver Neukum 		clear_bit(WDM_POLL_RUNNING, &desc->flags);
201afba937eSOliver Neukum 		err("unknown notification %d received: index %d len %d",
202afba937eSOliver Neukum 			dr->bNotificationType, dr->wIndex, dr->wLength);
203afba937eSOliver Neukum 		goto exit;
204afba937eSOliver Neukum 	}
205afba937eSOliver Neukum 
206afba937eSOliver Neukum 	req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
207afba937eSOliver Neukum 	req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
208afba937eSOliver Neukum 	req->wValue = 0;
209afba937eSOliver Neukum 	req->wIndex = desc->inum;
21087d65e54SOliver Neukum 	req->wLength = cpu_to_le16(desc->wMaxCommand);
211afba937eSOliver Neukum 
212afba937eSOliver Neukum 	usb_fill_control_urb(
213afba937eSOliver Neukum 		desc->response,
214afba937eSOliver Neukum 		interface_to_usbdev(desc->intf),
215afba937eSOliver Neukum 		/* using common endpoint 0 */
216afba937eSOliver Neukum 		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
217afba937eSOliver Neukum 		(unsigned char *)req,
218afba937eSOliver Neukum 		desc->inbuf,
21987d65e54SOliver Neukum 		desc->wMaxCommand,
220afba937eSOliver Neukum 		wdm_in_callback,
221afba937eSOliver Neukum 		desc
222afba937eSOliver Neukum 	);
223afba937eSOliver Neukum 	desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
224afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
225afba937eSOliver Neukum 	clear_bit(WDM_READ, &desc->flags);
226afba937eSOliver Neukum 	if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
227afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_ATOMIC);
228afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
229afba937eSOliver Neukum 			__func__, rv);
230afba937eSOliver Neukum 	}
231afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
232afba937eSOliver Neukum 	if (rv < 0) {
233afba937eSOliver Neukum 		if (rv == -EPERM)
234afba937eSOliver Neukum 			return;
235afba937eSOliver Neukum 		if (rv == -ENOMEM) {
236afba937eSOliver Neukum sw:
237afba937eSOliver Neukum 			rv = schedule_work(&desc->rxwork);
238afba937eSOliver Neukum 			if (rv)
239afba937eSOliver Neukum 				err("Cannot schedule work");
240afba937eSOliver Neukum 		}
241afba937eSOliver Neukum 	}
242afba937eSOliver Neukum exit:
243afba937eSOliver Neukum 	rv = usb_submit_urb(urb, GFP_ATOMIC);
244afba937eSOliver Neukum 	if (rv)
245afba937eSOliver Neukum 		err("%s - usb_submit_urb failed with result %d",
246afba937eSOliver Neukum 		     __func__, rv);
247afba937eSOliver Neukum 
248afba937eSOliver Neukum }
249afba937eSOliver Neukum 
250afba937eSOliver Neukum static void kill_urbs(struct wdm_device *desc)
251afba937eSOliver Neukum {
25217d80d56SOliver Neukum 	/* the order here is essential */
253afba937eSOliver Neukum 	usb_kill_urb(desc->command);
254afba937eSOliver Neukum 	usb_kill_urb(desc->validity);
255afba937eSOliver Neukum 	usb_kill_urb(desc->response);
256afba937eSOliver Neukum }
257afba937eSOliver Neukum 
258afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc)
259afba937eSOliver Neukum {
260afba937eSOliver Neukum 	usb_free_urb(desc->validity);
261afba937eSOliver Neukum 	usb_free_urb(desc->response);
262afba937eSOliver Neukum 	usb_free_urb(desc->command);
263afba937eSOliver Neukum }
264afba937eSOliver Neukum 
265afba937eSOliver Neukum static void cleanup(struct wdm_device *desc)
266afba937eSOliver Neukum {
267afba937eSOliver Neukum 	usb_buffer_free(interface_to_usbdev(desc->intf),
268afba937eSOliver Neukum 			desc->wMaxPacketSize,
269afba937eSOliver Neukum 			desc->sbuf,
270afba937eSOliver Neukum 			desc->validity->transfer_dma);
271afba937eSOliver Neukum 	usb_buffer_free(interface_to_usbdev(desc->intf),
27287d65e54SOliver Neukum 			desc->wMaxCommand,
273afba937eSOliver Neukum 			desc->inbuf,
274afba937eSOliver Neukum 			desc->response->transfer_dma);
275afba937eSOliver Neukum 	kfree(desc->orq);
276afba937eSOliver Neukum 	kfree(desc->irq);
277afba937eSOliver Neukum 	kfree(desc->ubuf);
278afba937eSOliver Neukum 	free_urbs(desc);
279afba937eSOliver Neukum 	kfree(desc);
280afba937eSOliver Neukum }
281afba937eSOliver Neukum 
282afba937eSOliver Neukum static ssize_t wdm_write
283afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
284afba937eSOliver Neukum {
285afba937eSOliver Neukum 	u8 *buf;
286afba937eSOliver Neukum 	int rv = -EMSGSIZE, r, we;
287afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
288afba937eSOliver Neukum 	struct usb_ctrlrequest *req;
289afba937eSOliver Neukum 
290afba937eSOliver Neukum 	if (count > desc->wMaxCommand)
291afba937eSOliver Neukum 		count = desc->wMaxCommand;
292afba937eSOliver Neukum 
293afba937eSOliver Neukum 	spin_lock_irq(&desc->iuspin);
294afba937eSOliver Neukum 	we = desc->werr;
295afba937eSOliver Neukum 	desc->werr = 0;
296afba937eSOliver Neukum 	spin_unlock_irq(&desc->iuspin);
297afba937eSOliver Neukum 	if (we < 0)
298afba937eSOliver Neukum 		return -EIO;
299afba937eSOliver Neukum 
300afba937eSOliver Neukum 	r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */
301afba937eSOliver Neukum 	rv = -ERESTARTSYS;
302afba937eSOliver Neukum 	if (r)
303afba937eSOliver Neukum 		goto outnl;
304afba937eSOliver Neukum 
30517d80d56SOliver Neukum 	r = usb_autopm_get_interface(desc->intf);
30617d80d56SOliver Neukum 	if (r < 0)
30717d80d56SOliver Neukum 		goto outnp;
308afba937eSOliver Neukum 	r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
309afba937eSOliver Neukum 							   &desc->flags));
310afba937eSOliver Neukum 	if (r < 0)
311afba937eSOliver Neukum 		goto out;
312afba937eSOliver Neukum 
313afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
314afba937eSOliver Neukum 		rv = -ENODEV;
315afba937eSOliver Neukum 		goto out;
316afba937eSOliver Neukum 	}
317afba937eSOliver Neukum 
318afba937eSOliver Neukum 	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
319afba937eSOliver Neukum 	if (!buf) {
320afba937eSOliver Neukum 		rv = -ENOMEM;
321afba937eSOliver Neukum 		goto out;
322afba937eSOliver Neukum 	}
323afba937eSOliver Neukum 
324afba937eSOliver Neukum 	r = copy_from_user(buf, buffer, count);
325afba937eSOliver Neukum 	if (r > 0) {
326afba937eSOliver Neukum 		kfree(buf);
327afba937eSOliver Neukum 		rv = -EFAULT;
328afba937eSOliver Neukum 		goto out;
329afba937eSOliver Neukum 	}
330afba937eSOliver Neukum 
331afba937eSOliver Neukum 	req = desc->orq;
332afba937eSOliver Neukum 	usb_fill_control_urb(
333afba937eSOliver Neukum 		desc->command,
334afba937eSOliver Neukum 		interface_to_usbdev(desc->intf),
335afba937eSOliver Neukum 		/* using common endpoint 0 */
336afba937eSOliver Neukum 		usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
337afba937eSOliver Neukum 		(unsigned char *)req,
338afba937eSOliver Neukum 		buf,
339afba937eSOliver Neukum 		count,
340afba937eSOliver Neukum 		wdm_out_callback,
341afba937eSOliver Neukum 		desc
342afba937eSOliver Neukum 	);
343afba937eSOliver Neukum 
344afba937eSOliver Neukum 	req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
345afba937eSOliver Neukum 			     USB_RECIP_INTERFACE);
346afba937eSOliver Neukum 	req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
347afba937eSOliver Neukum 	req->wValue = 0;
348afba937eSOliver Neukum 	req->wIndex = desc->inum;
349afba937eSOliver Neukum 	req->wLength = cpu_to_le16(count);
350afba937eSOliver Neukum 	set_bit(WDM_IN_USE, &desc->flags);
351afba937eSOliver Neukum 
352afba937eSOliver Neukum 	rv = usb_submit_urb(desc->command, GFP_KERNEL);
353afba937eSOliver Neukum 	if (rv < 0) {
354afba937eSOliver Neukum 		kfree(buf);
355afba937eSOliver Neukum 		clear_bit(WDM_IN_USE, &desc->flags);
35687d65e54SOliver Neukum 		err("Tx URB error: %d", rv);
357afba937eSOliver Neukum 	} else {
358afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
359afba937eSOliver Neukum 			req->wIndex);
360afba937eSOliver Neukum 	}
361afba937eSOliver Neukum out:
36217d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
36317d80d56SOliver Neukum outnp:
364afba937eSOliver Neukum 	mutex_unlock(&desc->wlock);
365afba937eSOliver Neukum outnl:
366afba937eSOliver Neukum 	return rv < 0 ? rv : count;
367afba937eSOliver Neukum }
368afba937eSOliver Neukum 
369afba937eSOliver Neukum static ssize_t wdm_read
370afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
371afba937eSOliver Neukum {
372afba937eSOliver Neukum 	int rv, cntr;
373afba937eSOliver Neukum 	int i = 0;
374afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
375afba937eSOliver Neukum 
376afba937eSOliver Neukum 
377afba937eSOliver Neukum 	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
378afba937eSOliver Neukum 	if (rv < 0)
379afba937eSOliver Neukum 		return -ERESTARTSYS;
380afba937eSOliver Neukum 
381afba937eSOliver Neukum 	if (desc->length == 0) {
382afba937eSOliver Neukum 		desc->read = 0;
383afba937eSOliver Neukum retry:
384afba937eSOliver Neukum 		i++;
385afba937eSOliver Neukum 		rv = wait_event_interruptible(desc->wait,
386afba937eSOliver Neukum 					      test_bit(WDM_READ, &desc->flags));
387afba937eSOliver Neukum 
38817d80d56SOliver Neukum 		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
38917d80d56SOliver Neukum 			rv = -ENODEV;
39017d80d56SOliver Neukum 			goto err;
39117d80d56SOliver Neukum 		}
39217d80d56SOliver Neukum 		usb_mark_last_busy(interface_to_usbdev(desc->intf));
393afba937eSOliver Neukum 		if (rv < 0) {
394afba937eSOliver Neukum 			rv = -ERESTARTSYS;
395afba937eSOliver Neukum 			goto err;
396afba937eSOliver Neukum 		}
397afba937eSOliver Neukum 
398afba937eSOliver Neukum 		spin_lock_irq(&desc->iuspin);
399afba937eSOliver Neukum 
400afba937eSOliver Neukum 		if (desc->rerr) { /* read completed, error happened */
401afba937eSOliver Neukum 			int t = desc->rerr;
402afba937eSOliver Neukum 			desc->rerr = 0;
403afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
404afba937eSOliver Neukum 			err("reading had resulted in %d", t);
405afba937eSOliver Neukum 			rv = -EIO;
406afba937eSOliver Neukum 			goto err;
407afba937eSOliver Neukum 		}
408afba937eSOliver Neukum 		/*
409afba937eSOliver Neukum 		 * recheck whether we've lost the race
410afba937eSOliver Neukum 		 * against the completion handler
411afba937eSOliver Neukum 		 */
412afba937eSOliver Neukum 		if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
413afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
414afba937eSOliver Neukum 			goto retry;
415afba937eSOliver Neukum 		}
416afba937eSOliver Neukum 		if (!desc->reslength) { /* zero length read */
417afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
418afba937eSOliver Neukum 			goto retry;
419afba937eSOliver Neukum 		}
420afba937eSOliver Neukum 		clear_bit(WDM_READ, &desc->flags);
421afba937eSOliver Neukum 		spin_unlock_irq(&desc->iuspin);
422afba937eSOliver Neukum 	}
423afba937eSOliver Neukum 
424afba937eSOliver Neukum 	cntr = count > desc->length ? desc->length : count;
425afba937eSOliver Neukum 	rv = copy_to_user(buffer, desc->ubuf, cntr);
426afba937eSOliver Neukum 	if (rv > 0) {
427afba937eSOliver Neukum 		rv = -EFAULT;
428afba937eSOliver Neukum 		goto err;
429afba937eSOliver Neukum 	}
430afba937eSOliver Neukum 
431afba937eSOliver Neukum 	for (i = 0; i < desc->length - cntr; i++)
432afba937eSOliver Neukum 		desc->ubuf[i] = desc->ubuf[i + cntr];
433afba937eSOliver Neukum 
434afba937eSOliver Neukum 	desc->length -= cntr;
43587d65e54SOliver Neukum 	/* in case we had outstanding data */
43687d65e54SOliver Neukum 	if (!desc->length)
43787d65e54SOliver Neukum 		clear_bit(WDM_READ, &desc->flags);
438afba937eSOliver Neukum 	rv = cntr;
439afba937eSOliver Neukum 
440afba937eSOliver Neukum err:
441afba937eSOliver Neukum 	mutex_unlock(&desc->rlock);
442afba937eSOliver Neukum 	if (rv < 0)
443afba937eSOliver Neukum 		err("wdm_read: exit error");
444afba937eSOliver Neukum 	return rv;
445afba937eSOliver Neukum }
446afba937eSOliver Neukum 
447afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id)
448afba937eSOliver Neukum {
449afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
450afba937eSOliver Neukum 
451afba937eSOliver Neukum 	wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
452afba937eSOliver Neukum 	if (desc->werr < 0)
453afba937eSOliver Neukum 		err("Error in flush path: %d", desc->werr);
454afba937eSOliver Neukum 
455afba937eSOliver Neukum 	return desc->werr;
456afba937eSOliver Neukum }
457afba937eSOliver Neukum 
458afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
459afba937eSOliver Neukum {
460afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
461afba937eSOliver Neukum 	unsigned long flags;
462afba937eSOliver Neukum 	unsigned int mask = 0;
463afba937eSOliver Neukum 
464afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
465afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
466afba937eSOliver Neukum 		mask = POLLERR;
467afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
468afba937eSOliver Neukum 		goto desc_out;
469afba937eSOliver Neukum 	}
470afba937eSOliver Neukum 	if (test_bit(WDM_READ, &desc->flags))
471afba937eSOliver Neukum 		mask = POLLIN | POLLRDNORM;
472afba937eSOliver Neukum 	if (desc->rerr || desc->werr)
473afba937eSOliver Neukum 		mask |= POLLERR;
474afba937eSOliver Neukum 	if (!test_bit(WDM_IN_USE, &desc->flags))
475afba937eSOliver Neukum 		mask |= POLLOUT | POLLWRNORM;
476afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
477afba937eSOliver Neukum 
478afba937eSOliver Neukum 	poll_wait(file, &desc->wait, wait);
479afba937eSOliver Neukum 
480afba937eSOliver Neukum desc_out:
481afba937eSOliver Neukum 	return mask;
482afba937eSOliver Neukum }
483afba937eSOliver Neukum 
484afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file)
485afba937eSOliver Neukum {
486afba937eSOliver Neukum 	int minor = iminor(inode);
487afba937eSOliver Neukum 	int rv = -ENODEV;
488afba937eSOliver Neukum 	struct usb_interface *intf;
489afba937eSOliver Neukum 	struct wdm_device *desc;
490afba937eSOliver Neukum 
491afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
492afba937eSOliver Neukum 	intf = usb_find_interface(&wdm_driver, minor);
493afba937eSOliver Neukum 	if (!intf)
494afba937eSOliver Neukum 		goto out;
495afba937eSOliver Neukum 
496afba937eSOliver Neukum 	desc = usb_get_intfdata(intf);
497afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags))
498afba937eSOliver Neukum 		goto out;
499afba937eSOliver Neukum 
50017d80d56SOliver Neukum 	;
501afba937eSOliver Neukum 	file->private_data = desc;
502afba937eSOliver Neukum 
50317d80d56SOliver Neukum 	rv = usb_autopm_get_interface(desc->intf);
50417d80d56SOliver Neukum 	if (rv < 0) {
50517d80d56SOliver Neukum 		err("Error autopm - %d", rv);
50617d80d56SOliver Neukum 		goto out;
50717d80d56SOliver Neukum 	}
50817d80d56SOliver Neukum 	intf->needs_remote_wakeup = 1;
509afba937eSOliver Neukum 
51017d80d56SOliver Neukum 	mutex_lock(&desc->plock);
51117d80d56SOliver Neukum 	if (!desc->count++) {
51217d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_KERNEL);
513afba937eSOliver Neukum 		if (rv < 0) {
514afba937eSOliver Neukum 			desc->count--;
515afba937eSOliver Neukum 			err("Error submitting int urb - %d", rv);
516afba937eSOliver Neukum 		}
51717d80d56SOliver Neukum 	} else {
518afba937eSOliver Neukum 		rv = 0;
51917d80d56SOliver Neukum 	}
52017d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
52117d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
522afba937eSOliver Neukum out:
523afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
524afba937eSOliver Neukum 	return rv;
525afba937eSOliver Neukum }
526afba937eSOliver Neukum 
527afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file)
528afba937eSOliver Neukum {
529afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
530afba937eSOliver Neukum 
531afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
53217d80d56SOliver Neukum 	mutex_lock(&desc->plock);
533afba937eSOliver Neukum 	desc->count--;
53417d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
53517d80d56SOliver Neukum 
536afba937eSOliver Neukum 	if (!desc->count) {
537afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
538afba937eSOliver Neukum 		kill_urbs(desc);
53917d80d56SOliver Neukum 		if (!test_bit(WDM_DISCONNECTING, &desc->flags))
54017d80d56SOliver Neukum 			desc->intf->needs_remote_wakeup = 0;
541afba937eSOliver Neukum 	}
542afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
543afba937eSOliver Neukum 	return 0;
544afba937eSOliver Neukum }
545afba937eSOliver Neukum 
546afba937eSOliver Neukum static const struct file_operations wdm_fops = {
547afba937eSOliver Neukum 	.owner =	THIS_MODULE,
548afba937eSOliver Neukum 	.read =		wdm_read,
549afba937eSOliver Neukum 	.write =	wdm_write,
550afba937eSOliver Neukum 	.open =		wdm_open,
551afba937eSOliver Neukum 	.flush =	wdm_flush,
552afba937eSOliver Neukum 	.release =	wdm_release,
553afba937eSOliver Neukum 	.poll =		wdm_poll
554afba937eSOliver Neukum };
555afba937eSOliver Neukum 
556afba937eSOliver Neukum static struct usb_class_driver wdm_class = {
557afba937eSOliver Neukum 	.name =		"cdc-wdm%d",
558afba937eSOliver Neukum 	.fops =		&wdm_fops,
559afba937eSOliver Neukum 	.minor_base =	WDM_MINOR_BASE,
560afba937eSOliver Neukum };
561afba937eSOliver Neukum 
562afba937eSOliver Neukum /* --- error handling --- */
563afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work)
564afba937eSOliver Neukum {
565afba937eSOliver Neukum 	struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
566afba937eSOliver Neukum 	unsigned long flags;
567afba937eSOliver Neukum 	int rv;
568afba937eSOliver Neukum 
569afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
570afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
571afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
572afba937eSOliver Neukum 	} else {
573afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
574afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_KERNEL);
575afba937eSOliver Neukum 		if (rv < 0 && rv != -EPERM) {
576afba937eSOliver Neukum 			spin_lock_irqsave(&desc->iuspin, flags);
577afba937eSOliver Neukum 			if (!test_bit(WDM_DISCONNECTING, &desc->flags))
578afba937eSOliver Neukum 				schedule_work(&desc->rxwork);
579afba937eSOliver Neukum 			spin_unlock_irqrestore(&desc->iuspin, flags);
580afba937eSOliver Neukum 		}
581afba937eSOliver Neukum 	}
582afba937eSOliver Neukum }
583afba937eSOliver Neukum 
584afba937eSOliver Neukum /* --- hotplug --- */
585afba937eSOliver Neukum 
586afba937eSOliver Neukum static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
587afba937eSOliver Neukum {
588afba937eSOliver Neukum 	int rv = -EINVAL;
589afba937eSOliver Neukum 	struct usb_device *udev = interface_to_usbdev(intf);
590afba937eSOliver Neukum 	struct wdm_device *desc;
591afba937eSOliver Neukum 	struct usb_host_interface *iface;
592afba937eSOliver Neukum 	struct usb_endpoint_descriptor *ep;
593afba937eSOliver Neukum 	struct usb_cdc_dmm_desc *dmhd;
594afba937eSOliver Neukum 	u8 *buffer = intf->altsetting->extra;
595afba937eSOliver Neukum 	int buflen = intf->altsetting->extralen;
596afba937eSOliver Neukum 	u16 maxcom = 0;
597afba937eSOliver Neukum 
598afba937eSOliver Neukum 	if (!buffer)
599afba937eSOliver Neukum 		goto out;
600afba937eSOliver Neukum 
601afba937eSOliver Neukum 	while (buflen > 0) {
602afba937eSOliver Neukum 		if (buffer [1] != USB_DT_CS_INTERFACE) {
603afba937eSOliver Neukum 			err("skipping garbage");
604afba937eSOliver Neukum 			goto next_desc;
605afba937eSOliver Neukum 		}
606afba937eSOliver Neukum 
607afba937eSOliver Neukum 		switch (buffer [2]) {
608afba937eSOliver Neukum 		case USB_CDC_HEADER_TYPE:
609afba937eSOliver Neukum 			break;
610afba937eSOliver Neukum 		case USB_CDC_DMM_TYPE:
611afba937eSOliver Neukum 			dmhd = (struct usb_cdc_dmm_desc *)buffer;
612afba937eSOliver Neukum 			maxcom = le16_to_cpu(dmhd->wMaxCommand);
613afba937eSOliver Neukum 			dev_dbg(&intf->dev,
614afba937eSOliver Neukum 				"Finding maximum buffer length: %d", maxcom);
615afba937eSOliver Neukum 			break;
616afba937eSOliver Neukum 		default:
617afba937eSOliver Neukum 			err("Ignoring extra header, type %d, length %d",
618afba937eSOliver Neukum 				buffer[2], buffer[0]);
619afba937eSOliver Neukum 			break;
620afba937eSOliver Neukum 		}
621afba937eSOliver Neukum next_desc:
622afba937eSOliver Neukum 		buflen -= buffer[0];
623afba937eSOliver Neukum 		buffer += buffer[0];
624afba937eSOliver Neukum 	}
625afba937eSOliver Neukum 
626afba937eSOliver Neukum 	rv = -ENOMEM;
627afba937eSOliver Neukum 	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
628afba937eSOliver Neukum 	if (!desc)
629afba937eSOliver Neukum 		goto out;
630afba937eSOliver Neukum 	mutex_init(&desc->wlock);
631afba937eSOliver Neukum 	mutex_init(&desc->rlock);
63217d80d56SOliver Neukum 	mutex_init(&desc->plock);
633afba937eSOliver Neukum 	spin_lock_init(&desc->iuspin);
634afba937eSOliver Neukum 	init_waitqueue_head(&desc->wait);
635afba937eSOliver Neukum 	desc->wMaxCommand = maxcom;
636afba937eSOliver Neukum 	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
637afba937eSOliver Neukum 	desc->intf = intf;
638afba937eSOliver Neukum 	INIT_WORK(&desc->rxwork, wdm_rxwork);
639afba937eSOliver Neukum 
640afba937eSOliver Neukum 	iface = &intf->altsetting[0];
641afba937eSOliver Neukum 	ep = &iface->endpoint[0].desc;
642afba937eSOliver Neukum 	if (!usb_endpoint_is_int_in(ep)) {
643afba937eSOliver Neukum 		rv = -EINVAL;
644afba937eSOliver Neukum 		goto err;
645afba937eSOliver Neukum 	}
646afba937eSOliver Neukum 
647fa4144b7SAl Viro 	desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
648fa4144b7SAl Viro 	desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
649afba937eSOliver Neukum 
650afba937eSOliver Neukum 	desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
651afba937eSOliver Neukum 	if (!desc->orq)
652afba937eSOliver Neukum 		goto err;
653afba937eSOliver Neukum 	desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
654afba937eSOliver Neukum 	if (!desc->irq)
655afba937eSOliver Neukum 		goto err;
656afba937eSOliver Neukum 
657afba937eSOliver Neukum 	desc->validity = usb_alloc_urb(0, GFP_KERNEL);
658afba937eSOliver Neukum 	if (!desc->validity)
659afba937eSOliver Neukum 		goto err;
660afba937eSOliver Neukum 
661afba937eSOliver Neukum 	desc->response = usb_alloc_urb(0, GFP_KERNEL);
662afba937eSOliver Neukum 	if (!desc->response)
663afba937eSOliver Neukum 		goto err;
664afba937eSOliver Neukum 
665afba937eSOliver Neukum 	desc->command = usb_alloc_urb(0, GFP_KERNEL);
666afba937eSOliver Neukum 	if (!desc->command)
667afba937eSOliver Neukum 		goto err;
668afba937eSOliver Neukum 
669afba937eSOliver Neukum 	desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
670afba937eSOliver Neukum 	if (!desc->ubuf)
671afba937eSOliver Neukum 		goto err;
672afba937eSOliver Neukum 
673afba937eSOliver Neukum 	desc->sbuf = usb_buffer_alloc(interface_to_usbdev(intf),
674afba937eSOliver Neukum 					desc->wMaxPacketSize,
675afba937eSOliver Neukum 					GFP_KERNEL,
676afba937eSOliver Neukum 					&desc->validity->transfer_dma);
677afba937eSOliver Neukum 	if (!desc->sbuf)
678afba937eSOliver Neukum 		goto err;
679afba937eSOliver Neukum 
680afba937eSOliver Neukum 	desc->inbuf = usb_buffer_alloc(interface_to_usbdev(intf),
681afba937eSOliver Neukum 					desc->bMaxPacketSize0,
682afba937eSOliver Neukum 					GFP_KERNEL,
683afba937eSOliver Neukum 					&desc->response->transfer_dma);
684afba937eSOliver Neukum 	if (!desc->inbuf)
685afba937eSOliver Neukum 		goto err2;
686afba937eSOliver Neukum 
687afba937eSOliver Neukum 	usb_fill_int_urb(
688afba937eSOliver Neukum 		desc->validity,
689afba937eSOliver Neukum 		interface_to_usbdev(intf),
690afba937eSOliver Neukum 		usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
691afba937eSOliver Neukum 		desc->sbuf,
692afba937eSOliver Neukum 		desc->wMaxPacketSize,
693afba937eSOliver Neukum 		wdm_int_callback,
694afba937eSOliver Neukum 		desc,
695afba937eSOliver Neukum 		ep->bInterval
696afba937eSOliver Neukum 	);
697afba937eSOliver Neukum 	desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
698afba937eSOliver Neukum 
699afba937eSOliver Neukum 	usb_set_intfdata(intf, desc);
700afba937eSOliver Neukum 	rv = usb_register_dev(intf, &wdm_class);
701afba937eSOliver Neukum 	dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
702afba937eSOliver Neukum 		 intf->minor - WDM_MINOR_BASE);
703afba937eSOliver Neukum 	if (rv < 0)
704afba937eSOliver Neukum 		goto err;
705afba937eSOliver Neukum out:
706afba937eSOliver Neukum 	return rv;
707afba937eSOliver Neukum err2:
708afba937eSOliver Neukum 	usb_buffer_free(interface_to_usbdev(desc->intf),
709afba937eSOliver Neukum 			desc->wMaxPacketSize,
710afba937eSOliver Neukum 			desc->sbuf,
711afba937eSOliver Neukum 			desc->validity->transfer_dma);
712afba937eSOliver Neukum err:
713afba937eSOliver Neukum 	free_urbs(desc);
714afba937eSOliver Neukum 	kfree(desc->ubuf);
715afba937eSOliver Neukum 	kfree(desc->orq);
716afba937eSOliver Neukum 	kfree(desc->irq);
717afba937eSOliver Neukum 	kfree(desc);
718afba937eSOliver Neukum 	return rv;
719afba937eSOliver Neukum }
720afba937eSOliver Neukum 
721afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf)
722afba937eSOliver Neukum {
723afba937eSOliver Neukum 	struct wdm_device *desc;
724afba937eSOliver Neukum 	unsigned long flags;
725afba937eSOliver Neukum 
726afba937eSOliver Neukum 	usb_deregister_dev(intf, &wdm_class);
727afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
728afba937eSOliver Neukum 	desc = usb_get_intfdata(intf);
729afba937eSOliver Neukum 
730afba937eSOliver Neukum 	/* the spinlock makes sure no new urbs are generated in the callbacks */
731afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
732afba937eSOliver Neukum 	set_bit(WDM_DISCONNECTING, &desc->flags);
733afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
73417d80d56SOliver Neukum 	/* to terminate pending flushes */
735afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
736afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
737afba937eSOliver Neukum 	cancel_work_sync(&desc->rxwork);
738afba937eSOliver Neukum 	kill_urbs(desc);
739afba937eSOliver Neukum 	wake_up_all(&desc->wait);
740afba937eSOliver Neukum 	if (!desc->count)
741afba937eSOliver Neukum 		cleanup(desc);
742afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
743afba937eSOliver Neukum }
744afba937eSOliver Neukum 
74517d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
74617d80d56SOliver Neukum {
74717d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
74817d80d56SOliver Neukum 	int rv = 0;
74917d80d56SOliver Neukum 
75017d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
75117d80d56SOliver Neukum 
75217d80d56SOliver Neukum 	mutex_lock(&desc->plock);
75335758589SOliver Neukum #ifdef CONFIG_PM
75417d80d56SOliver Neukum 	if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) {
75517d80d56SOliver Neukum 		rv = -EBUSY;
75617d80d56SOliver Neukum 	} else {
75735758589SOliver Neukum #endif
75817d80d56SOliver Neukum 		cancel_work_sync(&desc->rxwork);
75917d80d56SOliver Neukum 		kill_urbs(desc);
76035758589SOliver Neukum #ifdef CONFIG_PM
76117d80d56SOliver Neukum 	}
76235758589SOliver Neukum #endif
76317d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
76417d80d56SOliver Neukum 
76517d80d56SOliver Neukum 	return rv;
76617d80d56SOliver Neukum }
76717d80d56SOliver Neukum 
76817d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc)
76917d80d56SOliver Neukum {
77017d80d56SOliver Neukum 	int rv = 0;
77117d80d56SOliver Neukum 
77217d80d56SOliver Neukum 	if (desc->count) {
77317d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_NOIO);
77417d80d56SOliver Neukum 		if (rv < 0)
77517d80d56SOliver Neukum 			err("Error resume submitting int urb - %d", rv);
77617d80d56SOliver Neukum 	}
77717d80d56SOliver Neukum 	return rv;
77817d80d56SOliver Neukum }
77917d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf)
78017d80d56SOliver Neukum {
78117d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
78217d80d56SOliver Neukum 	int rv;
78317d80d56SOliver Neukum 
78417d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
78517d80d56SOliver Neukum 	mutex_lock(&desc->plock);
78617d80d56SOliver Neukum 	rv = recover_from_urb_loss(desc);
78717d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
78817d80d56SOliver Neukum 	return rv;
78917d80d56SOliver Neukum }
79017d80d56SOliver Neukum 
79117d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf)
79217d80d56SOliver Neukum {
79317d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
79417d80d56SOliver Neukum 
79517d80d56SOliver Neukum 	mutex_lock(&desc->plock);
79617d80d56SOliver Neukum 	return 0;
79717d80d56SOliver Neukum }
79817d80d56SOliver Neukum 
79917d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf)
80017d80d56SOliver Neukum {
80117d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
80217d80d56SOliver Neukum 	int rv;
80317d80d56SOliver Neukum 
80417d80d56SOliver Neukum 	rv = recover_from_urb_loss(desc);
80517d80d56SOliver Neukum 	mutex_unlock(&desc->plock);
80617d80d56SOliver Neukum 	return 0;
80717d80d56SOliver Neukum }
80817d80d56SOliver Neukum 
809afba937eSOliver Neukum static struct usb_driver wdm_driver = {
810afba937eSOliver Neukum 	.name =		"cdc_wdm",
811afba937eSOliver Neukum 	.probe =	wdm_probe,
812afba937eSOliver Neukum 	.disconnect =	wdm_disconnect,
81317d80d56SOliver Neukum 	.suspend =	wdm_suspend,
81417d80d56SOliver Neukum 	.resume =	wdm_resume,
81517d80d56SOliver Neukum 	.reset_resume =	wdm_resume,
81617d80d56SOliver Neukum 	.pre_reset =	wdm_pre_reset,
81717d80d56SOliver Neukum 	.post_reset =	wdm_post_reset,
818afba937eSOliver Neukum 	.id_table =	wdm_ids,
81917d80d56SOliver Neukum 	.supports_autosuspend = 1,
820afba937eSOliver Neukum };
821afba937eSOliver Neukum 
822afba937eSOliver Neukum /* --- low level module stuff --- */
823afba937eSOliver Neukum 
824afba937eSOliver Neukum static int __init wdm_init(void)
825afba937eSOliver Neukum {
826afba937eSOliver Neukum 	int rv;
827afba937eSOliver Neukum 
828afba937eSOliver Neukum 	rv = usb_register(&wdm_driver);
829afba937eSOliver Neukum 
830afba937eSOliver Neukum 	return rv;
831afba937eSOliver Neukum }
832afba937eSOliver Neukum 
833afba937eSOliver Neukum static void __exit wdm_exit(void)
834afba937eSOliver Neukum {
835afba937eSOliver Neukum 	usb_deregister(&wdm_driver);
836afba937eSOliver Neukum }
837afba937eSOliver Neukum 
838afba937eSOliver Neukum module_init(wdm_init);
839afba937eSOliver Neukum module_exit(wdm_exit);
840afba937eSOliver Neukum 
841afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR);
84287d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC);
843afba937eSOliver Neukum MODULE_LICENSE("GPL");
844