Blame view

drivers/usb/class/cdc-wdm.c 25.7 KB
afba937e5   Oliver Neukum   USB: CDC WDM driver
1
2
3
4
5
  /*
   * cdc-wdm.c
   *
   * This driver supports USB CDC WCM Device Management.
   *
052fbc0d7   Oliver Neukum   USB: correct erro...
6
   * Copyright (c) 2007-2009 Oliver Neukum
afba937e5   Oliver Neukum   USB: CDC WDM driver
7
8
9
10
11
12
13
14
15
   *
   * Some code taken from cdc-acm.c
   *
   * Released under the GPLv2.
   *
   * Many thanks to Carl Nordbeck
   */
  #include <linux/kernel.h>
  #include <linux/errno.h>
3edce1cf8   Bjørn Mork   USB: cdc-wdm: imp...
16
  #include <linux/ioctl.h>
afba937e5   Oliver Neukum   USB: CDC WDM driver
17
18
  #include <linux/slab.h>
  #include <linux/module.h>
afba937e5   Oliver Neukum   USB: CDC WDM driver
19
20
21
22
23
24
25
26
  #include <linux/mutex.h>
  #include <linux/uaccess.h>
  #include <linux/bitops.h>
  #include <linux/poll.h>
  #include <linux/usb.h>
  #include <linux/usb/cdc.h>
  #include <asm/byteorder.h>
  #include <asm/unaligned.h>
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
27
  #include <linux/usb/cdc-wdm.h>
afba937e5   Oliver Neukum   USB: CDC WDM driver
28
29
30
31
  
  /*
   * Version Information
   */
87d65e54b   Oliver Neukum   USB: cdc-wdm cleanup
32
  #define DRIVER_VERSION "v0.03"
afba937e5   Oliver Neukum   USB: CDC WDM driver
33
  #define DRIVER_AUTHOR "Oliver Neukum"
87d65e54b   Oliver Neukum   USB: cdc-wdm cleanup
34
  #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
afba937e5   Oliver Neukum   USB: CDC WDM driver
35

6ef4852b1   Németh Márton   USB class: make U...
36
  static const struct usb_device_id wdm_ids[] = {
afba937e5   Oliver Neukum   USB: CDC WDM driver
37
38
39
40
41
42
43
44
  	{
  		.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
  				 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
  		.bInterfaceClass = USB_CLASS_COMM,
  		.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
  	},
  	{ }
  };
aa5380b90   Oliver Neukum   USB: cdc-wdm: mak...
45
  MODULE_DEVICE_TABLE (usb, wdm_ids);
afba937e5   Oliver Neukum   USB: CDC WDM driver
46
47
48
49
50
51
52
53
54
  #define WDM_MINOR_BASE	176
  
  
  #define WDM_IN_USE		1
  #define WDM_DISCONNECTING	2
  #define WDM_RESULT		3
  #define WDM_READ		4
  #define WDM_INT_STALL		5
  #define WDM_POLL_RUNNING	6
922a5eadd   Oliver Neukum   usb: cdc-wdm: Fix...
55
  #define WDM_RESPONDING		7
beb1d35f1   Oliver Neukum   usb: cdc-wdm: Fix...
56
  #define WDM_SUSPENDING		8
880442027   Bjørn Mork   usb: cdc-wdm: mak...
57
  #define WDM_RESETTING		9
c0f5ecee4   Oliver Neukum   USB: cdc-wdm: fix...
58
  #define WDM_OVERFLOW		10
afba937e5   Oliver Neukum   USB: CDC WDM driver
59
60
  
  #define WDM_MAX			16
7e3054a00   Bjørn Mork   USB: cdc-wdm: Avo...
61
62
  /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */
  #define WDM_DEFAULT_BUFSIZE	256
afba937e5   Oliver Neukum   USB: CDC WDM driver
63
64
  
  static DEFINE_MUTEX(wdm_mutex);
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
65
66
  static DEFINE_SPINLOCK(wdm_device_list_lock);
  static LIST_HEAD(wdm_device_list);
afba937e5   Oliver Neukum   USB: CDC WDM driver
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  
  /* --- method tables --- */
  
  struct wdm_device {
  	u8			*inbuf; /* buffer for response */
  	u8			*outbuf; /* buffer for command */
  	u8			*sbuf; /* buffer for status */
  	u8			*ubuf; /* buffer for copy to user space */
  
  	struct urb		*command;
  	struct urb		*response;
  	struct urb		*validity;
  	struct usb_interface	*intf;
  	struct usb_ctrlrequest	*orq;
  	struct usb_ctrlrequest	*irq;
  	spinlock_t		iuspin;
  
  	unsigned long		flags;
  	u16			bufsize;
  	u16			wMaxCommand;
  	u16			wMaxPacketSize;
afba937e5   Oliver Neukum   USB: CDC WDM driver
88
89
90
91
92
93
94
  	__le16			inum;
  	int			reslength;
  	int			length;
  	int			read;
  	int			count;
  	dma_addr_t		shandle;
  	dma_addr_t		ihandle;
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
95
96
  	struct mutex		wlock;
  	struct mutex		rlock;
afba937e5   Oliver Neukum   USB: CDC WDM driver
97
98
99
100
  	wait_queue_head_t	wait;
  	struct work_struct	rxwork;
  	int			werr;
  	int			rerr;
73e06865e   Greg Suarez   USB: cdc-wdm: sup...
101
  	int                     resp_count;
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
102
103
  
  	struct list_head	device_list;
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
104
  	int			(*manage_power)(struct usb_interface *, int);
afba937e5   Oliver Neukum   USB: CDC WDM driver
105
106
107
  };
  
  static struct usb_driver wdm_driver;
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
108
109
110
  /* return intfdata if we own the interface, else look up intf in the list */
  static struct wdm_device *wdm_find_device(struct usb_interface *intf)
  {
6a4488689   Bjørn Mork   USB: cdc-wdm: fix...
111
  	struct wdm_device *desc;
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
112
113
114
115
  
  	spin_lock(&wdm_device_list_lock);
  	list_for_each_entry(desc, &wdm_device_list, device_list)
  		if (desc->intf == intf)
6a4488689   Bjørn Mork   USB: cdc-wdm: fix...
116
117
118
  			goto found;
  	desc = NULL;
  found:
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
119
120
121
122
123
124
125
  	spin_unlock(&wdm_device_list_lock);
  
  	return desc;
  }
  
  static struct wdm_device *wdm_find_device_by_minor(int minor)
  {
6a4488689   Bjørn Mork   USB: cdc-wdm: fix...
126
  	struct wdm_device *desc;
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
127
128
129
130
  
  	spin_lock(&wdm_device_list_lock);
  	list_for_each_entry(desc, &wdm_device_list, device_list)
  		if (desc->intf->minor == minor)
6a4488689   Bjørn Mork   USB: cdc-wdm: fix...
131
132
133
  			goto found;
  	desc = NULL;
  found:
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
134
135
136
137
  	spin_unlock(&wdm_device_list_lock);
  
  	return desc;
  }
afba937e5   Oliver Neukum   USB: CDC WDM driver
138
139
140
141
142
143
144
145
  /* --- callbacks --- */
  static void wdm_out_callback(struct urb *urb)
  {
  	struct wdm_device *desc;
  	desc = urb->context;
  	spin_lock(&desc->iuspin);
  	desc->werr = urb->status;
  	spin_unlock(&desc->iuspin);
afba937e5   Oliver Neukum   USB: CDC WDM driver
146
  	kfree(desc->outbuf);
5c22837ad   Oliver Neukum   USB: cdc-wdm: fix...
147
148
  	desc->outbuf = NULL;
  	clear_bit(WDM_IN_USE, &desc->flags);
afba937e5   Oliver Neukum   USB: CDC WDM driver
149
150
151
152
153
154
155
  	wake_up(&desc->wait);
  }
  
  static void wdm_in_callback(struct urb *urb)
  {
  	struct wdm_device *desc = urb->context;
  	int status = urb->status;
c0f5ecee4   Oliver Neukum   USB: cdc-wdm: fix...
156
  	int length = urb->actual_length;
afba937e5   Oliver Neukum   USB: CDC WDM driver
157
158
  
  	spin_lock(&desc->iuspin);
922a5eadd   Oliver Neukum   usb: cdc-wdm: Fix...
159
  	clear_bit(WDM_RESPONDING, &desc->flags);
afba937e5   Oliver Neukum   USB: CDC WDM driver
160
161
162
163
164
165
  
  	if (status) {
  		switch (status) {
  		case -ENOENT:
  			dev_dbg(&desc->intf->dev,
  				"nonzero urb status received: -ENOENT");
922a5eadd   Oliver Neukum   usb: cdc-wdm: Fix...
166
  			goto skip_error;
afba937e5   Oliver Neukum   USB: CDC WDM driver
167
168
169
  		case -ECONNRESET:
  			dev_dbg(&desc->intf->dev,
  				"nonzero urb status received: -ECONNRESET");
922a5eadd   Oliver Neukum   usb: cdc-wdm: Fix...
170
  			goto skip_error;
afba937e5   Oliver Neukum   USB: CDC WDM driver
171
172
173
  		case -ESHUTDOWN:
  			dev_dbg(&desc->intf->dev,
  				"nonzero urb status received: -ESHUTDOWN");
922a5eadd   Oliver Neukum   usb: cdc-wdm: Fix...
174
  			goto skip_error;
afba937e5   Oliver Neukum   USB: CDC WDM driver
175
  		case -EPIPE:
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
176
177
178
  			dev_err(&desc->intf->dev,
  				"nonzero urb status received: -EPIPE
  ");
afba937e5   Oliver Neukum   USB: CDC WDM driver
179
180
  			break;
  		default:
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
181
182
183
  			dev_err(&desc->intf->dev,
  				"Unexpected error %d
  ", status);
afba937e5   Oliver Neukum   USB: CDC WDM driver
184
185
186
187
188
  			break;
  		}
  	}
  
  	desc->rerr = status;
c0f5ecee4   Oliver Neukum   USB: cdc-wdm: fix...
189
190
191
192
193
194
195
196
197
198
199
  	if (length + desc->length > desc->wMaxCommand) {
  		/* The buffer would overflow */
  		set_bit(WDM_OVERFLOW, &desc->flags);
  	} else {
  		/* we may already be in overflow */
  		if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
  			memmove(desc->ubuf + desc->length, desc->inbuf, length);
  			desc->length += length;
  			desc->reslength = length;
  		}
  	}
922a5eadd   Oliver Neukum   usb: cdc-wdm: Fix...
200
  skip_error:
afba937e5   Oliver Neukum   USB: CDC WDM driver
201
202
203
204
205
206
207
208
209
  	wake_up(&desc->wait);
  
  	set_bit(WDM_READ, &desc->flags);
  	spin_unlock(&desc->iuspin);
  }
  
  static void wdm_int_callback(struct urb *urb)
  {
  	int rv = 0;
6dd433e6c   Oliver Neukum   USB: cdc-wdm: fix...
210
  	int responding;
afba937e5   Oliver Neukum   USB: CDC WDM driver
211
212
  	int status = urb->status;
  	struct wdm_device *desc;
afba937e5   Oliver Neukum   USB: CDC WDM driver
213
214
215
  	struct usb_cdc_notification *dr;
  
  	desc = urb->context;
afba937e5   Oliver Neukum   USB: CDC WDM driver
216
217
218
219
220
221
222
223
224
225
  	dr = (struct usb_cdc_notification *)desc->sbuf;
  
  	if (status) {
  		switch (status) {
  		case -ESHUTDOWN:
  		case -ENOENT:
  		case -ECONNRESET:
  			return; /* unplug */
  		case -EPIPE:
  			set_bit(WDM_INT_STALL, &desc->flags);
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
226
227
  			dev_err(&desc->intf->dev, "Stall on int endpoint
  ");
afba937e5   Oliver Neukum   USB: CDC WDM driver
228
229
  			goto sw; /* halt is cleared in work */
  		default:
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
230
231
232
  			dev_err(&desc->intf->dev,
  				"nonzero urb status received: %d
  ", status);
afba937e5   Oliver Neukum   USB: CDC WDM driver
233
234
235
236
237
  			break;
  		}
  	}
  
  	if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
238
239
240
  		dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes
  ",
  			urb->actual_length);
afba937e5   Oliver Neukum   USB: CDC WDM driver
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  		goto exit;
  	}
  
  	switch (dr->bNotificationType) {
  	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
  		dev_dbg(&desc->intf->dev,
  			"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
  			dr->wIndex, dr->wLength);
  		break;
  
  	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
  
  		dev_dbg(&desc->intf->dev,
  			"NOTIFY_NETWORK_CONNECTION %s network",
  			dr->wValue ? "connected to" : "disconnected from");
  		goto exit;
9983d6dc4   Bjørn Mork   usb: cdc-wdm: ign...
257
258
259
260
  	case USB_CDC_NOTIFY_SPEED_CHANGE:
  		dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)",
  			urb->actual_length);
  		goto exit;
afba937e5   Oliver Neukum   USB: CDC WDM driver
261
262
  	default:
  		clear_bit(WDM_POLL_RUNNING, &desc->flags);
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
263
264
265
  		dev_err(&desc->intf->dev,
  			"unknown notification %d received: index %d len %d
  ",
afba937e5   Oliver Neukum   USB: CDC WDM driver
266
267
268
  			dr->bNotificationType, dr->wIndex, dr->wLength);
  		goto exit;
  	}
afba937e5   Oliver Neukum   USB: CDC WDM driver
269
  	spin_lock(&desc->iuspin);
6dd433e6c   Oliver Neukum   USB: cdc-wdm: fix...
270
  	responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
73e06865e   Greg Suarez   USB: cdc-wdm: sup...
271
272
  	if (!desc->resp_count++ && !responding
  		&& !test_bit(WDM_DISCONNECTING, &desc->flags)
beb1d35f1   Oliver Neukum   usb: cdc-wdm: Fix...
273
  		&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
afba937e5   Oliver Neukum   USB: CDC WDM driver
274
275
276
277
278
279
  		rv = usb_submit_urb(desc->response, GFP_ATOMIC);
  		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
  			__func__, rv);
  	}
  	spin_unlock(&desc->iuspin);
  	if (rv < 0) {
922a5eadd   Oliver Neukum   usb: cdc-wdm: Fix...
280
  		clear_bit(WDM_RESPONDING, &desc->flags);
afba937e5   Oliver Neukum   USB: CDC WDM driver
281
282
283
284
285
286
  		if (rv == -EPERM)
  			return;
  		if (rv == -ENOMEM) {
  sw:
  			rv = schedule_work(&desc->rxwork);
  			if (rv)
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
287
288
289
  				dev_err(&desc->intf->dev,
  					"Cannot schedule work
  ");
afba937e5   Oliver Neukum   USB: CDC WDM driver
290
291
292
293
294
  		}
  	}
  exit:
  	rv = usb_submit_urb(urb, GFP_ATOMIC);
  	if (rv)
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
295
296
297
298
  		dev_err(&desc->intf->dev,
  			"%s - usb_submit_urb failed with result %d
  ",
  			__func__, rv);
afba937e5   Oliver Neukum   USB: CDC WDM driver
299
300
301
302
303
  
  }
  
  static void kill_urbs(struct wdm_device *desc)
  {
17d80d562   Oliver Neukum   USB: autosuspend ...
304
  	/* the order here is essential */
afba937e5   Oliver Neukum   USB: CDC WDM driver
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  	usb_kill_urb(desc->command);
  	usb_kill_urb(desc->validity);
  	usb_kill_urb(desc->response);
  }
  
  static void free_urbs(struct wdm_device *desc)
  {
  	usb_free_urb(desc->validity);
  	usb_free_urb(desc->response);
  	usb_free_urb(desc->command);
  }
  
  static void cleanup(struct wdm_device *desc)
  {
8457d99ca   Bjørn Mork   USB: cdc-wdm: no ...
319
320
  	kfree(desc->sbuf);
  	kfree(desc->inbuf);
afba937e5   Oliver Neukum   USB: CDC WDM driver
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  	kfree(desc->orq);
  	kfree(desc->irq);
  	kfree(desc->ubuf);
  	free_urbs(desc);
  	kfree(desc);
  }
  
  static ssize_t wdm_write
  (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
  {
  	u8 *buf;
  	int rv = -EMSGSIZE, r, we;
  	struct wdm_device *desc = file->private_data;
  	struct usb_ctrlrequest *req;
  
  	if (count > desc->wMaxCommand)
  		count = desc->wMaxCommand;
  
  	spin_lock_irq(&desc->iuspin);
  	we = desc->werr;
  	desc->werr = 0;
  	spin_unlock_irq(&desc->iuspin);
  	if (we < 0)
  		return -EIO;
5c22837ad   Oliver Neukum   USB: cdc-wdm: fix...
345
  	buf = kmalloc(count, GFP_KERNEL);
860e41a71   Oliver Neukum   usb: cdc-wdm: Fix...
346
347
348
349
350
351
352
353
354
355
356
357
358
  	if (!buf) {
  		rv = -ENOMEM;
  		goto outnl;
  	}
  
  	r = copy_from_user(buf, buffer, count);
  	if (r > 0) {
  		kfree(buf);
  		rv = -EFAULT;
  		goto outnl;
  	}
  
  	/* concurrent writes and disconnect */
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
359
  	r = mutex_lock_interruptible(&desc->wlock);
afba937e5   Oliver Neukum   USB: CDC WDM driver
360
  	rv = -ERESTARTSYS;
860e41a71   Oliver Neukum   usb: cdc-wdm: Fix...
361
362
  	if (r) {
  		kfree(buf);
afba937e5   Oliver Neukum   USB: CDC WDM driver
363
  		goto outnl;
860e41a71   Oliver Neukum   usb: cdc-wdm: Fix...
364
365
366
367
368
369
370
  	}
  
  	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
  		kfree(buf);
  		rv = -ENODEV;
  		goto outnp;
  	}
afba937e5   Oliver Neukum   USB: CDC WDM driver
371

17d80d562   Oliver Neukum   USB: autosuspend ...
372
  	r = usb_autopm_get_interface(desc->intf);
860e41a71   Oliver Neukum   usb: cdc-wdm: Fix...
373
374
  	if (r < 0) {
  		kfree(buf);
12a98b2bd   Oliver Neukum   USB: cdc-wdm: cle...
375
  		rv = usb_translate_errors(r);
17d80d562   Oliver Neukum   USB: autosuspend ...
376
  		goto outnp;
860e41a71   Oliver Neukum   usb: cdc-wdm: Fix...
377
  	}
7f1dc313d   Oliver Neukum   USB: CDC WDM driv...
378

0cdfb819b   David Sterba   USB: cdc-wdm: fix...
379
  	if (!(file->f_flags & O_NONBLOCK))
7f1dc313d   Oliver Neukum   USB: CDC WDM driv...
380
381
382
383
384
  		r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
  								&desc->flags));
  	else
  		if (test_bit(WDM_IN_USE, &desc->flags))
  			r = -EAGAIN;
880442027   Bjørn Mork   usb: cdc-wdm: mak...
385
386
387
  
  	if (test_bit(WDM_RESETTING, &desc->flags))
  		r = -EIO;
860e41a71   Oliver Neukum   usb: cdc-wdm: Fix...
388
  	if (r < 0) {
afba937e5   Oliver Neukum   USB: CDC WDM driver
389
  		kfree(buf);
12a98b2bd   Oliver Neukum   USB: cdc-wdm: cle...
390
  		rv = r;
afba937e5   Oliver Neukum   USB: CDC WDM driver
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  		goto out;
  	}
  
  	req = desc->orq;
  	usb_fill_control_urb(
  		desc->command,
  		interface_to_usbdev(desc->intf),
  		/* using common endpoint 0 */
  		usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
  		(unsigned char *)req,
  		buf,
  		count,
  		wdm_out_callback,
  		desc
  	);
  
  	req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
  			     USB_RECIP_INTERFACE);
  	req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
  	req->wValue = 0;
  	req->wIndex = desc->inum;
  	req->wLength = cpu_to_le16(count);
  	set_bit(WDM_IN_USE, &desc->flags);
5c22837ad   Oliver Neukum   USB: cdc-wdm: fix...
414
  	desc->outbuf = buf;
afba937e5   Oliver Neukum   USB: CDC WDM driver
415
416
417
418
  
  	rv = usb_submit_urb(desc->command, GFP_KERNEL);
  	if (rv < 0) {
  		kfree(buf);
5c22837ad   Oliver Neukum   USB: cdc-wdm: fix...
419
  		desc->outbuf = NULL;
afba937e5   Oliver Neukum   USB: CDC WDM driver
420
  		clear_bit(WDM_IN_USE, &desc->flags);
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
421
422
  		dev_err(&desc->intf->dev, "Tx URB error: %d
  ", rv);
12a98b2bd   Oliver Neukum   USB: cdc-wdm: cle...
423
  		rv = usb_translate_errors(rv);
afba937e5   Oliver Neukum   USB: CDC WDM driver
424
425
426
427
428
  	} else {
  		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
  			req->wIndex);
  	}
  out:
17d80d562   Oliver Neukum   USB: autosuspend ...
429
430
  	usb_autopm_put_interface(desc->intf);
  outnp:
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
431
  	mutex_unlock(&desc->wlock);
afba937e5   Oliver Neukum   USB: CDC WDM driver
432
433
434
  outnl:
  	return rv < 0 ? rv : count;
  }
8dd5cd539   Bjørn Mork   usb: cdc-wdm: avo...
435
436
437
438
439
440
441
442
443
444
445
446
447
  /*
   * clear WDM_READ flag and possibly submit the read urb if resp_count
   * is non-zero.
   *
   * Called with desc->iuspin locked
   */
  static int clear_wdm_read_flag(struct wdm_device *desc)
  {
  	int rv = 0;
  
  	clear_bit(WDM_READ, &desc->flags);
  
  	/* submit read urb only if the device is waiting for it */
f563926fe   Bjørn Mork   usb: cdc-wdm: res...
448
  	if (!desc->resp_count || !--desc->resp_count)
8dd5cd539   Bjørn Mork   usb: cdc-wdm: avo...
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
  		goto out;
  
  	set_bit(WDM_RESPONDING, &desc->flags);
  	spin_unlock_irq(&desc->iuspin);
  	rv = usb_submit_urb(desc->response, GFP_KERNEL);
  	spin_lock_irq(&desc->iuspin);
  	if (rv) {
  		dev_err(&desc->intf->dev,
  			"usb_submit_urb failed with result %d
  ", rv);
  
  		/* make sure the next notification trigger a submit */
  		clear_bit(WDM_RESPONDING, &desc->flags);
  		desc->resp_count = 0;
  	}
  out:
  	return rv;
  }
afba937e5   Oliver Neukum   USB: CDC WDM driver
467
468
469
  static ssize_t wdm_read
  (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
  {
711c68b3c   Ben Hutchings   cdc-wdm: Fix more...
470
  	int rv, cntr;
afba937e5   Oliver Neukum   USB: CDC WDM driver
471
472
  	int i = 0;
  	struct wdm_device *desc = file->private_data;
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
473
  	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
afba937e5   Oliver Neukum   USB: CDC WDM driver
474
475
  	if (rv < 0)
  		return -ERESTARTSYS;
711c68b3c   Ben Hutchings   cdc-wdm: Fix more...
476
477
  	cntr = ACCESS_ONCE(desc->length);
  	if (cntr == 0) {
afba937e5   Oliver Neukum   USB: CDC WDM driver
478
479
  		desc->read = 0;
  retry:
7f1dc313d   Oliver Neukum   USB: CDC WDM driv...
480
481
482
483
  		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
  			rv = -ENODEV;
  			goto err;
  		}
c0f5ecee4   Oliver Neukum   USB: cdc-wdm: fix...
484
485
486
487
488
  		if (test_bit(WDM_OVERFLOW, &desc->flags)) {
  			clear_bit(WDM_OVERFLOW, &desc->flags);
  			rv = -ENOBUFS;
  			goto err;
  		}
afba937e5   Oliver Neukum   USB: CDC WDM driver
489
  		i++;
7f1dc313d   Oliver Neukum   USB: CDC WDM driv...
490
491
492
493
494
495
496
497
498
499
  		if (file->f_flags & O_NONBLOCK) {
  			if (!test_bit(WDM_READ, &desc->flags)) {
  				rv = cntr ? cntr : -EAGAIN;
  				goto err;
  			}
  			rv = 0;
  		} else {
  			rv = wait_event_interruptible(desc->wait,
  				test_bit(WDM_READ, &desc->flags));
  		}
afba937e5   Oliver Neukum   USB: CDC WDM driver
500

7f1dc313d   Oliver Neukum   USB: CDC WDM driv...
501
  		/* may have happened while we slept */
17d80d562   Oliver Neukum   USB: autosuspend ...
502
503
504
505
  		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
  			rv = -ENODEV;
  			goto err;
  		}
880442027   Bjørn Mork   usb: cdc-wdm: mak...
506
507
508
509
  		if (test_bit(WDM_RESETTING, &desc->flags)) {
  			rv = -EIO;
  			goto err;
  		}
17d80d562   Oliver Neukum   USB: autosuspend ...
510
  		usb_mark_last_busy(interface_to_usbdev(desc->intf));
afba937e5   Oliver Neukum   USB: CDC WDM driver
511
512
513
514
515
516
517
518
  		if (rv < 0) {
  			rv = -ERESTARTSYS;
  			goto err;
  		}
  
  		spin_lock_irq(&desc->iuspin);
  
  		if (desc->rerr) { /* read completed, error happened */
afba937e5   Oliver Neukum   USB: CDC WDM driver
519
520
  			desc->rerr = 0;
  			spin_unlock_irq(&desc->iuspin);
afba937e5   Oliver Neukum   USB: CDC WDM driver
521
522
523
524
525
526
527
528
529
530
531
  			rv = -EIO;
  			goto err;
  		}
  		/*
  		 * recheck whether we've lost the race
  		 * against the completion handler
  		 */
  		if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
  			spin_unlock_irq(&desc->iuspin);
  			goto retry;
  		}
c0f5ecee4   Oliver Neukum   USB: cdc-wdm: fix...
532

afba937e5   Oliver Neukum   USB: CDC WDM driver
533
  		if (!desc->reslength) { /* zero length read */
b086b6b10   Bjørn Mork   USB: cdc-wdm: fix...
534
535
  			dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ
  ", __func__);
8dd5cd539   Bjørn Mork   usb: cdc-wdm: avo...
536
  			rv = clear_wdm_read_flag(desc);
afba937e5   Oliver Neukum   USB: CDC WDM driver
537
  			spin_unlock_irq(&desc->iuspin);
8dd5cd539   Bjørn Mork   usb: cdc-wdm: avo...
538
539
  			if (rv < 0)
  				goto err;
afba937e5   Oliver Neukum   USB: CDC WDM driver
540
541
  			goto retry;
  		}
711c68b3c   Ben Hutchings   cdc-wdm: Fix more...
542
  		cntr = desc->length;
afba937e5   Oliver Neukum   USB: CDC WDM driver
543
544
  		spin_unlock_irq(&desc->iuspin);
  	}
711c68b3c   Ben Hutchings   cdc-wdm: Fix more...
545
546
  	if (cntr > count)
  		cntr = count;
afba937e5   Oliver Neukum   USB: CDC WDM driver
547
548
549
550
551
  	rv = copy_to_user(buffer, desc->ubuf, cntr);
  	if (rv > 0) {
  		rv = -EFAULT;
  		goto err;
  	}
711c68b3c   Ben Hutchings   cdc-wdm: Fix more...
552
  	spin_lock_irq(&desc->iuspin);
afba937e5   Oliver Neukum   USB: CDC WDM driver
553
554
555
556
  	for (i = 0; i < desc->length - cntr; i++)
  		desc->ubuf[i] = desc->ubuf[i + cntr];
  
  	desc->length -= cntr;
87d65e54b   Oliver Neukum   USB: cdc-wdm cleanup
557
  	/* in case we had outstanding data */
8dd5cd539   Bjørn Mork   usb: cdc-wdm: avo...
558
559
560
  	if (!desc->length)
  		clear_wdm_read_flag(desc);
  	spin_unlock_irq(&desc->iuspin);
afba937e5   Oliver Neukum   USB: CDC WDM driver
561
562
563
  	rv = cntr;
  
  err:
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
564
  	mutex_unlock(&desc->rlock);
afba937e5   Oliver Neukum   USB: CDC WDM driver
565
566
567
568
569
570
571
572
  	return rv;
  }
  
  static int wdm_flush(struct file *file, fl_owner_t id)
  {
  	struct wdm_device *desc = file->private_data;
  
  	wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
6b0b79d38   Bjørn Mork   USB: cdc-wdm: can...
573
574
575
  
  	/* cannot dereference desc->intf if WDM_DISCONNECTING */
  	if (desc->werr < 0 && !test_bit(WDM_DISCONNECTING, &desc->flags))
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
576
577
578
  		dev_err(&desc->intf->dev, "Error in flush path: %d
  ",
  			desc->werr);
afba937e5   Oliver Neukum   USB: CDC WDM driver
579

24a85bae5   Oliver Neukum   USB: cdc-wdm: san...
580
  	return usb_translate_errors(desc->werr);
afba937e5   Oliver Neukum   USB: CDC WDM driver
581
582
583
584
585
586
587
588
589
590
  }
  
  static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
  {
  	struct wdm_device *desc = file->private_data;
  	unsigned long flags;
  	unsigned int mask = 0;
  
  	spin_lock_irqsave(&desc->iuspin, flags);
  	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
616b6937e   Bjørn Mork   USB: cdc-wdm: pol...
591
  		mask = POLLHUP | POLLERR;
afba937e5   Oliver Neukum   USB: CDC WDM driver
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
  		spin_unlock_irqrestore(&desc->iuspin, flags);
  		goto desc_out;
  	}
  	if (test_bit(WDM_READ, &desc->flags))
  		mask = POLLIN | POLLRDNORM;
  	if (desc->rerr || desc->werr)
  		mask |= POLLERR;
  	if (!test_bit(WDM_IN_USE, &desc->flags))
  		mask |= POLLOUT | POLLWRNORM;
  	spin_unlock_irqrestore(&desc->iuspin, flags);
  
  	poll_wait(file, &desc->wait, wait);
  
  desc_out:
  	return mask;
  }
  
  static int wdm_open(struct inode *inode, struct file *file)
  {
  	int minor = iminor(inode);
  	int rv = -ENODEV;
  	struct usb_interface *intf;
  	struct wdm_device *desc;
  
  	mutex_lock(&wdm_mutex);
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
617
618
  	desc = wdm_find_device_by_minor(minor);
  	if (!desc)
afba937e5   Oliver Neukum   USB: CDC WDM driver
619
  		goto out;
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
620
  	intf = desc->intf;
afba937e5   Oliver Neukum   USB: CDC WDM driver
621
622
  	if (test_bit(WDM_DISCONNECTING, &desc->flags))
  		goto out;
afba937e5   Oliver Neukum   USB: CDC WDM driver
623
  	file->private_data = desc;
17d80d562   Oliver Neukum   USB: autosuspend ...
624
  	rv = usb_autopm_get_interface(desc->intf);
afba937e5   Oliver Neukum   USB: CDC WDM driver
625
  	if (rv < 0) {
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
626
627
  		dev_err(&desc->intf->dev, "Error autopm - %d
  ", rv);
afba937e5   Oliver Neukum   USB: CDC WDM driver
628
629
  		goto out;
  	}
afba937e5   Oliver Neukum   USB: CDC WDM driver
630

e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
631
632
  	/* using write lock to protect desc->count */
  	mutex_lock(&desc->wlock);
17d80d562   Oliver Neukum   USB: autosuspend ...
633
  	if (!desc->count++) {
d771d8aa3   Oliver Neukum   USB: cdc-wdm: res...
634
635
  		desc->werr = 0;
  		desc->rerr = 0;
17d80d562   Oliver Neukum   USB: autosuspend ...
636
637
638
  		rv = usb_submit_urb(desc->validity, GFP_KERNEL);
  		if (rv < 0) {
  			desc->count--;
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
639
640
641
  			dev_err(&desc->intf->dev,
  				"Error submitting int urb - %d
  ", rv);
12a98b2bd   Oliver Neukum   USB: cdc-wdm: cle...
642
  			rv = usb_translate_errors(rv);
17d80d562   Oliver Neukum   USB: autosuspend ...
643
644
645
646
  		}
  	} else {
  		rv = 0;
  	}
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
647
  	mutex_unlock(&desc->wlock);
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
648
649
  	if (desc->count == 1)
  		desc->manage_power(intf, 1);
17d80d562   Oliver Neukum   USB: autosuspend ...
650
  	usb_autopm_put_interface(desc->intf);
afba937e5   Oliver Neukum   USB: CDC WDM driver
651
652
653
654
655
656
657
658
659
660
  out:
  	mutex_unlock(&wdm_mutex);
  	return rv;
  }
  
  static int wdm_release(struct inode *inode, struct file *file)
  {
  	struct wdm_device *desc = file->private_data;
  
  	mutex_lock(&wdm_mutex);
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
661
662
663
  
  	/* using write lock to protect desc->count */
  	mutex_lock(&desc->wlock);
afba937e5   Oliver Neukum   USB: CDC WDM driver
664
  	desc->count--;
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
665
  	mutex_unlock(&desc->wlock);
17d80d562   Oliver Neukum   USB: autosuspend ...
666

afba937e5   Oliver Neukum   USB: CDC WDM driver
667
  	if (!desc->count) {
880bca3a2   Bjørn Mork   USB: cdc-wdm: add...
668
  		if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
6b0b79d38   Bjørn Mork   USB: cdc-wdm: can...
669
670
  			dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
  			kill_urbs(desc);
73e06865e   Greg Suarez   USB: cdc-wdm: sup...
671
672
673
  			spin_lock_irq(&desc->iuspin);
  			desc->resp_count = 0;
  			spin_unlock_irq(&desc->iuspin);
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
674
  			desc->manage_power(desc->intf, 0);
880bca3a2   Bjørn Mork   USB: cdc-wdm: add...
675
  		} else {
6b0b79d38   Bjørn Mork   USB: cdc-wdm: can...
676
677
678
  			/* must avoid dev_printk here as desc->intf is invalid */
  			pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up
  ", __func__);
2f338c8a1   Oliver Neukum   USB: cdc-wdm: fix...
679
  			cleanup(desc);
880bca3a2   Bjørn Mork   USB: cdc-wdm: add...
680
  		}
afba937e5   Oliver Neukum   USB: CDC WDM driver
681
682
683
684
  	}
  	mutex_unlock(&wdm_mutex);
  	return 0;
  }
3edce1cf8   Bjørn Mork   USB: cdc-wdm: imp...
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
  static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
  	struct wdm_device *desc = file->private_data;
  	int rv = 0;
  
  	switch (cmd) {
  	case IOCTL_WDM_MAX_COMMAND:
  		if (copy_to_user((void __user *)arg, &desc->wMaxCommand, sizeof(desc->wMaxCommand)))
  			rv = -EFAULT;
  		break;
  	default:
  		rv = -ENOTTY;
  	}
  	return rv;
  }
afba937e5   Oliver Neukum   USB: CDC WDM driver
700
701
702
703
704
705
706
  static const struct file_operations wdm_fops = {
  	.owner =	THIS_MODULE,
  	.read =		wdm_read,
  	.write =	wdm_write,
  	.open =		wdm_open,
  	.flush =	wdm_flush,
  	.release =	wdm_release,
6038f373a   Arnd Bergmann   llseek: automatic...
707
  	.poll =		wdm_poll,
3edce1cf8   Bjørn Mork   USB: cdc-wdm: imp...
708
709
  	.unlocked_ioctl = wdm_ioctl,
  	.compat_ioctl = wdm_ioctl,
6038f373a   Arnd Bergmann   llseek: automatic...
710
  	.llseek =	noop_llseek,
afba937e5   Oliver Neukum   USB: CDC WDM driver
711
712
713
714
715
716
717
718
719
720
721
722
723
  };
  
  static struct usb_class_driver wdm_class = {
  	.name =		"cdc-wdm%d",
  	.fops =		&wdm_fops,
  	.minor_base =	WDM_MINOR_BASE,
  };
  
  /* --- error handling --- */
  static void wdm_rxwork(struct work_struct *work)
  {
  	struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
  	unsigned long flags;
6dd433e6c   Oliver Neukum   USB: cdc-wdm: fix...
724
725
  	int rv = 0;
  	int responding;
afba937e5   Oliver Neukum   USB: CDC WDM driver
726
727
728
729
730
  
  	spin_lock_irqsave(&desc->iuspin, flags);
  	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
  		spin_unlock_irqrestore(&desc->iuspin, flags);
  	} else {
6dd433e6c   Oliver Neukum   USB: cdc-wdm: fix...
731
  		responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
afba937e5   Oliver Neukum   USB: CDC WDM driver
732
  		spin_unlock_irqrestore(&desc->iuspin, flags);
6dd433e6c   Oliver Neukum   USB: cdc-wdm: fix...
733
734
  		if (!responding)
  			rv = usb_submit_urb(desc->response, GFP_KERNEL);
afba937e5   Oliver Neukum   USB: CDC WDM driver
735
736
  		if (rv < 0 && rv != -EPERM) {
  			spin_lock_irqsave(&desc->iuspin, flags);
6dd433e6c   Oliver Neukum   USB: cdc-wdm: fix...
737
  			clear_bit(WDM_RESPONDING, &desc->flags);
afba937e5   Oliver Neukum   USB: CDC WDM driver
738
739
740
741
742
743
744
745
  			if (!test_bit(WDM_DISCONNECTING, &desc->flags))
  				schedule_work(&desc->rxwork);
  			spin_unlock_irqrestore(&desc->iuspin, flags);
  		}
  	}
  }
  
  /* --- hotplug --- */
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
746
747
  static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
  		u16 bufsize, int (*manage_power)(struct usb_interface *, int))
afba937e5   Oliver Neukum   USB: CDC WDM driver
748
  {
0dffb4862   Bjørn Mork   usb: cdc-wdm: spl...
749
  	int rv = -ENOMEM;
afba937e5   Oliver Neukum   USB: CDC WDM driver
750
  	struct wdm_device *desc;
afba937e5   Oliver Neukum   USB: CDC WDM driver
751

afba937e5   Oliver Neukum   USB: CDC WDM driver
752
753
754
  	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
  	if (!desc)
  		goto out;
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
755
  	INIT_LIST_HEAD(&desc->device_list);
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
756
757
  	mutex_init(&desc->rlock);
  	mutex_init(&desc->wlock);
afba937e5   Oliver Neukum   USB: CDC WDM driver
758
759
  	spin_lock_init(&desc->iuspin);
  	init_waitqueue_head(&desc->wait);
0dffb4862   Bjørn Mork   usb: cdc-wdm: spl...
760
  	desc->wMaxCommand = bufsize;
052fbc0d7   Oliver Neukum   USB: correct erro...
761
  	/* this will be expanded and needed in hardware endianness */
afba937e5   Oliver Neukum   USB: CDC WDM driver
762
763
764
  	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
  	desc->intf = intf;
  	INIT_WORK(&desc->rxwork, wdm_rxwork);
052fbc0d7   Oliver Neukum   USB: correct erro...
765
  	rv = -EINVAL;
0dffb4862   Bjørn Mork   usb: cdc-wdm: spl...
766
  	if (!usb_endpoint_is_int_in(ep))
afba937e5   Oliver Neukum   USB: CDC WDM driver
767
  		goto err;
afba937e5   Oliver Neukum   USB: CDC WDM driver
768

29cc88979   Kuninori Morimoto   USB: use usb_endp...
769
  	desc->wMaxPacketSize = usb_endpoint_maxp(ep);
afba937e5   Oliver Neukum   USB: CDC WDM driver
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
  
  	desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
  	if (!desc->orq)
  		goto err;
  	desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
  	if (!desc->irq)
  		goto err;
  
  	desc->validity = usb_alloc_urb(0, GFP_KERNEL);
  	if (!desc->validity)
  		goto err;
  
  	desc->response = usb_alloc_urb(0, GFP_KERNEL);
  	if (!desc->response)
  		goto err;
  
  	desc->command = usb_alloc_urb(0, GFP_KERNEL);
  	if (!desc->command)
  		goto err;
  
  	desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
  	if (!desc->ubuf)
  		goto err;
8457d99ca   Bjørn Mork   USB: cdc-wdm: no ...
793
  	desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
afba937e5   Oliver Neukum   USB: CDC WDM driver
794
795
  	if (!desc->sbuf)
  		goto err;
8457d99ca   Bjørn Mork   USB: cdc-wdm: no ...
796
  	desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
afba937e5   Oliver Neukum   USB: CDC WDM driver
797
  	if (!desc->inbuf)
8457d99ca   Bjørn Mork   USB: cdc-wdm: no ...
798
  		goto err;
afba937e5   Oliver Neukum   USB: CDC WDM driver
799
800
801
802
803
804
805
806
807
808
809
  
  	usb_fill_int_urb(
  		desc->validity,
  		interface_to_usbdev(intf),
  		usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
  		desc->sbuf,
  		desc->wMaxPacketSize,
  		wdm_int_callback,
  		desc,
  		ep->bInterval
  	);
afba937e5   Oliver Neukum   USB: CDC WDM driver
810

19b85b3b8   Bjørn Mork   USB: cdc-wdm: no ...
811
812
813
814
815
816
817
818
  	desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
  	desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
  	desc->irq->wValue = 0;
  	desc->irq->wIndex = desc->inum;
  	desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
  
  	usb_fill_control_urb(
  		desc->response,
8143a8963   Bjørn Mork   USB: cdc-wdm: kil...
819
  		interface_to_usbdev(intf),
19b85b3b8   Bjørn Mork   USB: cdc-wdm: no ...
820
821
822
823
824
825
826
827
  		/* using common endpoint 0 */
  		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
  		(unsigned char *)desc->irq,
  		desc->inbuf,
  		desc->wMaxCommand,
  		wdm_in_callback,
  		desc
  	);
19b85b3b8   Bjørn Mork   USB: cdc-wdm: no ...
828

3cc361574   Bjørn Mork   usb: cdc-wdm: add...
829
  	desc->manage_power = manage_power;
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
830
831
832
  	spin_lock(&wdm_device_list_lock);
  	list_add(&desc->device_list, &wdm_device_list);
  	spin_unlock(&wdm_device_list_lock);
afba937e5   Oliver Neukum   USB: CDC WDM driver
833
  	rv = usb_register_dev(intf, &wdm_class);
afba937e5   Oliver Neukum   USB: CDC WDM driver
834
  	if (rv < 0)
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
835
  		goto err;
052fbc0d7   Oliver Neukum   USB: correct erro...
836
  	else
820c629a5   Bjørn Mork   USB: cdc-wdm: avo...
837
838
  		dev_info(&intf->dev, "%s: USB WDM device
  ", dev_name(intf->usb_dev));
afba937e5   Oliver Neukum   USB: CDC WDM driver
839
840
  out:
  	return rv;
afba937e5   Oliver Neukum   USB: CDC WDM driver
841
  err:
6286d85e8   Bjørn Mork   USB: cdc-wdm: rem...
842
843
844
  	spin_lock(&wdm_device_list_lock);
  	list_del(&desc->device_list);
  	spin_unlock(&wdm_device_list_lock);
0dffb4862   Bjørn Mork   usb: cdc-wdm: spl...
845
846
847
  	cleanup(desc);
  	return rv;
  }
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
848
849
850
851
  static int wdm_manage_power(struct usb_interface *intf, int on)
  {
  	/* need autopm_get/put here to ensure the usbcore sees the new value */
  	int rv = usb_autopm_get_interface(intf);
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
852
853
  
  	intf->needs_remote_wakeup = on;
4144bc861   Bjørn Mork   usb: cdc-wdm: man...
854
855
856
  	if (!rv)
  		usb_autopm_put_interface(intf);
  	return 0;
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
857
  }
0dffb4862   Bjørn Mork   usb: cdc-wdm: spl...
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
  static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
  {
  	int rv = -EINVAL;
  	struct usb_host_interface *iface;
  	struct usb_endpoint_descriptor *ep;
  	struct usb_cdc_dmm_desc *dmhd;
  	u8 *buffer = intf->altsetting->extra;
  	int buflen = intf->altsetting->extralen;
  	u16 maxcom = WDM_DEFAULT_BUFSIZE;
  
  	if (!buffer)
  		goto err;
  	while (buflen > 2) {
  		if (buffer[1] != USB_DT_CS_INTERFACE) {
  			dev_err(&intf->dev, "skipping garbage
  ");
  			goto next_desc;
  		}
  
  		switch (buffer[2]) {
  		case USB_CDC_HEADER_TYPE:
  			break;
  		case USB_CDC_DMM_TYPE:
  			dmhd = (struct usb_cdc_dmm_desc *)buffer;
  			maxcom = le16_to_cpu(dmhd->wMaxCommand);
  			dev_dbg(&intf->dev,
  				"Finding maximum buffer length: %d", maxcom);
  			break;
  		default:
  			dev_err(&intf->dev,
  				"Ignoring extra header, type %d, length %d
  ",
  				buffer[2], buffer[0]);
  			break;
  		}
  next_desc:
  		buflen -= buffer[0];
  		buffer += buffer[0];
  	}
  
  	iface = intf->cur_altsetting;
  	if (iface->desc.bNumEndpoints != 1)
  		goto err;
  	ep = &iface->endpoint[0].desc;
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
902
  	rv = wdm_create(intf, ep, maxcom, &wdm_manage_power);
0dffb4862   Bjørn Mork   usb: cdc-wdm: spl...
903
904
  
  err:
afba937e5   Oliver Neukum   USB: CDC WDM driver
905
906
  	return rv;
  }
3cc361574   Bjørn Mork   usb: cdc-wdm: add...
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
  /**
   * usb_cdc_wdm_register - register a WDM subdriver
   * @intf: usb interface the subdriver will associate with
   * @ep: interrupt endpoint to monitor for notifications
   * @bufsize: maximum message size to support for read/write
   *
   * Create WDM usb class character device and associate it with intf
   * without binding, allowing another driver to manage the interface.
   *
   * The subdriver will manage the given interrupt endpoint exclusively
   * and will issue control requests referring to the given intf. It
   * will otherwise avoid interferring, and in particular not do
   * usb_set_intfdata/usb_get_intfdata on intf.
   *
   * The return value is a pointer to the subdriver's struct usb_driver.
   * The registering driver is responsible for calling this subdriver's
   * disconnect, suspend, resume, pre_reset and post_reset methods from
   * its own.
   */
  struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf,
  					struct usb_endpoint_descriptor *ep,
  					int bufsize,
  					int (*manage_power)(struct usb_interface *, int))
  {
  	int rv = -EINVAL;
  
  	rv = wdm_create(intf, ep, bufsize, manage_power);
  	if (rv < 0)
  		goto err;
  
  	return &wdm_driver;
  err:
  	return ERR_PTR(rv);
  }
  EXPORT_SYMBOL(usb_cdc_wdm_register);
afba937e5   Oliver Neukum   USB: CDC WDM driver
942
943
944
945
946
947
  static void wdm_disconnect(struct usb_interface *intf)
  {
  	struct wdm_device *desc;
  	unsigned long flags;
  
  	usb_deregister_dev(intf, &wdm_class);
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
948
  	desc = wdm_find_device(intf);
afba937e5   Oliver Neukum   USB: CDC WDM driver
949
  	mutex_lock(&wdm_mutex);
afba937e5   Oliver Neukum   USB: CDC WDM driver
950
951
952
953
954
  
  	/* the spinlock makes sure no new urbs are generated in the callbacks */
  	spin_lock_irqsave(&desc->iuspin, flags);
  	set_bit(WDM_DISCONNECTING, &desc->flags);
  	set_bit(WDM_READ, &desc->flags);
17d80d562   Oliver Neukum   USB: autosuspend ...
955
  	/* to terminate pending flushes */
afba937e5   Oliver Neukum   USB: CDC WDM driver
956
957
  	clear_bit(WDM_IN_USE, &desc->flags);
  	spin_unlock_irqrestore(&desc->iuspin, flags);
62aaf24dc   Bjørn Mork   USB: cdc-wdm: cal...
958
  	wake_up_all(&desc->wait);
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
959
960
  	mutex_lock(&desc->rlock);
  	mutex_lock(&desc->wlock);
afba937e5   Oliver Neukum   USB: CDC WDM driver
961
  	kill_urbs(desc);
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
962
  	cancel_work_sync(&desc->rxwork);
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
963
964
  	mutex_unlock(&desc->wlock);
  	mutex_unlock(&desc->rlock);
6286d85e8   Bjørn Mork   USB: cdc-wdm: rem...
965
966
967
968
969
  
  	/* the desc->intf pointer used as list key is now invalid */
  	spin_lock(&wdm_device_list_lock);
  	list_del(&desc->device_list);
  	spin_unlock(&wdm_device_list_lock);
afba937e5   Oliver Neukum   USB: CDC WDM driver
970
971
  	if (!desc->count)
  		cleanup(desc);
880bca3a2   Bjørn Mork   USB: cdc-wdm: add...
972
973
974
  	else
  		dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup
  ", __func__, desc->count);
afba937e5   Oliver Neukum   USB: CDC WDM driver
975
976
  	mutex_unlock(&wdm_mutex);
  }
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
977
  #ifdef CONFIG_PM
17d80d562   Oliver Neukum   USB: autosuspend ...
978
979
  static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
  {
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
980
  	struct wdm_device *desc = wdm_find_device(intf);
17d80d562   Oliver Neukum   USB: autosuspend ...
981
982
983
984
  	int rv = 0;
  
  	dev_dbg(&desc->intf->dev, "wdm%d_suspend
  ", intf->minor);
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
985
  	/* if this is an autosuspend the caller does the locking */
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
986
987
988
989
  	if (!PMSG_IS_AUTO(message)) {
  		mutex_lock(&desc->rlock);
  		mutex_lock(&desc->wlock);
  	}
62e668547   Oliver Neukum   usb: cdc-wdm:Fix ...
990
  	spin_lock_irq(&desc->iuspin);
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
991

5b1b0b812   Alan Stern   PM / Runtime: Add...
992
  	if (PMSG_IS_AUTO(message) &&
922a5eadd   Oliver Neukum   usb: cdc-wdm: Fix...
993
994
  			(test_bit(WDM_IN_USE, &desc->flags)
  			|| test_bit(WDM_RESPONDING, &desc->flags))) {
62e668547   Oliver Neukum   usb: cdc-wdm:Fix ...
995
  		spin_unlock_irq(&desc->iuspin);
17d80d562   Oliver Neukum   USB: autosuspend ...
996
997
  		rv = -EBUSY;
  	} else {
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
998

beb1d35f1   Oliver Neukum   usb: cdc-wdm: Fix...
999
  		set_bit(WDM_SUSPENDING, &desc->flags);
62e668547   Oliver Neukum   usb: cdc-wdm:Fix ...
1000
  		spin_unlock_irq(&desc->iuspin);
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
1001
  		/* callback submits work - order is essential */
17d80d562   Oliver Neukum   USB: autosuspend ...
1002
  		kill_urbs(desc);
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
1003
  		cancel_work_sync(&desc->rxwork);
17d80d562   Oliver Neukum   USB: autosuspend ...
1004
  	}
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
1005
1006
1007
1008
  	if (!PMSG_IS_AUTO(message)) {
  		mutex_unlock(&desc->wlock);
  		mutex_unlock(&desc->rlock);
  	}
17d80d562   Oliver Neukum   USB: autosuspend ...
1009
1010
1011
  
  	return rv;
  }
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
1012
  #endif
17d80d562   Oliver Neukum   USB: autosuspend ...
1013
1014
1015
1016
1017
1018
1019
1020
  
  static int recover_from_urb_loss(struct wdm_device *desc)
  {
  	int rv = 0;
  
  	if (desc->count) {
  		rv = usb_submit_urb(desc->validity, GFP_NOIO);
  		if (rv < 0)
9908a32e9   Greg Kroah-Hartman   USB: remove err()...
1021
1022
1023
  			dev_err(&desc->intf->dev,
  				"Error resume submitting int urb - %d
  ", rv);
17d80d562   Oliver Neukum   USB: autosuspend ...
1024
1025
1026
  	}
  	return rv;
  }
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
1027
1028
  
  #ifdef CONFIG_PM
17d80d562   Oliver Neukum   USB: autosuspend ...
1029
1030
  static int wdm_resume(struct usb_interface *intf)
  {
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
1031
  	struct wdm_device *desc = wdm_find_device(intf);
17d80d562   Oliver Neukum   USB: autosuspend ...
1032
1033
1034
1035
  	int rv;
  
  	dev_dbg(&desc->intf->dev, "wdm%d_resume
  ", intf->minor);
338124c1f   Oliver Neukum   usb: cdc-wdm: Fix...
1036

beb1d35f1   Oliver Neukum   usb: cdc-wdm: Fix...
1037
  	clear_bit(WDM_SUSPENDING, &desc->flags);
62e668547   Oliver Neukum   usb: cdc-wdm:Fix ...
1038
  	rv = recover_from_urb_loss(desc);
338124c1f   Oliver Neukum   usb: cdc-wdm: Fix...
1039

17d80d562   Oliver Neukum   USB: autosuspend ...
1040
1041
  	return rv;
  }
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
1042
  #endif
17d80d562   Oliver Neukum   USB: autosuspend ...
1043
1044
1045
  
  static int wdm_pre_reset(struct usb_interface *intf)
  {
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
1046
  	struct wdm_device *desc = wdm_find_device(intf);
17d80d562   Oliver Neukum   USB: autosuspend ...
1047

d771d8aa3   Oliver Neukum   USB: cdc-wdm: res...
1048
1049
1050
1051
1052
1053
1054
  	/*
  	 * we notify everybody using poll of
  	 * an exceptional situation
  	 * must be done before recovery lest a spontaneous
  	 * message from the device is lost
  	 */
  	spin_lock_irq(&desc->iuspin);
880442027   Bjørn Mork   usb: cdc-wdm: mak...
1055
1056
1057
  	set_bit(WDM_RESETTING, &desc->flags);	/* inform read/write */
  	set_bit(WDM_READ, &desc->flags);	/* unblock read */
  	clear_bit(WDM_IN_USE, &desc->flags);	/* unblock write */
d771d8aa3   Oliver Neukum   USB: cdc-wdm: res...
1058
1059
1060
  	desc->rerr = -EINTR;
  	spin_unlock_irq(&desc->iuspin);
  	wake_up_all(&desc->wait);
880442027   Bjørn Mork   usb: cdc-wdm: mak...
1061
1062
1063
1064
  	mutex_lock(&desc->rlock);
  	mutex_lock(&desc->wlock);
  	kill_urbs(desc);
  	cancel_work_sync(&desc->rxwork);
17d80d562   Oliver Neukum   USB: autosuspend ...
1065
1066
1067
1068
1069
  	return 0;
  }
  
  static int wdm_post_reset(struct usb_interface *intf)
  {
b0c138608   Bjørn Mork   usb: cdc-wdm: add...
1070
  	struct wdm_device *desc = wdm_find_device(intf);
17d80d562   Oliver Neukum   USB: autosuspend ...
1071
  	int rv;
c0f5ecee4   Oliver Neukum   USB: cdc-wdm: fix...
1072
  	clear_bit(WDM_OVERFLOW, &desc->flags);
880442027   Bjørn Mork   usb: cdc-wdm: mak...
1073
  	clear_bit(WDM_RESETTING, &desc->flags);
17d80d562   Oliver Neukum   USB: autosuspend ...
1074
  	rv = recover_from_urb_loss(desc);
e8537bd2c   Bjørn Mork   USB: cdc-wdm: use...
1075
1076
  	mutex_unlock(&desc->wlock);
  	mutex_unlock(&desc->rlock);
17d80d562   Oliver Neukum   USB: autosuspend ...
1077
1078
  	return 0;
  }
afba937e5   Oliver Neukum   USB: CDC WDM driver
1079
1080
1081
1082
  static struct usb_driver wdm_driver = {
  	.name =		"cdc_wdm",
  	.probe =	wdm_probe,
  	.disconnect =	wdm_disconnect,
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
1083
  #ifdef CONFIG_PM
17d80d562   Oliver Neukum   USB: autosuspend ...
1084
1085
1086
  	.suspend =	wdm_suspend,
  	.resume =	wdm_resume,
  	.reset_resume =	wdm_resume,
d93d16e9a   Oliver Neukum   usb: cdc-wdm: Fix...
1087
  #endif
17d80d562   Oliver Neukum   USB: autosuspend ...
1088
1089
  	.pre_reset =	wdm_pre_reset,
  	.post_reset =	wdm_post_reset,
afba937e5   Oliver Neukum   USB: CDC WDM driver
1090
  	.id_table =	wdm_ids,
17d80d562   Oliver Neukum   USB: autosuspend ...
1091
  	.supports_autosuspend = 1,
e1f12eb6b   Sarah Sharp   USB: Disable hub-...
1092
  	.disable_hub_initiated_lpm = 1,
afba937e5   Oliver Neukum   USB: CDC WDM driver
1093
  };
65db43054   Greg Kroah-Hartman   USB: convert driv...
1094
  module_usb_driver(wdm_driver);
afba937e5   Oliver Neukum   USB: CDC WDM driver
1095
1096
  
  MODULE_AUTHOR(DRIVER_AUTHOR);
87d65e54b   Oliver Neukum   USB: cdc-wdm cleanup
1097
  MODULE_DESCRIPTION(DRIVER_DESC);
afba937e5   Oliver Neukum   USB: CDC WDM driver
1098
  MODULE_LICENSE("GPL");