xref: /openbmc/linux/drivers/usb/class/cdc-wdm.c (revision 24a85bae)
1afba937eSOliver Neukum /*
2afba937eSOliver Neukum  * cdc-wdm.c
3afba937eSOliver Neukum  *
4afba937eSOliver Neukum  * This driver supports USB CDC WCM Device Management.
5afba937eSOliver Neukum  *
6052fbc0dSOliver Neukum  * Copyright (c) 2007-2009 Oliver Neukum
7afba937eSOliver Neukum  *
8afba937eSOliver Neukum  * Some code taken from cdc-acm.c
9afba937eSOliver Neukum  *
10afba937eSOliver Neukum  * Released under the GPLv2.
11afba937eSOliver Neukum  *
12afba937eSOliver Neukum  * Many thanks to Carl Nordbeck
13afba937eSOliver Neukum  */
14afba937eSOliver Neukum #include <linux/kernel.h>
15afba937eSOliver Neukum #include <linux/errno.h>
16afba937eSOliver Neukum #include <linux/slab.h>
17afba937eSOliver Neukum #include <linux/module.h>
18afba937eSOliver Neukum #include <linux/mutex.h>
19afba937eSOliver Neukum #include <linux/uaccess.h>
20afba937eSOliver Neukum #include <linux/bitops.h>
21afba937eSOliver Neukum #include <linux/poll.h>
22afba937eSOliver Neukum #include <linux/usb.h>
23afba937eSOliver Neukum #include <linux/usb/cdc.h>
24afba937eSOliver Neukum #include <asm/byteorder.h>
25afba937eSOliver Neukum #include <asm/unaligned.h>
263cc36157SBjørn Mork #include <linux/usb/cdc-wdm.h>
27afba937eSOliver Neukum 
28afba937eSOliver Neukum /*
29afba937eSOliver Neukum  * Version Information
30afba937eSOliver Neukum  */
3187d65e54SOliver Neukum #define DRIVER_VERSION "v0.03"
32afba937eSOliver Neukum #define DRIVER_AUTHOR "Oliver Neukum"
3387d65e54SOliver Neukum #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
34afba937eSOliver Neukum 
35fec67b45SBjørn Mork #define HUAWEI_VENDOR_ID	0x12D1
36fec67b45SBjørn Mork 
376ef4852bSNémeth Márton static const struct usb_device_id wdm_ids[] = {
38afba937eSOliver Neukum 	{
39afba937eSOliver Neukum 		.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
40afba937eSOliver Neukum 				 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
41afba937eSOliver Neukum 		.bInterfaceClass = USB_CLASS_COMM,
42afba937eSOliver Neukum 		.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
43afba937eSOliver Neukum 	},
44fec67b45SBjørn Mork 	{
45fec67b45SBjørn Mork 		/*
46fec67b45SBjørn Mork 		 * Huawei E392, E398 and possibly other Qualcomm based modems
47fec67b45SBjørn Mork 		 * embed the Qualcomm QMI protocol inside CDC on CDC ECM like
48fec67b45SBjørn Mork 		 * control interfaces.  Userspace access to this is required
49fec67b45SBjørn Mork 		 * to configure the accompanying data interface
50fec67b45SBjørn Mork 		 */
51fec67b45SBjørn Mork 		.match_flags        = USB_DEVICE_ID_MATCH_VENDOR |
52fec67b45SBjørn Mork 					USB_DEVICE_ID_MATCH_INT_INFO,
53fec67b45SBjørn Mork 		.idVendor           = HUAWEI_VENDOR_ID,
54fec67b45SBjørn Mork 		.bInterfaceClass    = USB_CLASS_VENDOR_SPEC,
55fec67b45SBjørn Mork 		.bInterfaceSubClass = 1,
56fec67b45SBjørn Mork 		.bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
57fec67b45SBjørn Mork 	},
58afba937eSOliver Neukum 	{ }
59afba937eSOliver Neukum };
60afba937eSOliver Neukum 
61aa5380b9SOliver Neukum MODULE_DEVICE_TABLE (usb, wdm_ids);
62aa5380b9SOliver Neukum 
63afba937eSOliver Neukum #define WDM_MINOR_BASE	176
64afba937eSOliver Neukum 
65afba937eSOliver Neukum 
66afba937eSOliver Neukum #define WDM_IN_USE		1
67afba937eSOliver Neukum #define WDM_DISCONNECTING	2
68afba937eSOliver Neukum #define WDM_RESULT		3
69afba937eSOliver Neukum #define WDM_READ		4
70afba937eSOliver Neukum #define WDM_INT_STALL		5
71afba937eSOliver Neukum #define WDM_POLL_RUNNING	6
72922a5eadSOliver Neukum #define WDM_RESPONDING		7
73beb1d35fSOliver Neukum #define WDM_SUSPENDING		8
7488044202SBjørn Mork #define WDM_RESETTING		9
75afba937eSOliver Neukum 
76afba937eSOliver Neukum #define WDM_MAX			16
77afba937eSOliver Neukum 
787e3054a0SBjørn Mork /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */
797e3054a0SBjørn Mork #define WDM_DEFAULT_BUFSIZE	256
80afba937eSOliver Neukum 
81afba937eSOliver Neukum static DEFINE_MUTEX(wdm_mutex);
82b0c13860SBjørn Mork static DEFINE_SPINLOCK(wdm_device_list_lock);
83b0c13860SBjørn Mork static LIST_HEAD(wdm_device_list);
84afba937eSOliver Neukum 
85afba937eSOliver Neukum /* --- method tables --- */
86afba937eSOliver Neukum 
87afba937eSOliver Neukum struct wdm_device {
88afba937eSOliver Neukum 	u8			*inbuf; /* buffer for response */
89afba937eSOliver Neukum 	u8			*outbuf; /* buffer for command */
90afba937eSOliver Neukum 	u8			*sbuf; /* buffer for status */
91afba937eSOliver Neukum 	u8			*ubuf; /* buffer for copy to user space */
92afba937eSOliver Neukum 
93afba937eSOliver Neukum 	struct urb		*command;
94afba937eSOliver Neukum 	struct urb		*response;
95afba937eSOliver Neukum 	struct urb		*validity;
96afba937eSOliver Neukum 	struct usb_interface	*intf;
97afba937eSOliver Neukum 	struct usb_ctrlrequest	*orq;
98afba937eSOliver Neukum 	struct usb_ctrlrequest	*irq;
99afba937eSOliver Neukum 	spinlock_t		iuspin;
100afba937eSOliver Neukum 
101afba937eSOliver Neukum 	unsigned long		flags;
102afba937eSOliver Neukum 	u16			bufsize;
103afba937eSOliver Neukum 	u16			wMaxCommand;
104afba937eSOliver Neukum 	u16			wMaxPacketSize;
105afba937eSOliver Neukum 	__le16			inum;
106afba937eSOliver Neukum 	int			reslength;
107afba937eSOliver Neukum 	int			length;
108afba937eSOliver Neukum 	int			read;
109afba937eSOliver Neukum 	int			count;
110afba937eSOliver Neukum 	dma_addr_t		shandle;
111afba937eSOliver Neukum 	dma_addr_t		ihandle;
112e8537bd2SBjørn Mork 	struct mutex		wlock;
113e8537bd2SBjørn Mork 	struct mutex		rlock;
114afba937eSOliver Neukum 	wait_queue_head_t	wait;
115afba937eSOliver Neukum 	struct work_struct	rxwork;
116afba937eSOliver Neukum 	int			werr;
117afba937eSOliver Neukum 	int			rerr;
118b0c13860SBjørn Mork 
119b0c13860SBjørn Mork 	struct list_head	device_list;
1203cc36157SBjørn Mork 	int			(*manage_power)(struct usb_interface *, int);
121afba937eSOliver Neukum };
122afba937eSOliver Neukum 
123afba937eSOliver Neukum static struct usb_driver wdm_driver;
124afba937eSOliver Neukum 
125b0c13860SBjørn Mork /* return intfdata if we own the interface, else look up intf in the list */
126b0c13860SBjørn Mork static struct wdm_device *wdm_find_device(struct usb_interface *intf)
127b0c13860SBjørn Mork {
128b0c13860SBjørn Mork 	struct wdm_device *desc = NULL;
129b0c13860SBjørn Mork 
130b0c13860SBjørn Mork 	spin_lock(&wdm_device_list_lock);
131b0c13860SBjørn Mork 	list_for_each_entry(desc, &wdm_device_list, device_list)
132b0c13860SBjørn Mork 		if (desc->intf == intf)
133b0c13860SBjørn Mork 			break;
134b0c13860SBjørn Mork 	spin_unlock(&wdm_device_list_lock);
135b0c13860SBjørn Mork 
136b0c13860SBjørn Mork 	return desc;
137b0c13860SBjørn Mork }
138b0c13860SBjørn Mork 
139b0c13860SBjørn Mork static struct wdm_device *wdm_find_device_by_minor(int minor)
140b0c13860SBjørn Mork {
141b0c13860SBjørn Mork 	struct wdm_device *desc = NULL;
142b0c13860SBjørn Mork 
143b0c13860SBjørn Mork 	spin_lock(&wdm_device_list_lock);
144b0c13860SBjørn Mork 	list_for_each_entry(desc, &wdm_device_list, device_list)
145b0c13860SBjørn Mork 		if (desc->intf->minor == minor)
146b0c13860SBjørn Mork 			break;
147b0c13860SBjørn Mork 	spin_unlock(&wdm_device_list_lock);
148b0c13860SBjørn Mork 
149b0c13860SBjørn Mork 	return desc;
150b0c13860SBjørn Mork }
151b0c13860SBjørn Mork 
152afba937eSOliver Neukum /* --- callbacks --- */
153afba937eSOliver Neukum static void wdm_out_callback(struct urb *urb)
154afba937eSOliver Neukum {
155afba937eSOliver Neukum 	struct wdm_device *desc;
156afba937eSOliver Neukum 	desc = urb->context;
157afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
158afba937eSOliver Neukum 	desc->werr = urb->status;
159afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
160afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
161afba937eSOliver Neukum 	kfree(desc->outbuf);
162afba937eSOliver Neukum 	wake_up(&desc->wait);
163afba937eSOliver Neukum }
164afba937eSOliver Neukum 
165afba937eSOliver Neukum static void wdm_in_callback(struct urb *urb)
166afba937eSOliver Neukum {
167afba937eSOliver Neukum 	struct wdm_device *desc = urb->context;
168afba937eSOliver Neukum 	int status = urb->status;
169afba937eSOliver Neukum 
170afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
171922a5eadSOliver Neukum 	clear_bit(WDM_RESPONDING, &desc->flags);
172afba937eSOliver Neukum 
173afba937eSOliver Neukum 	if (status) {
174afba937eSOliver Neukum 		switch (status) {
175afba937eSOliver Neukum 		case -ENOENT:
176afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
177afba937eSOliver Neukum 				"nonzero urb status received: -ENOENT");
178922a5eadSOliver Neukum 			goto skip_error;
179afba937eSOliver Neukum 		case -ECONNRESET:
180afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
181afba937eSOliver Neukum 				"nonzero urb status received: -ECONNRESET");
182922a5eadSOliver Neukum 			goto skip_error;
183afba937eSOliver Neukum 		case -ESHUTDOWN:
184afba937eSOliver Neukum 			dev_dbg(&desc->intf->dev,
185afba937eSOliver Neukum 				"nonzero urb status received: -ESHUTDOWN");
186922a5eadSOliver Neukum 			goto skip_error;
187afba937eSOliver Neukum 		case -EPIPE:
1889908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1899908a32eSGreg Kroah-Hartman 				"nonzero urb status received: -EPIPE\n");
190afba937eSOliver Neukum 			break;
191afba937eSOliver Neukum 		default:
1929908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
1939908a32eSGreg Kroah-Hartman 				"Unexpected error %d\n", status);
194afba937eSOliver Neukum 			break;
195afba937eSOliver Neukum 		}
196afba937eSOliver Neukum 	}
197afba937eSOliver Neukum 
198afba937eSOliver Neukum 	desc->rerr = status;
199afba937eSOliver Neukum 	desc->reslength = urb->actual_length;
200afba937eSOliver Neukum 	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
201afba937eSOliver Neukum 	desc->length += desc->reslength;
202922a5eadSOliver Neukum skip_error:
203afba937eSOliver Neukum 	wake_up(&desc->wait);
204afba937eSOliver Neukum 
205afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
206afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
207afba937eSOliver Neukum }
208afba937eSOliver Neukum 
209afba937eSOliver Neukum static void wdm_int_callback(struct urb *urb)
210afba937eSOliver Neukum {
211afba937eSOliver Neukum 	int rv = 0;
212afba937eSOliver Neukum 	int status = urb->status;
213afba937eSOliver Neukum 	struct wdm_device *desc;
214afba937eSOliver Neukum 	struct usb_cdc_notification *dr;
215afba937eSOliver Neukum 
216afba937eSOliver Neukum 	desc = urb->context;
217afba937eSOliver Neukum 	dr = (struct usb_cdc_notification *)desc->sbuf;
218afba937eSOliver Neukum 
219afba937eSOliver Neukum 	if (status) {
220afba937eSOliver Neukum 		switch (status) {
221afba937eSOliver Neukum 		case -ESHUTDOWN:
222afba937eSOliver Neukum 		case -ENOENT:
223afba937eSOliver Neukum 		case -ECONNRESET:
224afba937eSOliver Neukum 			return; /* unplug */
225afba937eSOliver Neukum 		case -EPIPE:
226afba937eSOliver Neukum 			set_bit(WDM_INT_STALL, &desc->flags);
2279908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev, "Stall on int endpoint\n");
228afba937eSOliver Neukum 			goto sw; /* halt is cleared in work */
229afba937eSOliver Neukum 		default:
2309908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
2319908a32eSGreg Kroah-Hartman 				"nonzero urb status received: %d\n", status);
232afba937eSOliver Neukum 			break;
233afba937eSOliver Neukum 		}
234afba937eSOliver Neukum 	}
235afba937eSOliver Neukum 
236afba937eSOliver Neukum 	if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
2379908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
2389908a32eSGreg Kroah-Hartman 			urb->actual_length);
239afba937eSOliver Neukum 		goto exit;
240afba937eSOliver Neukum 	}
241afba937eSOliver Neukum 
242afba937eSOliver Neukum 	switch (dr->bNotificationType) {
243afba937eSOliver Neukum 	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
244afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev,
245afba937eSOliver Neukum 			"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
246afba937eSOliver Neukum 			dr->wIndex, dr->wLength);
247afba937eSOliver Neukum 		break;
248afba937eSOliver Neukum 
249afba937eSOliver Neukum 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
250afba937eSOliver Neukum 
251afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev,
252afba937eSOliver Neukum 			"NOTIFY_NETWORK_CONNECTION %s network",
253afba937eSOliver Neukum 			dr->wValue ? "connected to" : "disconnected from");
254afba937eSOliver Neukum 		goto exit;
255afba937eSOliver Neukum 	default:
256afba937eSOliver Neukum 		clear_bit(WDM_POLL_RUNNING, &desc->flags);
2579908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev,
2589908a32eSGreg Kroah-Hartman 			"unknown notification %d received: index %d len %d\n",
259afba937eSOliver Neukum 			dr->bNotificationType, dr->wIndex, dr->wLength);
260afba937eSOliver Neukum 		goto exit;
261afba937eSOliver Neukum 	}
262afba937eSOliver Neukum 
263afba937eSOliver Neukum 	spin_lock(&desc->iuspin);
264afba937eSOliver Neukum 	clear_bit(WDM_READ, &desc->flags);
265922a5eadSOliver Neukum 	set_bit(WDM_RESPONDING, &desc->flags);
266beb1d35fSOliver Neukum 	if (!test_bit(WDM_DISCONNECTING, &desc->flags)
267beb1d35fSOliver Neukum 		&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
268afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_ATOMIC);
269afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
270afba937eSOliver Neukum 			__func__, rv);
271afba937eSOliver Neukum 	}
272afba937eSOliver Neukum 	spin_unlock(&desc->iuspin);
273afba937eSOliver Neukum 	if (rv < 0) {
274922a5eadSOliver Neukum 		clear_bit(WDM_RESPONDING, &desc->flags);
275afba937eSOliver Neukum 		if (rv == -EPERM)
276afba937eSOliver Neukum 			return;
277afba937eSOliver Neukum 		if (rv == -ENOMEM) {
278afba937eSOliver Neukum sw:
279afba937eSOliver Neukum 			rv = schedule_work(&desc->rxwork);
280afba937eSOliver Neukum 			if (rv)
2819908a32eSGreg Kroah-Hartman 				dev_err(&desc->intf->dev,
2829908a32eSGreg Kroah-Hartman 					"Cannot schedule work\n");
283afba937eSOliver Neukum 		}
284afba937eSOliver Neukum 	}
285afba937eSOliver Neukum exit:
286afba937eSOliver Neukum 	rv = usb_submit_urb(urb, GFP_ATOMIC);
287afba937eSOliver Neukum 	if (rv)
2889908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev,
2899908a32eSGreg Kroah-Hartman 			"%s - usb_submit_urb failed with result %d\n",
290afba937eSOliver Neukum 			__func__, rv);
291afba937eSOliver Neukum 
292afba937eSOliver Neukum }
293afba937eSOliver Neukum 
294afba937eSOliver Neukum static void kill_urbs(struct wdm_device *desc)
295afba937eSOliver Neukum {
29617d80d56SOliver Neukum 	/* the order here is essential */
297afba937eSOliver Neukum 	usb_kill_urb(desc->command);
298afba937eSOliver Neukum 	usb_kill_urb(desc->validity);
299afba937eSOliver Neukum 	usb_kill_urb(desc->response);
300afba937eSOliver Neukum }
301afba937eSOliver Neukum 
302afba937eSOliver Neukum static void free_urbs(struct wdm_device *desc)
303afba937eSOliver Neukum {
304afba937eSOliver Neukum 	usb_free_urb(desc->validity);
305afba937eSOliver Neukum 	usb_free_urb(desc->response);
306afba937eSOliver Neukum 	usb_free_urb(desc->command);
307afba937eSOliver Neukum }
308afba937eSOliver Neukum 
309afba937eSOliver Neukum static void cleanup(struct wdm_device *desc)
310afba937eSOliver Neukum {
311b0c13860SBjørn Mork 	spin_lock(&wdm_device_list_lock);
312b0c13860SBjørn Mork 	list_del(&desc->device_list);
313b0c13860SBjørn Mork 	spin_unlock(&wdm_device_list_lock);
3148457d99cSBjørn Mork 	kfree(desc->sbuf);
3158457d99cSBjørn Mork 	kfree(desc->inbuf);
316afba937eSOliver Neukum 	kfree(desc->orq);
317afba937eSOliver Neukum 	kfree(desc->irq);
318afba937eSOliver Neukum 	kfree(desc->ubuf);
319afba937eSOliver Neukum 	free_urbs(desc);
320afba937eSOliver Neukum 	kfree(desc);
321afba937eSOliver Neukum }
322afba937eSOliver Neukum 
323afba937eSOliver Neukum static ssize_t wdm_write
324afba937eSOliver Neukum (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
325afba937eSOliver Neukum {
326afba937eSOliver Neukum 	u8 *buf;
327afba937eSOliver Neukum 	int rv = -EMSGSIZE, r, we;
328afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
329afba937eSOliver Neukum 	struct usb_ctrlrequest *req;
330afba937eSOliver Neukum 
331afba937eSOliver Neukum 	if (count > desc->wMaxCommand)
332afba937eSOliver Neukum 		count = desc->wMaxCommand;
333afba937eSOliver Neukum 
334afba937eSOliver Neukum 	spin_lock_irq(&desc->iuspin);
335afba937eSOliver Neukum 	we = desc->werr;
336afba937eSOliver Neukum 	desc->werr = 0;
337afba937eSOliver Neukum 	spin_unlock_irq(&desc->iuspin);
338afba937eSOliver Neukum 	if (we < 0)
339afba937eSOliver Neukum 		return -EIO;
340afba937eSOliver Neukum 
341860e41a7SOliver Neukum 	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
342860e41a7SOliver Neukum 	if (!buf) {
343860e41a7SOliver Neukum 		rv = -ENOMEM;
344afba937eSOliver Neukum 		goto outnl;
345860e41a7SOliver Neukum 	}
346860e41a7SOliver Neukum 
347860e41a7SOliver Neukum 	r = copy_from_user(buf, buffer, count);
348860e41a7SOliver Neukum 	if (r > 0) {
349860e41a7SOliver Neukum 		kfree(buf);
350860e41a7SOliver Neukum 		rv = -EFAULT;
351860e41a7SOliver Neukum 		goto outnl;
352860e41a7SOliver Neukum 	}
353860e41a7SOliver Neukum 
354860e41a7SOliver Neukum 	/* concurrent writes and disconnect */
355e8537bd2SBjørn Mork 	r = mutex_lock_interruptible(&desc->wlock);
356860e41a7SOliver Neukum 	rv = -ERESTARTSYS;
357860e41a7SOliver Neukum 	if (r) {
358860e41a7SOliver Neukum 		kfree(buf);
359860e41a7SOliver Neukum 		goto outnl;
360860e41a7SOliver Neukum 	}
361860e41a7SOliver Neukum 
362860e41a7SOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
363860e41a7SOliver Neukum 		kfree(buf);
364860e41a7SOliver Neukum 		rv = -ENODEV;
365860e41a7SOliver Neukum 		goto outnp;
366860e41a7SOliver Neukum 	}
367afba937eSOliver Neukum 
36817d80d56SOliver Neukum 	r = usb_autopm_get_interface(desc->intf);
369860e41a7SOliver Neukum 	if (r < 0) {
370860e41a7SOliver Neukum 		kfree(buf);
37117d80d56SOliver Neukum 		goto outnp;
372860e41a7SOliver Neukum 	}
3737f1dc313SOliver Neukum 
3740cdfb819SDavid Sterba 	if (!(file->f_flags & O_NONBLOCK))
375afba937eSOliver Neukum 		r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
376afba937eSOliver Neukum 								&desc->flags));
3777f1dc313SOliver Neukum 	else
3787f1dc313SOliver Neukum 		if (test_bit(WDM_IN_USE, &desc->flags))
3797f1dc313SOliver Neukum 			r = -EAGAIN;
38088044202SBjørn Mork 
38188044202SBjørn Mork 	if (test_bit(WDM_RESETTING, &desc->flags))
38288044202SBjørn Mork 		r = -EIO;
38388044202SBjørn Mork 
384860e41a7SOliver Neukum 	if (r < 0) {
385afba937eSOliver Neukum 		kfree(buf);
386afba937eSOliver Neukum 		goto out;
387afba937eSOliver Neukum 	}
388afba937eSOliver Neukum 
389afba937eSOliver Neukum 	req = desc->orq;
390afba937eSOliver Neukum 	usb_fill_control_urb(
391afba937eSOliver Neukum 		desc->command,
392afba937eSOliver Neukum 		interface_to_usbdev(desc->intf),
393afba937eSOliver Neukum 		/* using common endpoint 0 */
394afba937eSOliver Neukum 		usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
395afba937eSOliver Neukum 		(unsigned char *)req,
396afba937eSOliver Neukum 		buf,
397afba937eSOliver Neukum 		count,
398afba937eSOliver Neukum 		wdm_out_callback,
399afba937eSOliver Neukum 		desc
400afba937eSOliver Neukum 	);
401afba937eSOliver Neukum 
402afba937eSOliver Neukum 	req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
403afba937eSOliver Neukum 			     USB_RECIP_INTERFACE);
404afba937eSOliver Neukum 	req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
405afba937eSOliver Neukum 	req->wValue = 0;
406afba937eSOliver Neukum 	req->wIndex = desc->inum;
407afba937eSOliver Neukum 	req->wLength = cpu_to_le16(count);
408afba937eSOliver Neukum 	set_bit(WDM_IN_USE, &desc->flags);
409afba937eSOliver Neukum 
410afba937eSOliver Neukum 	rv = usb_submit_urb(desc->command, GFP_KERNEL);
411afba937eSOliver Neukum 	if (rv < 0) {
412afba937eSOliver Neukum 		kfree(buf);
413afba937eSOliver Neukum 		clear_bit(WDM_IN_USE, &desc->flags);
4149908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
415afba937eSOliver Neukum 	} else {
416afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
417afba937eSOliver Neukum 			req->wIndex);
418afba937eSOliver Neukum 	}
419afba937eSOliver Neukum out:
42017d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
42117d80d56SOliver Neukum outnp:
422e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
423afba937eSOliver Neukum outnl:
424afba937eSOliver Neukum 	return rv < 0 ? rv : count;
425afba937eSOliver Neukum }
426afba937eSOliver Neukum 
427afba937eSOliver Neukum static ssize_t wdm_read
428afba937eSOliver Neukum (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
429afba937eSOliver Neukum {
430711c68b3SBen Hutchings 	int rv, cntr;
431afba937eSOliver Neukum 	int i = 0;
432afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
433afba937eSOliver Neukum 
434afba937eSOliver Neukum 
435e8537bd2SBjørn Mork 	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
436afba937eSOliver Neukum 	if (rv < 0)
437afba937eSOliver Neukum 		return -ERESTARTSYS;
438afba937eSOliver Neukum 
439711c68b3SBen Hutchings 	cntr = ACCESS_ONCE(desc->length);
440711c68b3SBen Hutchings 	if (cntr == 0) {
441afba937eSOliver Neukum 		desc->read = 0;
442afba937eSOliver Neukum retry:
4437f1dc313SOliver Neukum 		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
4447f1dc313SOliver Neukum 			rv = -ENODEV;
4457f1dc313SOliver Neukum 			goto err;
4467f1dc313SOliver Neukum 		}
447afba937eSOliver Neukum 		i++;
4487f1dc313SOliver Neukum 		if (file->f_flags & O_NONBLOCK) {
4497f1dc313SOliver Neukum 			if (!test_bit(WDM_READ, &desc->flags)) {
4507f1dc313SOliver Neukum 				rv = cntr ? cntr : -EAGAIN;
4517f1dc313SOliver Neukum 				goto err;
4527f1dc313SOliver Neukum 			}
4537f1dc313SOliver Neukum 			rv = 0;
4547f1dc313SOliver Neukum 		} else {
455afba937eSOliver Neukum 			rv = wait_event_interruptible(desc->wait,
456afba937eSOliver Neukum 				test_bit(WDM_READ, &desc->flags));
4577f1dc313SOliver Neukum 		}
458afba937eSOliver Neukum 
4597f1dc313SOliver Neukum 		/* may have happened while we slept */
46017d80d56SOliver Neukum 		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
46117d80d56SOliver Neukum 			rv = -ENODEV;
46217d80d56SOliver Neukum 			goto err;
46317d80d56SOliver Neukum 		}
46488044202SBjørn Mork 		if (test_bit(WDM_RESETTING, &desc->flags)) {
46588044202SBjørn Mork 			rv = -EIO;
46688044202SBjørn Mork 			goto err;
46788044202SBjørn Mork 		}
46817d80d56SOliver Neukum 		usb_mark_last_busy(interface_to_usbdev(desc->intf));
469afba937eSOliver Neukum 		if (rv < 0) {
470afba937eSOliver Neukum 			rv = -ERESTARTSYS;
471afba937eSOliver Neukum 			goto err;
472afba937eSOliver Neukum 		}
473afba937eSOliver Neukum 
474afba937eSOliver Neukum 		spin_lock_irq(&desc->iuspin);
475afba937eSOliver Neukum 
476afba937eSOliver Neukum 		if (desc->rerr) { /* read completed, error happened */
477afba937eSOliver Neukum 			desc->rerr = 0;
478afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
479afba937eSOliver Neukum 			rv = -EIO;
480afba937eSOliver Neukum 			goto err;
481afba937eSOliver Neukum 		}
482afba937eSOliver Neukum 		/*
483afba937eSOliver Neukum 		 * recheck whether we've lost the race
484afba937eSOliver Neukum 		 * against the completion handler
485afba937eSOliver Neukum 		 */
486afba937eSOliver Neukum 		if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
487afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
488afba937eSOliver Neukum 			goto retry;
489afba937eSOliver Neukum 		}
490afba937eSOliver Neukum 		if (!desc->reslength) { /* zero length read */
491afba937eSOliver Neukum 			spin_unlock_irq(&desc->iuspin);
492afba937eSOliver Neukum 			goto retry;
493afba937eSOliver Neukum 		}
494711c68b3SBen Hutchings 		cntr = desc->length;
495afba937eSOliver Neukum 		spin_unlock_irq(&desc->iuspin);
496afba937eSOliver Neukum 	}
497afba937eSOliver Neukum 
498711c68b3SBen Hutchings 	if (cntr > count)
499711c68b3SBen Hutchings 		cntr = count;
500afba937eSOliver Neukum 	rv = copy_to_user(buffer, desc->ubuf, cntr);
501afba937eSOliver Neukum 	if (rv > 0) {
502afba937eSOliver Neukum 		rv = -EFAULT;
503afba937eSOliver Neukum 		goto err;
504afba937eSOliver Neukum 	}
505afba937eSOliver Neukum 
506711c68b3SBen Hutchings 	spin_lock_irq(&desc->iuspin);
507711c68b3SBen Hutchings 
508afba937eSOliver Neukum 	for (i = 0; i < desc->length - cntr; i++)
509afba937eSOliver Neukum 		desc->ubuf[i] = desc->ubuf[i + cntr];
510afba937eSOliver Neukum 
511afba937eSOliver Neukum 	desc->length -= cntr;
51287d65e54SOliver Neukum 	/* in case we had outstanding data */
51387d65e54SOliver Neukum 	if (!desc->length)
51487d65e54SOliver Neukum 		clear_bit(WDM_READ, &desc->flags);
515711c68b3SBen Hutchings 
516711c68b3SBen Hutchings 	spin_unlock_irq(&desc->iuspin);
517711c68b3SBen Hutchings 
518afba937eSOliver Neukum 	rv = cntr;
519afba937eSOliver Neukum 
520afba937eSOliver Neukum err:
521e8537bd2SBjørn Mork 	mutex_unlock(&desc->rlock);
522afba937eSOliver Neukum 	return rv;
523afba937eSOliver Neukum }
524afba937eSOliver Neukum 
525afba937eSOliver Neukum static int wdm_flush(struct file *file, fl_owner_t id)
526afba937eSOliver Neukum {
527afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
528afba937eSOliver Neukum 
529afba937eSOliver Neukum 	wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
530afba937eSOliver Neukum 	if (desc->werr < 0)
5319908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Error in flush path: %d\n",
5329908a32eSGreg Kroah-Hartman 			desc->werr);
533afba937eSOliver Neukum 
53424a85baeSOliver Neukum 	return usb_translate_errors(desc->werr);
535afba937eSOliver Neukum }
536afba937eSOliver Neukum 
537afba937eSOliver Neukum static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
538afba937eSOliver Neukum {
539afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
540afba937eSOliver Neukum 	unsigned long flags;
541afba937eSOliver Neukum 	unsigned int mask = 0;
542afba937eSOliver Neukum 
543afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
544afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
545afba937eSOliver Neukum 		mask = POLLERR;
546afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
547afba937eSOliver Neukum 		goto desc_out;
548afba937eSOliver Neukum 	}
549afba937eSOliver Neukum 	if (test_bit(WDM_READ, &desc->flags))
550afba937eSOliver Neukum 		mask = POLLIN | POLLRDNORM;
551afba937eSOliver Neukum 	if (desc->rerr || desc->werr)
552afba937eSOliver Neukum 		mask |= POLLERR;
553afba937eSOliver Neukum 	if (!test_bit(WDM_IN_USE, &desc->flags))
554afba937eSOliver Neukum 		mask |= POLLOUT | POLLWRNORM;
555afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
556afba937eSOliver Neukum 
557afba937eSOliver Neukum 	poll_wait(file, &desc->wait, wait);
558afba937eSOliver Neukum 
559afba937eSOliver Neukum desc_out:
560afba937eSOliver Neukum 	return mask;
561afba937eSOliver Neukum }
562afba937eSOliver Neukum 
563afba937eSOliver Neukum static int wdm_open(struct inode *inode, struct file *file)
564afba937eSOliver Neukum {
565afba937eSOliver Neukum 	int minor = iminor(inode);
566afba937eSOliver Neukum 	int rv = -ENODEV;
567afba937eSOliver Neukum 	struct usb_interface *intf;
568afba937eSOliver Neukum 	struct wdm_device *desc;
569afba937eSOliver Neukum 
570afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
571b0c13860SBjørn Mork 	desc = wdm_find_device_by_minor(minor);
572b0c13860SBjørn Mork 	if (!desc)
573afba937eSOliver Neukum 		goto out;
574afba937eSOliver Neukum 
575b0c13860SBjørn Mork 	intf = desc->intf;
576afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags))
577afba937eSOliver Neukum 		goto out;
578afba937eSOliver Neukum 	file->private_data = desc;
579afba937eSOliver Neukum 
58017d80d56SOliver Neukum 	rv = usb_autopm_get_interface(desc->intf);
58117d80d56SOliver Neukum 	if (rv < 0) {
5829908a32eSGreg Kroah-Hartman 		dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
58317d80d56SOliver Neukum 		goto out;
58417d80d56SOliver Neukum 	}
585afba937eSOliver Neukum 
586e8537bd2SBjørn Mork 	/* using write lock to protect desc->count */
587e8537bd2SBjørn Mork 	mutex_lock(&desc->wlock);
58817d80d56SOliver Neukum 	if (!desc->count++) {
589d771d8aaSOliver Neukum 		desc->werr = 0;
590d771d8aaSOliver Neukum 		desc->rerr = 0;
59117d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_KERNEL);
592afba937eSOliver Neukum 		if (rv < 0) {
593afba937eSOliver Neukum 			desc->count--;
5949908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
5959908a32eSGreg Kroah-Hartman 				"Error submitting int urb - %d\n", rv);
596afba937eSOliver Neukum 		}
59717d80d56SOliver Neukum 	} else {
598afba937eSOliver Neukum 		rv = 0;
59917d80d56SOliver Neukum 	}
600e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
6013cc36157SBjørn Mork 	if (desc->count == 1)
6023cc36157SBjørn Mork 		desc->manage_power(intf, 1);
60317d80d56SOliver Neukum 	usb_autopm_put_interface(desc->intf);
604afba937eSOliver Neukum out:
605afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
606afba937eSOliver Neukum 	return rv;
607afba937eSOliver Neukum }
608afba937eSOliver Neukum 
609afba937eSOliver Neukum static int wdm_release(struct inode *inode, struct file *file)
610afba937eSOliver Neukum {
611afba937eSOliver Neukum 	struct wdm_device *desc = file->private_data;
612afba937eSOliver Neukum 
613afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
614e8537bd2SBjørn Mork 
615e8537bd2SBjørn Mork 	/* using write lock to protect desc->count */
616e8537bd2SBjørn Mork 	mutex_lock(&desc->wlock);
617afba937eSOliver Neukum 	desc->count--;
618e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
61917d80d56SOliver Neukum 
620afba937eSOliver Neukum 	if (!desc->count) {
621afba937eSOliver Neukum 		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
622afba937eSOliver Neukum 		kill_urbs(desc);
62317d80d56SOliver Neukum 		if (!test_bit(WDM_DISCONNECTING, &desc->flags))
6243cc36157SBjørn Mork 			desc->manage_power(desc->intf, 0);
625afba937eSOliver Neukum 	}
626afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
627afba937eSOliver Neukum 	return 0;
628afba937eSOliver Neukum }
629afba937eSOliver Neukum 
630afba937eSOliver Neukum static const struct file_operations wdm_fops = {
631afba937eSOliver Neukum 	.owner =	THIS_MODULE,
632afba937eSOliver Neukum 	.read =		wdm_read,
633afba937eSOliver Neukum 	.write =	wdm_write,
634afba937eSOliver Neukum 	.open =		wdm_open,
635afba937eSOliver Neukum 	.flush =	wdm_flush,
636afba937eSOliver Neukum 	.release =	wdm_release,
6376038f373SArnd Bergmann 	.poll =		wdm_poll,
6386038f373SArnd Bergmann 	.llseek =	noop_llseek,
639afba937eSOliver Neukum };
640afba937eSOliver Neukum 
641afba937eSOliver Neukum static struct usb_class_driver wdm_class = {
642afba937eSOliver Neukum 	.name =		"cdc-wdm%d",
643afba937eSOliver Neukum 	.fops =		&wdm_fops,
644afba937eSOliver Neukum 	.minor_base =	WDM_MINOR_BASE,
645afba937eSOliver Neukum };
646afba937eSOliver Neukum 
647afba937eSOliver Neukum /* --- error handling --- */
648afba937eSOliver Neukum static void wdm_rxwork(struct work_struct *work)
649afba937eSOliver Neukum {
650afba937eSOliver Neukum 	struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
651afba937eSOliver Neukum 	unsigned long flags;
652afba937eSOliver Neukum 	int rv;
653afba937eSOliver Neukum 
654afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
655afba937eSOliver Neukum 	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
656afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
657afba937eSOliver Neukum 	} else {
658afba937eSOliver Neukum 		spin_unlock_irqrestore(&desc->iuspin, flags);
659afba937eSOliver Neukum 		rv = usb_submit_urb(desc->response, GFP_KERNEL);
660afba937eSOliver Neukum 		if (rv < 0 && rv != -EPERM) {
661afba937eSOliver Neukum 			spin_lock_irqsave(&desc->iuspin, flags);
662afba937eSOliver Neukum 			if (!test_bit(WDM_DISCONNECTING, &desc->flags))
663afba937eSOliver Neukum 				schedule_work(&desc->rxwork);
664afba937eSOliver Neukum 			spin_unlock_irqrestore(&desc->iuspin, flags);
665afba937eSOliver Neukum 		}
666afba937eSOliver Neukum 	}
667afba937eSOliver Neukum }
668afba937eSOliver Neukum 
669afba937eSOliver Neukum /* --- hotplug --- */
670afba937eSOliver Neukum 
6713cc36157SBjørn Mork static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
6723cc36157SBjørn Mork 		u16 bufsize, int (*manage_power)(struct usb_interface *, int))
673afba937eSOliver Neukum {
6740dffb486SBjørn Mork 	int rv = -ENOMEM;
675afba937eSOliver Neukum 	struct wdm_device *desc;
676afba937eSOliver Neukum 
677afba937eSOliver Neukum 	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
678afba937eSOliver Neukum 	if (!desc)
679afba937eSOliver Neukum 		goto out;
680b0c13860SBjørn Mork 	INIT_LIST_HEAD(&desc->device_list);
681e8537bd2SBjørn Mork 	mutex_init(&desc->rlock);
682e8537bd2SBjørn Mork 	mutex_init(&desc->wlock);
683afba937eSOliver Neukum 	spin_lock_init(&desc->iuspin);
684afba937eSOliver Neukum 	init_waitqueue_head(&desc->wait);
6850dffb486SBjørn Mork 	desc->wMaxCommand = bufsize;
686052fbc0dSOliver Neukum 	/* this will be expanded and needed in hardware endianness */
687afba937eSOliver Neukum 	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
688afba937eSOliver Neukum 	desc->intf = intf;
689afba937eSOliver Neukum 	INIT_WORK(&desc->rxwork, wdm_rxwork);
690afba937eSOliver Neukum 
691afba937eSOliver Neukum 	rv = -EINVAL;
6920dffb486SBjørn Mork 	if (!usb_endpoint_is_int_in(ep))
693052fbc0dSOliver Neukum 		goto err;
694afba937eSOliver Neukum 
69529cc8897SKuninori Morimoto 	desc->wMaxPacketSize = usb_endpoint_maxp(ep);
696afba937eSOliver Neukum 
697afba937eSOliver Neukum 	desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
698afba937eSOliver Neukum 	if (!desc->orq)
699afba937eSOliver Neukum 		goto err;
700afba937eSOliver Neukum 	desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
701afba937eSOliver Neukum 	if (!desc->irq)
702afba937eSOliver Neukum 		goto err;
703afba937eSOliver Neukum 
704afba937eSOliver Neukum 	desc->validity = usb_alloc_urb(0, GFP_KERNEL);
705afba937eSOliver Neukum 	if (!desc->validity)
706afba937eSOliver Neukum 		goto err;
707afba937eSOliver Neukum 
708afba937eSOliver Neukum 	desc->response = usb_alloc_urb(0, GFP_KERNEL);
709afba937eSOliver Neukum 	if (!desc->response)
710afba937eSOliver Neukum 		goto err;
711afba937eSOliver Neukum 
712afba937eSOliver Neukum 	desc->command = usb_alloc_urb(0, GFP_KERNEL);
713afba937eSOliver Neukum 	if (!desc->command)
714afba937eSOliver Neukum 		goto err;
715afba937eSOliver Neukum 
716afba937eSOliver Neukum 	desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
717afba937eSOliver Neukum 	if (!desc->ubuf)
718afba937eSOliver Neukum 		goto err;
719afba937eSOliver Neukum 
7208457d99cSBjørn Mork 	desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
721afba937eSOliver Neukum 	if (!desc->sbuf)
722afba937eSOliver Neukum 		goto err;
723afba937eSOliver Neukum 
7248457d99cSBjørn Mork 	desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
725afba937eSOliver Neukum 	if (!desc->inbuf)
7268457d99cSBjørn Mork 		goto err;
727afba937eSOliver Neukum 
728afba937eSOliver Neukum 	usb_fill_int_urb(
729afba937eSOliver Neukum 		desc->validity,
730afba937eSOliver Neukum 		interface_to_usbdev(intf),
731afba937eSOliver Neukum 		usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
732afba937eSOliver Neukum 		desc->sbuf,
733afba937eSOliver Neukum 		desc->wMaxPacketSize,
734afba937eSOliver Neukum 		wdm_int_callback,
735afba937eSOliver Neukum 		desc,
736afba937eSOliver Neukum 		ep->bInterval
737afba937eSOliver Neukum 	);
738afba937eSOliver Neukum 
73919b85b3bSBjørn Mork 	desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
74019b85b3bSBjørn Mork 	desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
74119b85b3bSBjørn Mork 	desc->irq->wValue = 0;
74219b85b3bSBjørn Mork 	desc->irq->wIndex = desc->inum;
74319b85b3bSBjørn Mork 	desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
74419b85b3bSBjørn Mork 
74519b85b3bSBjørn Mork 	usb_fill_control_urb(
74619b85b3bSBjørn Mork 		desc->response,
7478143a896SBjørn Mork 		interface_to_usbdev(intf),
74819b85b3bSBjørn Mork 		/* using common endpoint 0 */
74919b85b3bSBjørn Mork 		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
75019b85b3bSBjørn Mork 		(unsigned char *)desc->irq,
75119b85b3bSBjørn Mork 		desc->inbuf,
75219b85b3bSBjørn Mork 		desc->wMaxCommand,
75319b85b3bSBjørn Mork 		wdm_in_callback,
75419b85b3bSBjørn Mork 		desc
75519b85b3bSBjørn Mork 	);
75619b85b3bSBjørn Mork 
7573cc36157SBjørn Mork 	desc->manage_power = manage_power;
7583cc36157SBjørn Mork 
759b0c13860SBjørn Mork 	spin_lock(&wdm_device_list_lock);
760b0c13860SBjørn Mork 	list_add(&desc->device_list, &wdm_device_list);
761b0c13860SBjørn Mork 	spin_unlock(&wdm_device_list_lock);
762b0c13860SBjørn Mork 
763afba937eSOliver Neukum 	rv = usb_register_dev(intf, &wdm_class);
764052fbc0dSOliver Neukum 	if (rv < 0)
765b0c13860SBjørn Mork 		goto err;
766052fbc0dSOliver Neukum 	else
767820c629aSBjørn Mork 		dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
768afba937eSOliver Neukum out:
769afba937eSOliver Neukum 	return rv;
770afba937eSOliver Neukum err:
7710dffb486SBjørn Mork 	cleanup(desc);
7720dffb486SBjørn Mork 	return rv;
7730dffb486SBjørn Mork }
7740dffb486SBjørn Mork 
7753cc36157SBjørn Mork static int wdm_manage_power(struct usb_interface *intf, int on)
7763cc36157SBjørn Mork {
7773cc36157SBjørn Mork 	/* need autopm_get/put here to ensure the usbcore sees the new value */
7783cc36157SBjørn Mork 	int rv = usb_autopm_get_interface(intf);
7793cc36157SBjørn Mork 	if (rv < 0)
7803cc36157SBjørn Mork 		goto err;
7813cc36157SBjørn Mork 
7823cc36157SBjørn Mork 	intf->needs_remote_wakeup = on;
7833cc36157SBjørn Mork 	usb_autopm_put_interface(intf);
7843cc36157SBjørn Mork err:
7853cc36157SBjørn Mork 	return rv;
7863cc36157SBjørn Mork }
7873cc36157SBjørn Mork 
7880dffb486SBjørn Mork static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
7890dffb486SBjørn Mork {
7900dffb486SBjørn Mork 	int rv = -EINVAL;
7910dffb486SBjørn Mork 	struct usb_host_interface *iface;
7920dffb486SBjørn Mork 	struct usb_endpoint_descriptor *ep;
7930dffb486SBjørn Mork 	struct usb_cdc_dmm_desc *dmhd;
7940dffb486SBjørn Mork 	u8 *buffer = intf->altsetting->extra;
7950dffb486SBjørn Mork 	int buflen = intf->altsetting->extralen;
7960dffb486SBjørn Mork 	u16 maxcom = WDM_DEFAULT_BUFSIZE;
7970dffb486SBjørn Mork 
7980dffb486SBjørn Mork 	if (!buffer)
7990dffb486SBjørn Mork 		goto err;
8000dffb486SBjørn Mork 	while (buflen > 2) {
8010dffb486SBjørn Mork 		if (buffer[1] != USB_DT_CS_INTERFACE) {
8020dffb486SBjørn Mork 			dev_err(&intf->dev, "skipping garbage\n");
8030dffb486SBjørn Mork 			goto next_desc;
8040dffb486SBjørn Mork 		}
8050dffb486SBjørn Mork 
8060dffb486SBjørn Mork 		switch (buffer[2]) {
8070dffb486SBjørn Mork 		case USB_CDC_HEADER_TYPE:
8080dffb486SBjørn Mork 			break;
8090dffb486SBjørn Mork 		case USB_CDC_DMM_TYPE:
8100dffb486SBjørn Mork 			dmhd = (struct usb_cdc_dmm_desc *)buffer;
8110dffb486SBjørn Mork 			maxcom = le16_to_cpu(dmhd->wMaxCommand);
8120dffb486SBjørn Mork 			dev_dbg(&intf->dev,
8130dffb486SBjørn Mork 				"Finding maximum buffer length: %d", maxcom);
8140dffb486SBjørn Mork 			break;
8150dffb486SBjørn Mork 		default:
8160dffb486SBjørn Mork 			dev_err(&intf->dev,
8170dffb486SBjørn Mork 				"Ignoring extra header, type %d, length %d\n",
8180dffb486SBjørn Mork 				buffer[2], buffer[0]);
8190dffb486SBjørn Mork 			break;
8200dffb486SBjørn Mork 		}
8210dffb486SBjørn Mork next_desc:
8220dffb486SBjørn Mork 		buflen -= buffer[0];
8230dffb486SBjørn Mork 		buffer += buffer[0];
8240dffb486SBjørn Mork 	}
8250dffb486SBjørn Mork 
8260dffb486SBjørn Mork 	iface = intf->cur_altsetting;
8270dffb486SBjørn Mork 	if (iface->desc.bNumEndpoints != 1)
8280dffb486SBjørn Mork 		goto err;
8290dffb486SBjørn Mork 	ep = &iface->endpoint[0].desc;
8300dffb486SBjørn Mork 
8313cc36157SBjørn Mork 	rv = wdm_create(intf, ep, maxcom, &wdm_manage_power);
8320dffb486SBjørn Mork 
8330dffb486SBjørn Mork err:
834afba937eSOliver Neukum 	return rv;
835afba937eSOliver Neukum }
836afba937eSOliver Neukum 
8373cc36157SBjørn Mork /**
8383cc36157SBjørn Mork  * usb_cdc_wdm_register - register a WDM subdriver
8393cc36157SBjørn Mork  * @intf: usb interface the subdriver will associate with
8403cc36157SBjørn Mork  * @ep: interrupt endpoint to monitor for notifications
8413cc36157SBjørn Mork  * @bufsize: maximum message size to support for read/write
8423cc36157SBjørn Mork  *
8433cc36157SBjørn Mork  * Create WDM usb class character device and associate it with intf
8443cc36157SBjørn Mork  * without binding, allowing another driver to manage the interface.
8453cc36157SBjørn Mork  *
8463cc36157SBjørn Mork  * The subdriver will manage the given interrupt endpoint exclusively
8473cc36157SBjørn Mork  * and will issue control requests referring to the given intf. It
8483cc36157SBjørn Mork  * will otherwise avoid interferring, and in particular not do
8493cc36157SBjørn Mork  * usb_set_intfdata/usb_get_intfdata on intf.
8503cc36157SBjørn Mork  *
8513cc36157SBjørn Mork  * The return value is a pointer to the subdriver's struct usb_driver.
8523cc36157SBjørn Mork  * The registering driver is responsible for calling this subdriver's
8533cc36157SBjørn Mork  * disconnect, suspend, resume, pre_reset and post_reset methods from
8543cc36157SBjørn Mork  * its own.
8553cc36157SBjørn Mork  */
8563cc36157SBjørn Mork struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf,
8573cc36157SBjørn Mork 					struct usb_endpoint_descriptor *ep,
8583cc36157SBjørn Mork 					int bufsize,
8593cc36157SBjørn Mork 					int (*manage_power)(struct usb_interface *, int))
8603cc36157SBjørn Mork {
8613cc36157SBjørn Mork 	int rv = -EINVAL;
8623cc36157SBjørn Mork 
8633cc36157SBjørn Mork 	rv = wdm_create(intf, ep, bufsize, manage_power);
8643cc36157SBjørn Mork 	if (rv < 0)
8653cc36157SBjørn Mork 		goto err;
8663cc36157SBjørn Mork 
8673cc36157SBjørn Mork 	return &wdm_driver;
8683cc36157SBjørn Mork err:
8693cc36157SBjørn Mork 	return ERR_PTR(rv);
8703cc36157SBjørn Mork }
8713cc36157SBjørn Mork EXPORT_SYMBOL(usb_cdc_wdm_register);
8723cc36157SBjørn Mork 
873afba937eSOliver Neukum static void wdm_disconnect(struct usb_interface *intf)
874afba937eSOliver Neukum {
875afba937eSOliver Neukum 	struct wdm_device *desc;
876afba937eSOliver Neukum 	unsigned long flags;
877afba937eSOliver Neukum 
878afba937eSOliver Neukum 	usb_deregister_dev(intf, &wdm_class);
879b0c13860SBjørn Mork 	desc = wdm_find_device(intf);
880afba937eSOliver Neukum 	mutex_lock(&wdm_mutex);
881afba937eSOliver Neukum 
882afba937eSOliver Neukum 	/* the spinlock makes sure no new urbs are generated in the callbacks */
883afba937eSOliver Neukum 	spin_lock_irqsave(&desc->iuspin, flags);
884afba937eSOliver Neukum 	set_bit(WDM_DISCONNECTING, &desc->flags);
885afba937eSOliver Neukum 	set_bit(WDM_READ, &desc->flags);
88617d80d56SOliver Neukum 	/* to terminate pending flushes */
887afba937eSOliver Neukum 	clear_bit(WDM_IN_USE, &desc->flags);
888afba937eSOliver Neukum 	spin_unlock_irqrestore(&desc->iuspin, flags);
88962aaf24dSBjørn Mork 	wake_up_all(&desc->wait);
890e8537bd2SBjørn Mork 	mutex_lock(&desc->rlock);
891e8537bd2SBjørn Mork 	mutex_lock(&desc->wlock);
892afba937eSOliver Neukum 	kill_urbs(desc);
893d93d16e9SOliver Neukum 	cancel_work_sync(&desc->rxwork);
894e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
895e8537bd2SBjørn Mork 	mutex_unlock(&desc->rlock);
896afba937eSOliver Neukum 	if (!desc->count)
897afba937eSOliver Neukum 		cleanup(desc);
898afba937eSOliver Neukum 	mutex_unlock(&wdm_mutex);
899afba937eSOliver Neukum }
900afba937eSOliver Neukum 
901d93d16e9SOliver Neukum #ifdef CONFIG_PM
90217d80d56SOliver Neukum static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
90317d80d56SOliver Neukum {
904b0c13860SBjørn Mork 	struct wdm_device *desc = wdm_find_device(intf);
90517d80d56SOliver Neukum 	int rv = 0;
90617d80d56SOliver Neukum 
90717d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
90817d80d56SOliver Neukum 
909d93d16e9SOliver Neukum 	/* if this is an autosuspend the caller does the locking */
910e8537bd2SBjørn Mork 	if (!PMSG_IS_AUTO(message)) {
911e8537bd2SBjørn Mork 		mutex_lock(&desc->rlock);
912e8537bd2SBjørn Mork 		mutex_lock(&desc->wlock);
913e8537bd2SBjørn Mork 	}
91462e66854SOliver Neukum 	spin_lock_irq(&desc->iuspin);
915d93d16e9SOliver Neukum 
9165b1b0b81SAlan Stern 	if (PMSG_IS_AUTO(message) &&
917922a5eadSOliver Neukum 			(test_bit(WDM_IN_USE, &desc->flags)
918922a5eadSOliver Neukum 			|| test_bit(WDM_RESPONDING, &desc->flags))) {
91962e66854SOliver Neukum 		spin_unlock_irq(&desc->iuspin);
92017d80d56SOliver Neukum 		rv = -EBUSY;
92117d80d56SOliver Neukum 	} else {
922d93d16e9SOliver Neukum 
923beb1d35fSOliver Neukum 		set_bit(WDM_SUSPENDING, &desc->flags);
92462e66854SOliver Neukum 		spin_unlock_irq(&desc->iuspin);
925d93d16e9SOliver Neukum 		/* callback submits work - order is essential */
92617d80d56SOliver Neukum 		kill_urbs(desc);
927d93d16e9SOliver Neukum 		cancel_work_sync(&desc->rxwork);
92817d80d56SOliver Neukum 	}
929e8537bd2SBjørn Mork 	if (!PMSG_IS_AUTO(message)) {
930e8537bd2SBjørn Mork 		mutex_unlock(&desc->wlock);
931e8537bd2SBjørn Mork 		mutex_unlock(&desc->rlock);
932e8537bd2SBjørn Mork 	}
93317d80d56SOliver Neukum 
93417d80d56SOliver Neukum 	return rv;
93517d80d56SOliver Neukum }
936d93d16e9SOliver Neukum #endif
93717d80d56SOliver Neukum 
93817d80d56SOliver Neukum static int recover_from_urb_loss(struct wdm_device *desc)
93917d80d56SOliver Neukum {
94017d80d56SOliver Neukum 	int rv = 0;
94117d80d56SOliver Neukum 
94217d80d56SOliver Neukum 	if (desc->count) {
94317d80d56SOliver Neukum 		rv = usb_submit_urb(desc->validity, GFP_NOIO);
94417d80d56SOliver Neukum 		if (rv < 0)
9459908a32eSGreg Kroah-Hartman 			dev_err(&desc->intf->dev,
9469908a32eSGreg Kroah-Hartman 				"Error resume submitting int urb - %d\n", rv);
94717d80d56SOliver Neukum 	}
94817d80d56SOliver Neukum 	return rv;
94917d80d56SOliver Neukum }
950d93d16e9SOliver Neukum 
951d93d16e9SOliver Neukum #ifdef CONFIG_PM
95217d80d56SOliver Neukum static int wdm_resume(struct usb_interface *intf)
95317d80d56SOliver Neukum {
954b0c13860SBjørn Mork 	struct wdm_device *desc = wdm_find_device(intf);
95517d80d56SOliver Neukum 	int rv;
95617d80d56SOliver Neukum 
95717d80d56SOliver Neukum 	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
958338124c1SOliver Neukum 
959beb1d35fSOliver Neukum 	clear_bit(WDM_SUSPENDING, &desc->flags);
96062e66854SOliver Neukum 	rv = recover_from_urb_loss(desc);
961338124c1SOliver Neukum 
96217d80d56SOliver Neukum 	return rv;
96317d80d56SOliver Neukum }
964d93d16e9SOliver Neukum #endif
96517d80d56SOliver Neukum 
96617d80d56SOliver Neukum static int wdm_pre_reset(struct usb_interface *intf)
96717d80d56SOliver Neukum {
968b0c13860SBjørn Mork 	struct wdm_device *desc = wdm_find_device(intf);
96917d80d56SOliver Neukum 
970d771d8aaSOliver Neukum 	/*
971d771d8aaSOliver Neukum 	 * we notify everybody using poll of
972d771d8aaSOliver Neukum 	 * an exceptional situation
973d771d8aaSOliver Neukum 	 * must be done before recovery lest a spontaneous
974d771d8aaSOliver Neukum 	 * message from the device is lost
975d771d8aaSOliver Neukum 	 */
976d771d8aaSOliver Neukum 	spin_lock_irq(&desc->iuspin);
97788044202SBjørn Mork 	set_bit(WDM_RESETTING, &desc->flags);	/* inform read/write */
97888044202SBjørn Mork 	set_bit(WDM_READ, &desc->flags);	/* unblock read */
97988044202SBjørn Mork 	clear_bit(WDM_IN_USE, &desc->flags);	/* unblock write */
980d771d8aaSOliver Neukum 	desc->rerr = -EINTR;
981d771d8aaSOliver Neukum 	spin_unlock_irq(&desc->iuspin);
982d771d8aaSOliver Neukum 	wake_up_all(&desc->wait);
98388044202SBjørn Mork 	mutex_lock(&desc->rlock);
98488044202SBjørn Mork 	mutex_lock(&desc->wlock);
98588044202SBjørn Mork 	kill_urbs(desc);
98688044202SBjørn Mork 	cancel_work_sync(&desc->rxwork);
98717d80d56SOliver Neukum 	return 0;
98817d80d56SOliver Neukum }
98917d80d56SOliver Neukum 
99017d80d56SOliver Neukum static int wdm_post_reset(struct usb_interface *intf)
99117d80d56SOliver Neukum {
992b0c13860SBjørn Mork 	struct wdm_device *desc = wdm_find_device(intf);
99317d80d56SOliver Neukum 	int rv;
99417d80d56SOliver Neukum 
99588044202SBjørn Mork 	clear_bit(WDM_RESETTING, &desc->flags);
99617d80d56SOliver Neukum 	rv = recover_from_urb_loss(desc);
997e8537bd2SBjørn Mork 	mutex_unlock(&desc->wlock);
998e8537bd2SBjørn Mork 	mutex_unlock(&desc->rlock);
99917d80d56SOliver Neukum 	return 0;
100017d80d56SOliver Neukum }
100117d80d56SOliver Neukum 
1002afba937eSOliver Neukum static struct usb_driver wdm_driver = {
1003afba937eSOliver Neukum 	.name =		"cdc_wdm",
1004afba937eSOliver Neukum 	.probe =	wdm_probe,
1005afba937eSOliver Neukum 	.disconnect =	wdm_disconnect,
1006d93d16e9SOliver Neukum #ifdef CONFIG_PM
100717d80d56SOliver Neukum 	.suspend =	wdm_suspend,
100817d80d56SOliver Neukum 	.resume =	wdm_resume,
100917d80d56SOliver Neukum 	.reset_resume =	wdm_resume,
1010d93d16e9SOliver Neukum #endif
101117d80d56SOliver Neukum 	.pre_reset =	wdm_pre_reset,
101217d80d56SOliver Neukum 	.post_reset =	wdm_post_reset,
1013afba937eSOliver Neukum 	.id_table =	wdm_ids,
101417d80d56SOliver Neukum 	.supports_autosuspend = 1,
1015afba937eSOliver Neukum };
1016afba937eSOliver Neukum 
101765db4305SGreg Kroah-Hartman module_usb_driver(wdm_driver);
1018afba937eSOliver Neukum 
1019afba937eSOliver Neukum MODULE_AUTHOR(DRIVER_AUTHOR);
102087d65e54SOliver Neukum MODULE_DESCRIPTION(DRIVER_DESC);
1021afba937eSOliver Neukum MODULE_LICENSE("GPL");
1022