xref: /openbmc/linux/drivers/usb/class/cdc-wdm.c (revision 65db4305)
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>
26afba937eSOliver Neukum 
27afba937eSOliver Neukum /*
28afba937eSOliver Neukum  * Version Information
29afba937eSOliver Neukum  */
3087d65e54SOliver Neukum #define DRIVER_VERSION "v0.03"
31afba937eSOliver Neukum #define DRIVER_AUTHOR "Oliver Neukum"
3287d65e54SOliver Neukum #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
33afba937eSOliver Neukum 
346ef4852bSNémeth Márton static const struct usb_device_id wdm_ids[] = {
35afba937eSOliver Neukum 	{
36afba937eSOliver Neukum 		.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
37afba937eSOliver Neukum 				 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
38afba937eSOliver Neukum 		.bInterfaceClass = USB_CLASS_COMM,
39afba937eSOliver Neukum 		.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
40afba937eSOliver Neukum 	},
41afba937eSOliver Neukum 	{ }
42afba937eSOliver Neukum };
43afba937eSOliver Neukum 
44aa5380b9SOliver Neukum MODULE_DEVICE_TABLE (usb, wdm_ids);
45aa5380b9SOliver Neukum 
46afba937eSOliver Neukum #define WDM_MINOR_BASE	176
47afba937eSOliver Neukum 
48afba937eSOliver Neukum 
49afba937eSOliver Neukum #define WDM_IN_USE		1
50afba937eSOliver Neukum #define WDM_DISCONNECTING	2
51afba937eSOliver Neukum #define WDM_RESULT		3
52afba937eSOliver Neukum #define WDM_READ		4
53afba937eSOliver Neukum #define WDM_INT_STALL		5
54afba937eSOliver Neukum #define WDM_POLL_RUNNING	6
55922a5eadSOliver Neukum #define WDM_RESPONDING		7
56beb1d35fSOliver Neukum #define WDM_SUSPENDING		8
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;
91860e41a7SOliver Neukum 	struct mutex		lock;
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);
119922a5eadSOliver Neukum 	clear_bit(WDM_RESPONDING, &desc->flags);
120afba937eSOliver Neukum 
121afba937eSOliver Neukum 	if (status) {
122afba937eSOliver Neukum 		switch (status) {
123afba937eSOliver Neukum 		case -ENOENT:
124afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
125afba937eSOliver Neukum 				"nonzero urb status received: -ENOENT");
126922a5eadSOliver Neukum 			goto skip_error;
127afba937eSOliver Neukum 		case -ECONNRESET:
128afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
129afba937eSOliver Neukum 				"nonzero urb status received: -ECONNRESET");
130922a5eadSOliver Neukum 			goto skip_error;
131afba937eSOliver Neukum 		case -ESHUTDOWN:
132afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
133afba937eSOliver Neukum 				"nonzero urb status received: -ESHUTDOWN");
134922a5eadSOliver Neukum 			goto skip_error;
135afba937eSOliver Neukum 		case -EPIPE:
1369908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1379908a32eSGreg Kroah-Hartman 				"nonzero urb status received: -EPIPE\n");
138afba937eSOliver Neukum 			break;
139afba937eSOliver Neukum 		default:
1409908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1419908a32eSGreg Kroah-Hartman 				"Unexpected error %d\n", status);
142afba937eSOliver Neukum 			break;
143afba937eSOliver Neukum 		}
144afba937eSOliver Neukum 	}
145afba937eSOliver Neukum 
146afba937eSOliver Neukum 	desc->rerr = status;
147afba937eSOliver Neukum 	desc->reslength = urb->actual_length;
148afba937eSOliver Neukum 	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
149afba937eSOliver Neukum 	desc->length += desc->reslength;
150922a5eadSOliver Neukum skip_error:
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);
233922a5eadSOliver Neukum 	set_bit(WDM_RESPONDING, &desc->flags);
234beb1d35fSOliver Neukum 	if (!test_bit(WDM_DISCONNECTING, &desc->flags)
235beb1d35fSOliver Neukum 		&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
236afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_ATOMIC);
237afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
238afba937eSOliver Neukum 			__func__, rv);
239afba937eSOliver Neukum 	}
240afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
241afba937eSOliver Neukum 	if (rv < 0) {
242922a5eadSOliver Neukum 		clear_bit(WDM_RESPONDING, &desc->flags);
243afba937eSOliver Neukum 		if (rv == -EPERM)
244afba937eSOliver Neukum 			return;
245afba937eSOliver Neukum 		if (rv == -ENOMEM) {
246afba937eSOliver Neukum sw:
247afba937eSOliver Neukum 			rv = schedule_work(&desc->rxwork);
248afba937eSOliver Neukum 			if (rv)
2499908a32eSGreg Kroah-Hartman 				dev_err(&desc->intf->dev,
2509908a32eSGreg Kroah-Hartman 					"Cannot schedule work\n");
251afba937eSOliver Neukum 		}
252afba937eSOliver Neukum 	}
253afba937eSOliver Neukum exit:
254afba937eSOliver Neukum 	rv = usb_submit_urb(urb, GFP_ATOMIC);
255afba937eSOliver Neukum 	if (rv)
2569908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev,
2579908a32eSGreg Kroah-Hartman 			"%s - usb_submit_urb failed with result %d\n",
258afba937eSOliver Neukum 			__func__, rv);
259afba937eSOliver Neukum 
260afba937eSOliver Neukum }
261afba937eSOliver Neukum 
262afba937eSOliver Neukum static void kill_urbs(struct wdm_device *desc)
263afba937eSOliver Neukum {
26417d80d56SOliver Neukum 	/* the order here is essential */
265afba937eSOliver Neukum 	usb_kill_urb(desc->command);
266afba937eSOliver Neukum 	usb_kill_urb(desc->validity);
267afba937eSOliver Neukum 	usb_kill_urb(desc->response);
268afba937eSOliver Neukum }
269afba937eSOliver Neukum 
270afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc)
271afba937eSOliver Neukum {
272afba937eSOliver Neukum 	usb_free_urb(desc->validity);
273afba937eSOliver Neukum 	usb_free_urb(desc->response);
274afba937eSOliver Neukum 	usb_free_urb(desc->command);
275afba937eSOliver Neukum }
276afba937eSOliver Neukum 
277afba937eSOliver Neukum static void cleanup(struct wdm_device *desc)
278afba937eSOliver Neukum {
279997ea58eSDaniel Mack 	usb_free_coherent(interface_to_usbdev(desc->intf),
280afba937eSOliver Neukum 			  desc->wMaxPacketSize,
281afba937eSOliver Neukum 			  desc->sbuf,
282afba937eSOliver Neukum 			  desc->validity->transfer_dma);
283997ea58eSDaniel Mack 	usb_free_coherent(interface_to_usbdev(desc->intf),
284878b753eSRobert Lukassen 			  desc->bMaxPacketSize0,
285afba937eSOliver Neukum 			  desc->inbuf,
286afba937eSOliver Neukum 			  desc->response->transfer_dma);
287afba937eSOliver Neukum 	kfree(desc->orq);
288afba937eSOliver Neukum 	kfree(desc->irq);
289afba937eSOliver Neukum 	kfree(desc->ubuf);
290afba937eSOliver Neukum 	free_urbs(desc);
291afba937eSOliver Neukum 	kfree(desc);
292afba937eSOliver Neukum }
293afba937eSOliver Neukum 
294afba937eSOliver Neukum static ssize_t wdm_write
295afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
296afba937eSOliver Neukum {
297afba937eSOliver Neukum 	u8 *buf;
298afba937eSOliver Neukum 	int rv = -EMSGSIZE, r, we;
299afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
300afba937eSOliver Neukum 	struct usb_ctrlrequest *req;
301afba937eSOliver Neukum 
302afba937eSOliver Neukum 	if (count > desc->wMaxCommand)
303afba937eSOliver Neukum 		count = desc->wMaxCommand;
304afba937eSOliver Neukum 
305afba937eSOliver Neukum 	spin_lock_irq(&desc->iuspin);
306afba937eSOliver Neukum 	we = desc->werr;
307afba937eSOliver Neukum 	desc->werr = 0;
308afba937eSOliver Neukum 	spin_unlock_irq(&desc->iuspin);
309afba937eSOliver Neukum 	if (we < 0)
310afba937eSOliver Neukum 		return -EIO;
311afba937eSOliver Neukum 
312860e41a7SOliver Neukum 	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
313860e41a7SOliver Neukum 	if (!buf) {
314860e41a7SOliver Neukum 		rv = -ENOMEM;
315afba937eSOliver Neukum 		goto outnl;
316860e41a7SOliver Neukum 	}
317860e41a7SOliver Neukum 
318860e41a7SOliver Neukum 	r = copy_from_user(buf, buffer, count);
319860e41a7SOliver Neukum 	if (r > 0) {
320860e41a7SOliver Neukum 		kfree(buf);
321860e41a7SOliver Neukum 		rv = -EFAULT;
322860e41a7SOliver Neukum 		goto outnl;
323860e41a7SOliver Neukum 	}
324860e41a7SOliver Neukum 
325860e41a7SOliver Neukum 	/* concurrent writes and disconnect */
326860e41a7SOliver Neukum 	r = mutex_lock_interruptible(&desc->lock);
327860e41a7SOliver Neukum 	rv = -ERESTARTSYS;
328860e41a7SOliver Neukum 	if (r) {
329860e41a7SOliver Neukum 		kfree(buf);
330860e41a7SOliver Neukum 		goto outnl;
331860e41a7SOliver Neukum 	}
332860e41a7SOliver Neukum 
333860e41a7SOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
334860e41a7SOliver Neukum 		kfree(buf);
335860e41a7SOliver Neukum 		rv = -ENODEV;
336860e41a7SOliver Neukum 		goto outnp;
337860e41a7SOliver Neukum 	}
338afba937eSOliver Neukum 
33917d80d56SOliver Neukum 	r = usb_autopm_get_interface(desc->intf);
340860e41a7SOliver Neukum 	if (r < 0) {
341860e41a7SOliver Neukum 		kfree(buf);
34217d80d56SOliver Neukum 		goto outnp;
343860e41a7SOliver Neukum 	}
3447f1dc313SOliver Neukum 
3450cdfb819SDavid Sterba 	if (!(file->f_flags & O_NONBLOCK))
346afba937eSOliver Neukum 		r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
347afba937eSOliver Neukum 								&desc->flags));
3487f1dc313SOliver Neukum 	else
3497f1dc313SOliver Neukum 		if (test_bit(WDM_IN_USE, &desc->flags))
3507f1dc313SOliver Neukum 			r = -EAGAIN;
351860e41a7SOliver Neukum 	if (r < 0) {
352afba937eSOliver Neukum 		kfree(buf);
353afba937eSOliver Neukum 		goto out;
354afba937eSOliver Neukum 	}
355afba937eSOliver Neukum 
356afba937eSOliver Neukum 	req = desc->orq;
357afba937eSOliver Neukum 	usb_fill_control_urb(
358afba937eSOliver Neukum 		desc->command,
359afba937eSOliver Neukum 		interface_to_usbdev(desc->intf),
360afba937eSOliver Neukum 		/* using common endpoint 0 */
361afba937eSOliver Neukum 		usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
362afba937eSOliver Neukum 		(unsigned char *)req,
363afba937eSOliver Neukum 		buf,
364afba937eSOliver Neukum 		count,
365afba937eSOliver Neukum 		wdm_out_callback,
366afba937eSOliver Neukum 		desc
367afba937eSOliver Neukum 	);
368afba937eSOliver Neukum 
369afba937eSOliver Neukum 	req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
370afba937eSOliver Neukum 			     USB_RECIP_INTERFACE);
371afba937eSOliver Neukum 	req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
372afba937eSOliver Neukum 	req->wValue = 0;
373afba937eSOliver Neukum 	req->wIndex = desc->inum;
374afba937eSOliver Neukum 	req->wLength = cpu_to_le16(count);
375afba937eSOliver Neukum 	set_bit(WDM_IN_USE, &desc->flags);
376afba937eSOliver Neukum 
377afba937eSOliver Neukum 	rv = usb_submit_urb(desc->command, GFP_KERNEL);
378afba937eSOliver Neukum 	if (rv < 0) {
379afba937eSOliver Neukum 		kfree(buf);
380afba937eSOliver Neukum 		clear_bit(WDM_IN_USE, &desc->flags);
3819908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
382afba937eSOliver Neukum 	} else {
383afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
384afba937eSOliver Neukum 			req->wIndex);
385afba937eSOliver Neukum 	}
386afba937eSOliver Neukum out:
38717d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
38817d80d56SOliver Neukum outnp:
389860e41a7SOliver Neukum 	mutex_unlock(&desc->lock);
390afba937eSOliver Neukum outnl:
391afba937eSOliver Neukum 	return rv < 0 ? rv : count;
392afba937eSOliver Neukum }
393afba937eSOliver Neukum 
394afba937eSOliver Neukum static ssize_t wdm_read
395afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
396afba937eSOliver Neukum {
3977f1dc313SOliver Neukum 	int rv, cntr = 0;
398afba937eSOliver Neukum 	int i = 0;
399afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
400afba937eSOliver Neukum 
401afba937eSOliver Neukum 
402860e41a7SOliver Neukum 	rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */
403afba937eSOliver Neukum 	if (rv < 0)
404afba937eSOliver Neukum 		return -ERESTARTSYS;
405afba937eSOliver Neukum 
406afba937eSOliver Neukum 	if (desc->length == 0) {
407afba937eSOliver Neukum 		desc->read = 0;
408afba937eSOliver Neukum retry:
4097f1dc313SOliver Neukum 		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
4107f1dc313SOliver Neukum 			rv = -ENODEV;
4117f1dc313SOliver Neukum 			goto err;
4127f1dc313SOliver Neukum 		}
413afba937eSOliver Neukum 		i++;
4147f1dc313SOliver Neukum 		if (file->f_flags & O_NONBLOCK) {
4157f1dc313SOliver Neukum 			if (!test_bit(WDM_READ, &desc->flags)) {
4167f1dc313SOliver Neukum 				rv = cntr ? cntr : -EAGAIN;
4177f1dc313SOliver Neukum 				goto err;
4187f1dc313SOliver Neukum 			}
4197f1dc313SOliver Neukum 			rv = 0;
4207f1dc313SOliver Neukum 		} else {
421afba937eSOliver Neukum 			rv = wait_event_interruptible(desc->wait,
422afba937eSOliver Neukum 				test_bit(WDM_READ, &desc->flags));
4237f1dc313SOliver Neukum 		}
424afba937eSOliver Neukum 
4257f1dc313SOliver Neukum 		/* may have happened while we slept */
42617d80d56SOliver Neukum 		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
42717d80d56SOliver Neukum 			rv = -ENODEV;
42817d80d56SOliver Neukum 			goto err;
42917d80d56SOliver Neukum 		}
43017d80d56SOliver Neukum 		usb_mark_last_busy(interface_to_usbdev(desc->intf));
431afba937eSOliver Neukum 		if (rv < 0) {
432afba937eSOliver Neukum 			rv = -ERESTARTSYS;
433afba937eSOliver Neukum 			goto err;
434afba937eSOliver Neukum 		}
435afba937eSOliver Neukum 
436afba937eSOliver Neukum 		spin_lock_irq(&desc->iuspin);
437afba937eSOliver Neukum 
438afba937eSOliver Neukum 		if (desc->rerr) { /* read completed, error happened */
439afba937eSOliver Neukum 			desc->rerr = 0;
440afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
441afba937eSOliver Neukum 			rv = -EIO;
442afba937eSOliver Neukum 			goto err;
443afba937eSOliver Neukum 		}
444afba937eSOliver Neukum 		/*
445afba937eSOliver Neukum 		 * recheck whether we've lost the race
446afba937eSOliver Neukum 		 * against the completion handler
447afba937eSOliver Neukum 		 */
448afba937eSOliver Neukum 		if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
449afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
450afba937eSOliver Neukum 			goto retry;
451afba937eSOliver Neukum 		}
452afba937eSOliver Neukum 		if (!desc->reslength) { /* zero length read */
453afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
454afba937eSOliver Neukum 			goto retry;
455afba937eSOliver Neukum 		}
456afba937eSOliver Neukum 		clear_bit(WDM_READ, &desc->flags);
457afba937eSOliver Neukum 		spin_unlock_irq(&desc->iuspin);
458afba937eSOliver Neukum 	}
459afba937eSOliver Neukum 
460afba937eSOliver Neukum 	cntr = count > desc->length ? desc->length : count;
461afba937eSOliver Neukum 	rv = copy_to_user(buffer, desc->ubuf, cntr);
462afba937eSOliver Neukum 	if (rv > 0) {
463afba937eSOliver Neukum 		rv = -EFAULT;
464afba937eSOliver Neukum 		goto err;
465afba937eSOliver Neukum 	}
466afba937eSOliver Neukum 
467afba937eSOliver Neukum 	for (i = 0; i < desc->length - cntr; i++)
468afba937eSOliver Neukum 		desc->ubuf[i] = desc->ubuf[i + cntr];
469afba937eSOliver Neukum 
470afba937eSOliver Neukum 	desc->length -= cntr;
47187d65e54SOliver Neukum 	/* in case we had outstanding data */
47287d65e54SOliver Neukum 	if (!desc->length)
47387d65e54SOliver Neukum 		clear_bit(WDM_READ, &desc->flags);
474afba937eSOliver Neukum 	rv = cntr;
475afba937eSOliver Neukum 
476afba937eSOliver Neukum err:
477860e41a7SOliver Neukum 	mutex_unlock(&desc->lock);
478afba937eSOliver Neukum 	return rv;
479afba937eSOliver Neukum }
480afba937eSOliver Neukum 
481afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id)
482afba937eSOliver Neukum {
483afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
484afba937eSOliver Neukum 
485afba937eSOliver Neukum 	wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
486afba937eSOliver Neukum 	if (desc->werr < 0)
4879908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Error in flush path: %d\n",
4889908a32eSGreg Kroah-Hartman 			desc->werr);
489afba937eSOliver Neukum 
490afba937eSOliver Neukum 	return desc->werr;
491afba937eSOliver Neukum }
492afba937eSOliver Neukum 
493afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
494afba937eSOliver Neukum {
495afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
496afba937eSOliver Neukum 	unsigned long flags;
497afba937eSOliver Neukum 	unsigned int mask = 0;
498afba937eSOliver Neukum 
499afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
500afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
501afba937eSOliver Neukum 		mask = POLLERR;
502afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
503afba937eSOliver Neukum 		goto desc_out;
504afba937eSOliver Neukum 	}
505afba937eSOliver Neukum 	if (test_bit(WDM_READ, &desc->flags))
506afba937eSOliver Neukum 		mask = POLLIN | POLLRDNORM;
507afba937eSOliver Neukum 	if (desc->rerr || desc->werr)
508afba937eSOliver Neukum 		mask |= POLLERR;
509afba937eSOliver Neukum 	if (!test_bit(WDM_IN_USE, &desc->flags))
510afba937eSOliver Neukum 		mask |= POLLOUT | POLLWRNORM;
511afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
512afba937eSOliver Neukum 
513afba937eSOliver Neukum 	poll_wait(file, &desc->wait, wait);
514afba937eSOliver Neukum 
515afba937eSOliver Neukum desc_out:
516afba937eSOliver Neukum 	return mask;
517afba937eSOliver Neukum }
518afba937eSOliver Neukum 
519afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file)
520afba937eSOliver Neukum {
521afba937eSOliver Neukum 	int minor = iminor(inode);
522afba937eSOliver Neukum 	int rv = -ENODEV;
523afba937eSOliver Neukum 	struct usb_interface *intf;
524afba937eSOliver Neukum 	struct wdm_device *desc;
525afba937eSOliver Neukum 
526afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
527afba937eSOliver Neukum 	intf = usb_find_interface(&wdm_driver, minor);
528afba937eSOliver Neukum 	if (!intf)
529afba937eSOliver Neukum 		goto out;
530afba937eSOliver Neukum 
531afba937eSOliver Neukum 	desc = usb_get_intfdata(intf);
532afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags))
533afba937eSOliver Neukum 		goto out;
534afba937eSOliver Neukum 	file->private_data = desc;
535afba937eSOliver Neukum 
53617d80d56SOliver Neukum 	rv = usb_autopm_get_interface(desc->intf);
53717d80d56SOliver Neukum 	if (rv < 0) {
5389908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
53917d80d56SOliver Neukum 		goto out;
54017d80d56SOliver Neukum 	}
54117d80d56SOliver Neukum 	intf->needs_remote_wakeup = 1;
542afba937eSOliver Neukum 
543860e41a7SOliver Neukum 	mutex_lock(&desc->lock);
54417d80d56SOliver Neukum 	if (!desc->count++) {
545d771d8aaSOliver Neukum 		desc->werr = 0;
546d771d8aaSOliver Neukum 		desc->rerr = 0;
54717d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_KERNEL);
548afba937eSOliver Neukum 		if (rv < 0) {
549afba937eSOliver Neukum 			desc->count--;
5509908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
5519908a32eSGreg Kroah-Hartman 				"Error submitting int urb - %d\n", rv);
552afba937eSOliver Neukum 		}
55317d80d56SOliver Neukum 	} else {
554afba937eSOliver Neukum 		rv = 0;
55517d80d56SOliver Neukum 	}
556860e41a7SOliver Neukum 	mutex_unlock(&desc->lock);
55717d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
558afba937eSOliver Neukum out:
559afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
560afba937eSOliver Neukum 	return rv;
561afba937eSOliver Neukum }
562afba937eSOliver Neukum 
563afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file)
564afba937eSOliver Neukum {
565afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
566afba937eSOliver Neukum 
567afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
568860e41a7SOliver Neukum 	mutex_lock(&desc->lock);
569afba937eSOliver Neukum 	desc->count--;
570860e41a7SOliver Neukum 	mutex_unlock(&desc->lock);
57117d80d56SOliver Neukum 
572afba937eSOliver Neukum 	if (!desc->count) {
573afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
574afba937eSOliver Neukum 		kill_urbs(desc);
57517d80d56SOliver Neukum 		if (!test_bit(WDM_DISCONNECTING, &desc->flags))
57617d80d56SOliver Neukum 			desc->intf->needs_remote_wakeup = 0;
577afba937eSOliver Neukum 	}
578afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
579afba937eSOliver Neukum 	return 0;
580afba937eSOliver Neukum }
581afba937eSOliver Neukum 
582afba937eSOliver Neukum static const struct file_operations wdm_fops = {
583afba937eSOliver Neukum 	.owner =	THIS_MODULE,
584afba937eSOliver Neukum 	.read =		wdm_read,
585afba937eSOliver Neukum 	.write =	wdm_write,
586afba937eSOliver Neukum 	.open =		wdm_open,
587afba937eSOliver Neukum 	.flush =	wdm_flush,
588afba937eSOliver Neukum 	.release =	wdm_release,
5896038f373SArnd Bergmann 	.poll =		wdm_poll,
5906038f373SArnd Bergmann 	.llseek =	noop_llseek,
591afba937eSOliver Neukum };
592afba937eSOliver Neukum 
593afba937eSOliver Neukum static struct usb_class_driver wdm_class = {
594afba937eSOliver Neukum 	.name =		"cdc-wdm%d",
595afba937eSOliver Neukum 	.fops =		&wdm_fops,
596afba937eSOliver Neukum 	.minor_base =	WDM_MINOR_BASE,
597afba937eSOliver Neukum };
598afba937eSOliver Neukum 
599afba937eSOliver Neukum /* --- error handling --- */
600afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work)
601afba937eSOliver Neukum {
602afba937eSOliver Neukum 	struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
603afba937eSOliver Neukum 	unsigned long flags;
604afba937eSOliver Neukum 	int rv;
605afba937eSOliver Neukum 
606afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
607afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
608afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
609afba937eSOliver Neukum 	} else {
610afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
611afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_KERNEL);
612afba937eSOliver Neukum 		if (rv < 0 && rv != -EPERM) {
613afba937eSOliver Neukum 			spin_lock_irqsave(&desc->iuspin, flags);
614afba937eSOliver Neukum 			if (!test_bit(WDM_DISCONNECTING, &desc->flags))
615afba937eSOliver Neukum 				schedule_work(&desc->rxwork);
616afba937eSOliver Neukum 			spin_unlock_irqrestore(&desc->iuspin, flags);
617afba937eSOliver Neukum 		}
618afba937eSOliver Neukum 	}
619afba937eSOliver Neukum }
620afba937eSOliver Neukum 
621afba937eSOliver Neukum /* --- hotplug --- */
622afba937eSOliver Neukum 
623afba937eSOliver Neukum static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
624afba937eSOliver Neukum {
625afba937eSOliver Neukum 	int rv = -EINVAL;
626afba937eSOliver Neukum 	struct usb_device *udev = interface_to_usbdev(intf);
627afba937eSOliver Neukum 	struct wdm_device *desc;
628afba937eSOliver Neukum 	struct usb_host_interface *iface;
629afba937eSOliver Neukum 	struct usb_endpoint_descriptor *ep;
630afba937eSOliver Neukum 	struct usb_cdc_dmm_desc *dmhd;
631afba937eSOliver Neukum 	u8 *buffer = intf->altsetting->extra;
632afba937eSOliver Neukum 	int buflen = intf->altsetting->extralen;
633afba937eSOliver Neukum 	u16 maxcom = 0;
634afba937eSOliver Neukum 
635afba937eSOliver Neukum 	if (!buffer)
636afba937eSOliver Neukum 		goto out;
637afba937eSOliver Neukum 
638052fbc0dSOliver Neukum 	while (buflen > 2) {
639afba937eSOliver Neukum 		if (buffer [1] != USB_DT_CS_INTERFACE) {
6409908a32eSGreg Kroah-Hartman 			dev_err(&intf->dev, "skipping garbage\n");
641afba937eSOliver Neukum 			goto next_desc;
642afba937eSOliver Neukum 		}
643afba937eSOliver Neukum 
644afba937eSOliver Neukum 		switch (buffer [2]) {
645afba937eSOliver Neukum 		case USB_CDC_HEADER_TYPE:
646afba937eSOliver Neukum 			break;
647afba937eSOliver Neukum 		case USB_CDC_DMM_TYPE:
648afba937eSOliver Neukum 			dmhd = (struct usb_cdc_dmm_desc *)buffer;
649afba937eSOliver Neukum 			maxcom = le16_to_cpu(dmhd->wMaxCommand);
650afba937eSOliver Neukum 			dev_dbg(&intf->dev,
651afba937eSOliver Neukum 				"Finding maximum buffer length: %d", maxcom);
652afba937eSOliver Neukum 			break;
653afba937eSOliver Neukum 		default:
6549908a32eSGreg Kroah-Hartman 			dev_err(&intf->dev,
6559908a32eSGreg Kroah-Hartman 				"Ignoring extra header, type %d, length %d\n",
656afba937eSOliver Neukum 				buffer[2], buffer[0]);
657afba937eSOliver Neukum 			break;
658afba937eSOliver Neukum 		}
659afba937eSOliver Neukum next_desc:
660afba937eSOliver Neukum 		buflen -= buffer[0];
661afba937eSOliver Neukum 		buffer += buffer[0];
662afba937eSOliver Neukum 	}
663afba937eSOliver Neukum 
664afba937eSOliver Neukum 	rv = -ENOMEM;
665afba937eSOliver Neukum 	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
666afba937eSOliver Neukum 	if (!desc)
667afba937eSOliver Neukum 		goto out;
668860e41a7SOliver Neukum 	mutex_init(&desc->lock);
669afba937eSOliver Neukum 	spin_lock_init(&desc->iuspin);
670afba937eSOliver Neukum 	init_waitqueue_head(&desc->wait);
671afba937eSOliver Neukum 	desc->wMaxCommand = maxcom;
672052fbc0dSOliver Neukum 	/* this will be expanded and needed in hardware endianness */
673afba937eSOliver Neukum 	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
674afba937eSOliver Neukum 	desc->intf = intf;
675afba937eSOliver Neukum 	INIT_WORK(&desc->rxwork, wdm_rxwork);
676afba937eSOliver Neukum 
677afba937eSOliver Neukum 	rv = -EINVAL;
678052fbc0dSOliver Neukum 	iface = intf->cur_altsetting;
679052fbc0dSOliver Neukum 	if (iface->desc.bNumEndpoints != 1)
680afba937eSOliver Neukum 		goto err;
681052fbc0dSOliver Neukum 	ep = &iface->endpoint[0].desc;
682052fbc0dSOliver Neukum 	if (!ep || !usb_endpoint_is_int_in(ep))
683052fbc0dSOliver Neukum 		goto err;
684afba937eSOliver Neukum 
68529cc8897SKuninori Morimoto 	desc->wMaxPacketSize = usb_endpoint_maxp(ep);
686fa4144b7SAl Viro 	desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
687afba937eSOliver Neukum 
688afba937eSOliver Neukum 	desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
689afba937eSOliver Neukum 	if (!desc->orq)
690afba937eSOliver Neukum 		goto err;
691afba937eSOliver Neukum 	desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
692afba937eSOliver Neukum 	if (!desc->irq)
693afba937eSOliver Neukum 		goto err;
694afba937eSOliver Neukum 
695afba937eSOliver Neukum 	desc->validity = usb_alloc_urb(0, GFP_KERNEL);
696afba937eSOliver Neukum 	if (!desc->validity)
697afba937eSOliver Neukum 		goto err;
698afba937eSOliver Neukum 
699afba937eSOliver Neukum 	desc->response = usb_alloc_urb(0, GFP_KERNEL);
700afba937eSOliver Neukum 	if (!desc->response)
701afba937eSOliver Neukum 		goto err;
702afba937eSOliver Neukum 
703afba937eSOliver Neukum 	desc->command = usb_alloc_urb(0, GFP_KERNEL);
704afba937eSOliver Neukum 	if (!desc->command)
705afba937eSOliver Neukum 		goto err;
706afba937eSOliver Neukum 
707afba937eSOliver Neukum 	desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
708afba937eSOliver Neukum 	if (!desc->ubuf)
709afba937eSOliver Neukum 		goto err;
710afba937eSOliver Neukum 
711997ea58eSDaniel Mack 	desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf),
712afba937eSOliver Neukum 					desc->wMaxPacketSize,
713afba937eSOliver Neukum 					GFP_KERNEL,
714afba937eSOliver Neukum 					&desc->validity->transfer_dma);
715afba937eSOliver Neukum 	if (!desc->sbuf)
716afba937eSOliver Neukum 		goto err;
717afba937eSOliver Neukum 
718997ea58eSDaniel Mack 	desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf),
719afba937eSOliver Neukum 					 desc->bMaxPacketSize0,
720afba937eSOliver Neukum 					 GFP_KERNEL,
721afba937eSOliver Neukum 					 &desc->response->transfer_dma);
722afba937eSOliver Neukum 	if (!desc->inbuf)
723afba937eSOliver Neukum 		goto err2;
724afba937eSOliver Neukum 
725afba937eSOliver Neukum 	usb_fill_int_urb(
726afba937eSOliver Neukum 		desc->validity,
727afba937eSOliver Neukum 		interface_to_usbdev(intf),
728afba937eSOliver Neukum 		usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
729afba937eSOliver Neukum 		desc->sbuf,
730afba937eSOliver Neukum 		desc->wMaxPacketSize,
731afba937eSOliver Neukum 		wdm_int_callback,
732afba937eSOliver Neukum 		desc,
733afba937eSOliver Neukum 		ep->bInterval
734afba937eSOliver Neukum 	);
735afba937eSOliver Neukum 	desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
736afba937eSOliver Neukum 
737afba937eSOliver Neukum 	usb_set_intfdata(intf, desc);
738afba937eSOliver Neukum 	rv = usb_register_dev(intf, &wdm_class);
739052fbc0dSOliver Neukum 	if (rv < 0)
740052fbc0dSOliver Neukum 		goto err3;
741052fbc0dSOliver Neukum 	else
742afba937eSOliver Neukum 		dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
743afba937eSOliver Neukum 			intf->minor - WDM_MINOR_BASE);
744afba937eSOliver Neukum out:
745afba937eSOliver Neukum 	return rv;
746052fbc0dSOliver Neukum err3:
747052fbc0dSOliver Neukum 	usb_set_intfdata(intf, NULL);
748997ea58eSDaniel Mack 	usb_free_coherent(interface_to_usbdev(desc->intf),
749052fbc0dSOliver Neukum 			  desc->bMaxPacketSize0,
750052fbc0dSOliver Neukum 			desc->inbuf,
751052fbc0dSOliver Neukum 			desc->response->transfer_dma);
752afba937eSOliver Neukum err2:
753997ea58eSDaniel Mack 	usb_free_coherent(interface_to_usbdev(desc->intf),
754afba937eSOliver Neukum 			  desc->wMaxPacketSize,
755afba937eSOliver Neukum 			  desc->sbuf,
756afba937eSOliver Neukum 			  desc->validity->transfer_dma);
757afba937eSOliver Neukum err:
758afba937eSOliver Neukum 	free_urbs(desc);
759afba937eSOliver Neukum 	kfree(desc->ubuf);
760afba937eSOliver Neukum 	kfree(desc->orq);
761afba937eSOliver Neukum 	kfree(desc->irq);
762afba937eSOliver Neukum 	kfree(desc);
763afba937eSOliver Neukum 	return rv;
764afba937eSOliver Neukum }
765afba937eSOliver Neukum 
766afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf)
767afba937eSOliver Neukum {
768afba937eSOliver Neukum 	struct wdm_device *desc;
769afba937eSOliver Neukum 	unsigned long flags;
770afba937eSOliver Neukum 
771afba937eSOliver Neukum 	usb_deregister_dev(intf, &wdm_class);
772afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
773afba937eSOliver Neukum 	desc = usb_get_intfdata(intf);
774afba937eSOliver Neukum 
775afba937eSOliver Neukum 	/* the spinlock makes sure no new urbs are generated in the callbacks */
776afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
777afba937eSOliver Neukum 	set_bit(WDM_DISCONNECTING, &desc->flags);
778afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
77917d80d56SOliver Neukum 	/* to terminate pending flushes */
780afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
781afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
782860e41a7SOliver Neukum 	mutex_lock(&desc->lock);
783afba937eSOliver Neukum 	kill_urbs(desc);
784d93d16e9SOliver Neukum 	cancel_work_sync(&desc->rxwork);
785860e41a7SOliver Neukum 	mutex_unlock(&desc->lock);
786afba937eSOliver Neukum 	wake_up_all(&desc->wait);
787afba937eSOliver Neukum 	if (!desc->count)
788afba937eSOliver Neukum 		cleanup(desc);
789afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
790afba937eSOliver Neukum }
791afba937eSOliver Neukum 
792d93d16e9SOliver Neukum #ifdef CONFIG_PM
79317d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
79417d80d56SOliver Neukum {
79517d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
79617d80d56SOliver Neukum 	int rv = 0;
79717d80d56SOliver Neukum 
79817d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
79917d80d56SOliver Neukum 
800d93d16e9SOliver Neukum 	/* if this is an autosuspend the caller does the locking */
8015b1b0b81SAlan Stern 	if (!PMSG_IS_AUTO(message))
802860e41a7SOliver Neukum 		mutex_lock(&desc->lock);
80362e66854SOliver Neukum 	spin_lock_irq(&desc->iuspin);
804d93d16e9SOliver Neukum 
8055b1b0b81SAlan Stern 	if (PMSG_IS_AUTO(message) &&
806922a5eadSOliver Neukum 			(test_bit(WDM_IN_USE, &desc->flags)
807922a5eadSOliver Neukum 			|| test_bit(WDM_RESPONDING, &desc->flags))) {
80862e66854SOliver Neukum 		spin_unlock_irq(&desc->iuspin);
80917d80d56SOliver Neukum 		rv = -EBUSY;
81017d80d56SOliver Neukum 	} else {
811d93d16e9SOliver Neukum 
812beb1d35fSOliver Neukum 		set_bit(WDM_SUSPENDING, &desc->flags);
81362e66854SOliver Neukum 		spin_unlock_irq(&desc->iuspin);
814d93d16e9SOliver Neukum 		/* callback submits work - order is essential */
81517d80d56SOliver Neukum 		kill_urbs(desc);
816d93d16e9SOliver Neukum 		cancel_work_sync(&desc->rxwork);
81717d80d56SOliver Neukum 	}
8185b1b0b81SAlan Stern 	if (!PMSG_IS_AUTO(message))
819860e41a7SOliver Neukum 		mutex_unlock(&desc->lock);
82017d80d56SOliver Neukum 
82117d80d56SOliver Neukum 	return rv;
82217d80d56SOliver Neukum }
823d93d16e9SOliver Neukum #endif
82417d80d56SOliver Neukum 
82517d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc)
82617d80d56SOliver Neukum {
82717d80d56SOliver Neukum 	int rv = 0;
82817d80d56SOliver Neukum 
82917d80d56SOliver Neukum 	if (desc->count) {
83017d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_NOIO);
83117d80d56SOliver Neukum 		if (rv < 0)
8329908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
8339908a32eSGreg Kroah-Hartman 				"Error resume submitting int urb - %d\n", rv);
83417d80d56SOliver Neukum 	}
83517d80d56SOliver Neukum 	return rv;
83617d80d56SOliver Neukum }
837d93d16e9SOliver Neukum 
838d93d16e9SOliver Neukum #ifdef CONFIG_PM
83917d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf)
84017d80d56SOliver Neukum {
84117d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
84217d80d56SOliver Neukum 	int rv;
84317d80d56SOliver Neukum 
84417d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
845338124c1SOliver Neukum 
846beb1d35fSOliver Neukum 	clear_bit(WDM_SUSPENDING, &desc->flags);
84762e66854SOliver Neukum 	rv = recover_from_urb_loss(desc);
848338124c1SOliver Neukum 
84917d80d56SOliver Neukum 	return rv;
85017d80d56SOliver Neukum }
851d93d16e9SOliver Neukum #endif
85217d80d56SOliver Neukum 
85317d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf)
85417d80d56SOliver Neukum {
85517d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
85617d80d56SOliver Neukum 
857860e41a7SOliver Neukum 	mutex_lock(&desc->lock);
858d771d8aaSOliver Neukum 	kill_urbs(desc);
859d771d8aaSOliver Neukum 
860d771d8aaSOliver Neukum 	/*
861d771d8aaSOliver Neukum 	 * we notify everybody using poll of
862d771d8aaSOliver Neukum 	 * an exceptional situation
863d771d8aaSOliver Neukum 	 * must be done before recovery lest a spontaneous
864d771d8aaSOliver Neukum 	 * message from the device is lost
865d771d8aaSOliver Neukum 	 */
866d771d8aaSOliver Neukum 	spin_lock_irq(&desc->iuspin);
867d771d8aaSOliver Neukum 	desc->rerr = -EINTR;
868d771d8aaSOliver Neukum 	spin_unlock_irq(&desc->iuspin);
869d771d8aaSOliver Neukum 	wake_up_all(&desc->wait);
87017d80d56SOliver Neukum 	return 0;
87117d80d56SOliver Neukum }
87217d80d56SOliver Neukum 
87317d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf)
87417d80d56SOliver Neukum {
87517d80d56SOliver Neukum 	struct wdm_device *desc = usb_get_intfdata(intf);
87617d80d56SOliver Neukum 	int rv;
87717d80d56SOliver Neukum 
87817d80d56SOliver Neukum 	rv = recover_from_urb_loss(desc);
879860e41a7SOliver Neukum 	mutex_unlock(&desc->lock);
88017d80d56SOliver Neukum 	return 0;
88117d80d56SOliver Neukum }
88217d80d56SOliver Neukum 
883afba937eSOliver Neukum static struct usb_driver wdm_driver = {
884afba937eSOliver Neukum 	.name =		"cdc_wdm",
885afba937eSOliver Neukum 	.probe =	wdm_probe,
886afba937eSOliver Neukum 	.disconnect =	wdm_disconnect,
887d93d16e9SOliver Neukum #ifdef CONFIG_PM
88817d80d56SOliver Neukum 	.suspend =	wdm_suspend,
88917d80d56SOliver Neukum 	.resume =	wdm_resume,
89017d80d56SOliver Neukum 	.reset_resume =	wdm_resume,
891d93d16e9SOliver Neukum #endif
89217d80d56SOliver Neukum 	.pre_reset =	wdm_pre_reset,
89317d80d56SOliver Neukum 	.post_reset =	wdm_post_reset,
894afba937eSOliver Neukum 	.id_table =	wdm_ids,
89517d80d56SOliver Neukum 	.supports_autosuspend = 1,
896afba937eSOliver Neukum };
897afba937eSOliver Neukum 
89865db4305SGreg Kroah-Hartman module_usb_driver(wdm_driver);
899afba937eSOliver Neukum 
900afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR);
90187d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC);
902afba937eSOliver Neukum MODULE_LICENSE("GPL");
903