aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/class/cdc-wdm.c
blob: 3771d6e6d0ccb95587c2bf3ca9de5e6fdf492348 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
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
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
/*
 * cdc-wdm.c
 *
 * This driver supports USB CDC WCM Device Management.
 *
 * Copyright (c) 2007-2008 Oliver Neukum
 *
 * Some code taken from cdc-acm.c
 *
 * Released under the GPLv2.
 *
 * Many thanks to Carl Nordbeck
 */
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#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>

/*
 * Version Information
 */
#define DRIVER_VERSION "v0.03"
#define DRIVER_AUTHOR "Oliver Neukum"
#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"

static struct usb_device_id wdm_ids[] = {
	{
		.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
				 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
		.bInterfaceClass = USB_CLASS_COMM,
		.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
	},
	{ }
};

MODULE_DEVICE_TABLE (usb, wdm_ids);

#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


#define WDM_MAX			16


static DEFINE_MUTEX(wdm_mutex);

/* --- 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;
	u16			bMaxPacketSize0;
	__le16			inum;
	int			reslength;
	int			length;
	int			read;
	int			count;
	dma_addr_t		shandle;
	dma_addr_t		ihandle;
	struct mutex		wlock;
	struct mutex		rlock;
	struct mutex		plock;
	wait_queue_head_t	wait;
	struct work_struct	rxwork;
	int			werr;
	int			rerr;
};

static struct usb_driver wdm_driver;

/* --- 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);
	clear_bit(WDM_IN_USE, &desc->flags);
	kfree(desc->outbuf);
	wake_up(&desc->wait);
}

static void wdm_in_callback(struct urb *urb)
{
	struct wdm_device *desc = urb->context;
	int status = urb->status;

	spin_lock(&desc->iuspin);

	if (status) {
		switch (status) {
		case -ENOENT:
			dev_dbg(&desc->intf->dev,
				"nonzero urb status received: -ENOENT");
			break;
		case -ECONNRESET:
			dev_dbg(&desc->intf->dev,
				"nonzero urb status received: -ECONNRESET");
			break;
		case -ESHUTDOWN:
			dev_dbg(&desc->intf->dev,
				"nonzero urb status received: -ESHUTDOWN");
			break;
		case -EPIPE:
			dev_err(&desc->intf->dev,
				"nonzero urb status received: -EPIPE\n");
			break;
		default:
			dev_err(&desc->intf->dev,
				"Unexpected error %d\n", status);
			break;
		}
	}

	desc->rerr = status;
	desc->reslength = urb->actual_length;
	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
	desc->length += desc->reslength;
	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;
	int status = urb->status;
	struct wdm_device *desc;
	struct usb_ctrlrequest *req;
	struct usb_cdc_notification *dr;

	desc = urb->context;
	req = desc->irq;
	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);
			dev_err(&desc->intf->dev, "Stall on int endpoint\n");
			goto sw; /* halt is cleared in work */
		default:
			dev_err(&desc->intf->dev,
				"nonzero urb status received: %d\n", status);
			break;
		}
	}

	if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
		dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
			urb->actual_length);
		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;
	default:
		clear_bit(WDM_POLL_RUNNING, &desc->flags);
		dev_err(&desc->intf->dev,
			"unknown notification %d received: index %d len %d\n",
			dr->bNotificationType, dr->wIndex, dr->wLength);
		goto exit;
	}

	req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
	req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
	req->wValue = 0;
	req->wIndex = desc->inum;
	req->wLength = cpu_to_le16(desc->wMaxCommand);

	usb_fill_control_urb(
		desc->response,
		interface_to_usbdev(desc->intf),
		/* using common endpoint 0 */
		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
		(unsigned char *)req,
		desc->inbuf,
		desc->wMaxCommand,
		wdm_in_callback,
		desc
	);
	desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	spin_lock(&desc->iuspin);
	clear_bit(WDM_READ, &desc->flags);
	if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
		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) {
		if (rv == -EPERM)
			return;
		if (rv == -ENOMEM) {
sw:
			rv = schedule_work(&desc->rxwork);
			if (rv)
				dev_err(&desc->intf->dev,
					"Cannot schedule work\n");
		}
	}
exit:
	rv = usb_submit_urb(urb, GFP_ATOMIC);
	if (rv)
		dev_err(&desc->intf->dev,
			"%s - usb_submit_urb failed with result %d\n",
			__func__, rv);

}

static void kill_urbs(struct wdm_device *desc)
{
	/* the order here is essential */
	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)
{
	usb_buffer_free(interface_to_usbdev(desc->intf),
			desc->wMaxPacketSize,
			desc->sbuf,
			desc->validity->transfer_dma);
	usb_buffer_free(interface_to_usbdev(desc->intf),
			desc->wMaxCommand,
			desc->inbuf,
			desc->response->transfer_dma);
	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;

	r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */
	rv = -ERESTARTSYS;
	if (r)
		goto outnl;

	r = usb_autopm_get_interface(desc->intf);
	if (r < 0)
		goto outnp;
	r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
							   &desc->flags));
	if (r < 0)
		goto out;

	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
		rv = -ENODEV;
		goto out;
	}

	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
	if (!buf) {
		rv = -ENOMEM;
		goto out;
	}

	r = copy_from_user(buf, buffer, count);
	if (r > 0) {
		kfree(buf);
		rv = -EFAULT;
		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);

	rv = usb_submit_urb(desc->command, GFP_KERNEL);
	if (rv < 0) {
		kfree(buf);
		clear_bit(WDM_IN_USE, &desc->flags);
		dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
	} else {
		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
			req->wIndex);
	}
out:
	usb_autopm_put_interface(desc->intf);
outnp:
	mutex_unlock(&desc->wlock);
outnl:
	return rv < 0 ? rv : count;
}

static ssize_t wdm_read
(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
	int rv, cntr;
	int i = 0;
	struct wdm_device *desc = file->private_data;


	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
	if (rv < 0)
		return -ERESTARTSYS;

	if (desc->length == 0) {
		desc->read = 0;
retry:
		i++;
		rv = wait_event_interruptible(desc->wait,
					      test_bit(WDM_READ, &desc->flags));

		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
			rv = -ENODEV;
			goto err;
		}
		usb_mark_last_busy(interface_to_usbdev(desc->intf));
		if (rv < 0) {
			rv = -ERESTARTSYS;
			goto err;
		}

		spin_lock_irq(&desc->iuspin);

		if (desc->rerr) { /* read completed, error happened */
			int t = desc->rerr;
			desc->rerr = 0;
			spin_unlock_irq(&desc->iuspin);
			dev_err(&desc->intf->dev,
				"reading had resulted in %d\n", t);
			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;
		}
		if (!desc->reslength) { /* zero length read */
			spin_unlock_irq(&desc->iuspin);
			goto retry;
		}
		clear_bit(WDM_READ, &desc->flags);
		spin_unlock_irq(&desc->iuspin);
	}

	cntr = count > desc->length ? desc->length : count;
	rv = copy_to_user(buffer, desc->ubuf, cntr);
	if (rv > 0) {
		rv = -EFAULT;
		goto err;
	}

	for (i = 0; i < desc->length - cntr; i++)
		desc->ubuf[i] = desc->ubuf[i + cntr];

	desc->length -= cntr;
	/* in case we had outstanding data */
	if (!desc->length)
		clear_bit(WDM_READ, &desc->flags);
	rv = cntr;

err:
	mutex_unlock(&desc->rlock);
	if (rv < 0)
		dev_err(&desc->intf->dev, "wdm_read: exit error\n");
	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));
	if (desc->werr < 0)
		dev_err(&desc->intf->dev, "Error in flush path: %d\n",
			desc->werr);

	return desc->werr;
}

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)) {
		mask = POLLERR;
		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);
	intf = usb_find_interface(&wdm_driver, minor);
	if (!intf)
		goto out;

	desc = usb_get_intfdata(intf);
	if (test_bit(WDM_DISCONNECTING, &desc->flags))
		goto out;

	;
	file->private_data = desc;

	rv = usb_autopm_get_interface(desc->intf);
	if (rv < 0) {
		dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
		goto out;
	}
	intf->needs_remote_wakeup = 1;

	mutex_lock(&desc->plock);
	if (!desc->count++) {
		rv = usb_submit_urb(desc->validity, GFP_KERNEL);
		if (rv < 0) {
			desc->count--;
			dev_err(&desc->intf->dev,
				"Error submitting int urb - %d\n", rv);
		}
	} else {
		rv = 0;
	}
	mutex_unlock(&desc->plock);
	usb_autopm_put_interface(desc->intf);
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);
	mutex_lock(&desc->plock);
	desc->count--;
	mutex_unlock(&desc->plock);

	if (!desc->count) {
		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
		kill_urbs(desc);
		if (!test_bit(WDM_DISCONNECTING, &desc->flags))
			desc->intf->needs_remote_wakeup = 0;
	}
	mutex_unlock(&wdm_mutex);
	return 0;
}

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,
	.poll =		wdm_poll
};

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;
	int rv;

	spin_lock_irqsave(&desc->iuspin, flags);
	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
		spin_unlock_irqrestore(&desc->iuspin, flags);
	} else {
		spin_unlock_irqrestore(&desc->iuspin, flags);
		rv = usb_submit_urb(desc->response, GFP_KERNEL);
		if (rv < 0 && rv != -EPERM) {
			spin_lock_irqsave(&desc->iuspin, flags);
			if (!test_bit(WDM_DISCONNECTING, &desc->flags))
				schedule_work(&desc->rxwork);
			spin_unlock_irqrestore(&desc->iuspin, flags);
		}
	}
}

/* --- hotplug --- */

static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	int rv = -EINVAL;
	struct usb_device *udev = interface_to_usbdev(intf);
	struct wdm_device *desc;
	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 = 0;

	if (!buffer)
		goto out;

	while (buflen > 0) {
		if (buffer [1] != USB_DT_CS_INTERFACE) {
			dev_err(&intf->dev, "skipping garbage\n");
			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\n",
				buffer[2], buffer[0]);
			break;
		}
next_desc:
		buflen -= buffer[0];
		buffer += buffer[0];
	}

	rv = -ENOMEM;
	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
	if (!desc)
		goto out;
	mutex_init(&desc->wlock);
	mutex_init(&desc->rlock);
	mutex_init(&desc->plock);
	spin_lock_init(&desc->iuspin);
	init_waitqueue_head(&desc->wait);
	desc->wMaxCommand = maxcom;
	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
	desc->intf = intf;
	INIT_WORK(&desc->rxwork, wdm_rxwork);

	iface = &intf->altsetting[0];
	ep = &iface->endpoint[0].desc;
	if (!usb_endpoint_is_int_in(ep)) {
		rv = -EINVAL;
		goto err;
	}

	desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
	desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;

	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;

	desc->sbuf = usb_buffer_alloc(interface_to_usbdev(intf),
					desc->wMaxPacketSize,
					GFP_KERNEL,
					&desc->validity->transfer_dma);
	if (!desc->sbuf)
		goto err;

	desc->inbuf = usb_buffer_alloc(interface_to_usbdev(intf),
					desc->bMaxPacketSize0,
					GFP_KERNEL,
					&desc->response->transfer_dma);
	if (!desc->inbuf)
		goto err2;

	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
	);
	desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	usb_set_intfdata(intf, desc);
	rv = usb_register_dev(intf, &wdm_class);
	dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
		 intf->minor - WDM_MINOR_BASE);
	if (rv < 0)
		goto err;
out:
	return rv;
err2:
	usb_buffer_free(interface_to_usbdev(desc->intf),
			desc->wMaxPacketSize,
			desc->sbuf,
			desc->validity->transfer_dma);
err:
	free_urbs(desc);
	kfree(desc->ubuf);
	kfree(desc->orq);
	kfree(desc->irq);
	kfree(desc);
	return rv;
}

static void wdm_disconnect(struct usb_interface *intf)
{
	struct wdm_device *desc;
	unsigned long flags;

	usb_deregister_dev(intf, &wdm_class);
	mutex_lock(&wdm_mutex);
	desc = usb_get_intfdata(intf);

	/* 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);
	/* to terminate pending flushes */
	clear_bit(WDM_IN_USE, &desc->flags);
	spin_unlock_irqrestore(&desc->iuspin, flags);
	cancel_work_sync(&desc->rxwork);
	kill_urbs(desc);
	wake_up_all(&desc->wait);
	if (!desc->count)
		cleanup(desc);
	mutex_unlock(&wdm_mutex);
}

static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct wdm_device *desc = usb_get_intfdata(intf);
	int rv = 0;

	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);

	mutex_lock(&desc->plock);
#ifdef CONFIG_PM
	if ((message.event & PM_EVENT_AUTO) &&
			test_bit(WDM_IN_USE, &desc->flags)) {
		rv = -EBUSY;
	} else {
#endif
		cancel_work_sync(&desc->rxwork);
		kill_urbs(desc);
#ifdef CONFIG_PM
	}
#endif
	mutex_unlock(&desc->plock);

	return rv;
}

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)
			dev_err(&desc->intf->dev,
				"Error resume submitting int urb - %d\n", rv);
	}
	return rv;
}
static int wdm_resume(struct usb_interface *intf)
{
	struct wdm_device *desc = usb_get_intfdata(intf);
	int rv;

	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
	mutex_lock(&desc->plock);
	rv = recover_from_urb_loss(desc);
	mutex_unlock(&desc->plock);
	return rv;
}

static int wdm_pre_reset(struct usb_interface *intf)
{
	struct wdm_device *desc = usb_get_intfdata(intf);

	mutex_lock(&desc->plock);
	return 0;
}

static int wdm_post_reset(struct usb_interface *intf)
{
	struct wdm_device *desc = usb_get_intfdata(intf);
	int rv;

	rv = recover_from_urb_loss(desc);
	mutex_unlock(&desc->plock);
	return 0;
}

static struct usb_driver wdm_driver = {
	.name =		"cdc_wdm",
	.probe =	wdm_probe,
	.disconnect =	wdm_disconnect,
	.suspend =	wdm_suspend,
	.resume =	wdm_resume,
	.reset_resume =	wdm_resume,
	.pre_reset =	wdm_pre_reset,
	.post_reset =	wdm_post_reset,
	.id_table =	wdm_ids,
	.supports_autosuspend = 1,
};

/* --- low level module stuff --- */

static int __init wdm_init(void)
{
	int rv;

	rv = usb_register(&wdm_driver);

	return rv;
}

static void __exit wdm_exit(void)
{
	usb_deregister(&wdm_driver);
}

module_init(wdm_init);
module_exit(wdm_exit);

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
ass="hl opt">.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = 1, }; static const struct usb_interface_descriptor gs_bulk_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GS_BULK_INTERFACE_ID, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_CDC_DATA, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = GS_DATA_STR_ID, }; static const struct usb_interface_descriptor gs_control_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GS_CONTROL_INTERFACE_ID, .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, .iInterface = GS_CONTROL_STR_ID, }; static const struct usb_interface_descriptor gs_data_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GS_DATA_INTERFACE_ID, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_CDC_DATA, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = GS_DATA_STR_ID, }; static const struct usb_cdc_header_desc gs_header_desc = { .bLength = sizeof(gs_header_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, .bcdCDC = __constant_cpu_to_le16(0x0110), }; static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = { .bLength = sizeof(gs_call_mgmt_descriptor), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, .bmCapabilities = 0, .bDataInterface = 1, /* index of data interface */ }; static struct usb_cdc_acm_descriptor gs_acm_descriptor = { .bLength = sizeof(gs_acm_descriptor), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ACM_TYPE, .bmCapabilities = 0, }; static const struct usb_cdc_union_desc gs_union_desc = { .bLength = sizeof(gs_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_UNION_TYPE, .bMasterInterface0 = 0, /* index of control interface */ .bSlaveInterface0 = 1, /* index of data interface */ }; static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, }; static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; static struct usb_endpoint_descriptor gs_fullspeed_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; static const struct usb_descriptor_header *gs_bulk_fullspeed_function[] = { (struct usb_descriptor_header *) &gs_otg_descriptor, (struct usb_descriptor_header *) &gs_bulk_interface_desc, (struct usb_descriptor_header *) &gs_fullspeed_in_desc, (struct usb_descriptor_header *) &gs_fullspeed_out_desc, NULL, }; static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = { (struct usb_descriptor_header *) &gs_otg_descriptor, (struct usb_descriptor_header *) &gs_control_interface_desc, (struct usb_descriptor_header *) &gs_header_desc, (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, (struct usb_descriptor_header *) &gs_acm_descriptor, (struct usb_descriptor_header *) &gs_union_desc, (struct usb_descriptor_header *) &gs_fullspeed_notify_desc, (struct usb_descriptor_header *) &gs_data_interface_desc, (struct usb_descriptor_header *) &gs_fullspeed_in_desc, (struct usb_descriptor_header *) &gs_fullspeed_out_desc, NULL, }; #ifdef CONFIG_USB_GADGET_DUALSPEED static struct usb_endpoint_descriptor gs_highspeed_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, }; static struct usb_endpoint_descriptor gs_highspeed_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16(512), }; static struct usb_endpoint_descriptor gs_highspeed_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16(512), }; static struct usb_qualifier_descriptor gs_qualifier_desc = { .bLength = sizeof(struct usb_qualifier_descriptor), .bDescriptorType = USB_DT_DEVICE_QUALIFIER, .bcdUSB = __constant_cpu_to_le16 (0x0200), /* assumes ep0 uses the same value for both speeds ... */ .bNumConfigurations = GS_NUM_CONFIGS, }; static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = { (struct usb_descriptor_header *) &gs_otg_descriptor, (struct usb_descriptor_header *) &gs_bulk_interface_desc, (struct usb_descriptor_header *) &gs_highspeed_in_desc, (struct usb_descriptor_header *) &gs_highspeed_out_desc, NULL, }; static const struct usb_descriptor_header *gs_acm_highspeed_function[] = { (struct usb_descriptor_header *) &gs_otg_descriptor, (struct usb_descriptor_header *) &gs_control_interface_desc, (struct usb_descriptor_header *) &gs_header_desc, (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, (struct usb_descriptor_header *) &gs_acm_descriptor, (struct usb_descriptor_header *) &gs_union_desc, (struct usb_descriptor_header *) &gs_highspeed_notify_desc, (struct usb_descriptor_header *) &gs_data_interface_desc, (struct usb_descriptor_header *) &gs_highspeed_in_desc, (struct usb_descriptor_header *) &gs_highspeed_out_desc, NULL, }; #endif /* CONFIG_USB_GADGET_DUALSPEED */ /* Module */ MODULE_DESCRIPTION(GS_LONG_NAME); MODULE_AUTHOR("Al Borchers"); MODULE_LICENSE("GPL"); #ifdef GS_DEBUG module_param(debug, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on"); #endif module_param(read_q_size, uint, S_IRUGO); MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32"); module_param(write_q_size, uint, S_IRUGO); MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32"); module_param(write_buf_size, uint, S_IRUGO); MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192"); module_param(use_acm, uint, S_IRUGO); MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no"); module_init(gs_module_init); module_exit(gs_module_exit); /* * gs_module_init * * Register as a USB gadget driver and a tty driver. */ static int __init gs_module_init(void) { int i; int retval; retval = usb_gadget_register_driver(&gs_gadget_driver); if (retval) { printk(KERN_ERR "gs_module_init: cannot register gadget driver, ret=%d\n", retval); return retval; } gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS); if (!gs_tty_driver) return -ENOMEM; gs_tty_driver->owner = THIS_MODULE; gs_tty_driver->driver_name = GS_SHORT_NAME; gs_tty_driver->name = "ttygs"; gs_tty_driver->major = GS_MAJOR; gs_tty_driver->minor_start = GS_MINOR_START; gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; gs_tty_driver->init_termios = tty_std_termios; gs_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(gs_tty_driver, &gs_tty_ops); for (i=0; i < GS_NUM_PORTS; i++) sema_init(&gs_open_close_sem[i], 1); retval = tty_register_driver(gs_tty_driver); if (retval) { usb_gadget_unregister_driver(&gs_gadget_driver); put_tty_driver(gs_tty_driver); printk(KERN_ERR "gs_module_init: cannot register tty driver, ret=%d\n", retval); return retval; } printk(KERN_INFO "gs_module_init: %s %s loaded\n", GS_LONG_NAME, GS_VERSION_STR); return 0; } /* * gs_module_exit * * Unregister as a tty driver and a USB gadget driver. */ static void __exit gs_module_exit(void) { tty_unregister_driver(gs_tty_driver); put_tty_driver(gs_tty_driver); usb_gadget_unregister_driver(&gs_gadget_driver); printk(KERN_INFO "gs_module_exit: %s %s unloaded\n", GS_LONG_NAME, GS_VERSION_STR); } /* TTY Driver */ /* * gs_open */ static int gs_open(struct tty_struct *tty, struct file *file) { int port_num; unsigned long flags; struct gs_port *port; struct gs_dev *dev; struct gs_buf *buf; struct semaphore *sem; int ret; port_num = tty->index; gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file); if (port_num < 0 || port_num >= GS_NUM_PORTS) { printk(KERN_ERR "gs_open: (%d,%p,%p) invalid port number\n", port_num, tty, file); return -ENODEV; } dev = gs_device; if (dev == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) NULL device pointer\n", port_num, tty, file); return -ENODEV; } sem = &gs_open_close_sem[port_num]; if (down_interruptible(sem)) { printk(KERN_ERR "gs_open: (%d,%p,%p) interrupted waiting for semaphore\n", port_num, tty, file); return -ERESTARTSYS; } spin_lock_irqsave(&dev->dev_lock, flags); if (dev->dev_config == GS_NO_CONFIG_ID) { printk(KERN_ERR "gs_open: (%d,%p,%p) device is not connected\n", port_num, tty, file); ret = -ENODEV; goto exit_unlock_dev; } port = dev->dev_port[port_num]; if (port == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) NULL port pointer\n", port_num, tty, file); ret = -ENODEV; goto exit_unlock_dev; } spin_lock(&port->port_lock); spin_unlock(&dev->dev_lock); if (port->port_dev == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) port disconnected (1)\n", port_num, tty, file); ret = -EIO; goto exit_unlock_port; } if (port->port_open_count > 0) { ++port->port_open_count; gs_debug("gs_open: (%d,%p,%p) already open\n", port_num, tty, file); ret = 0; goto exit_unlock_port; } tty->driver_data = NULL; /* mark port as in use, we can drop port lock and sleep if necessary */ port->port_in_use = 1; /* allocate write buffer on first open */ if (port->port_write_buf == NULL) { spin_unlock_irqrestore(&port->port_lock, flags); buf = gs_buf_alloc(write_buf_size, GFP_KERNEL); spin_lock_irqsave(&port->port_lock, flags); /* might have been disconnected while asleep, check */ if (port->port_dev == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) port disconnected (2)\n", port_num, tty, file); port->port_in_use = 0; ret = -EIO; goto exit_unlock_port; } if ((port->port_write_buf=buf) == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) cannot allocate port write buffer\n", port_num, tty, file); port->port_in_use = 0; ret = -ENOMEM; goto exit_unlock_port; } } /* wait for carrier detect (not implemented) */ /* might have been disconnected while asleep, check */ if (port->port_dev == NULL) { printk(KERN_ERR "gs_open: (%d,%p,%p) port disconnected (3)\n", port_num, tty, file); port->port_in_use = 0; ret = -EIO; goto exit_unlock_port; } tty->driver_data = port; port->port_tty = tty; port->port_open_count = 1; port->port_in_use = 0; gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file); ret = 0; exit_unlock_port: spin_unlock_irqrestore(&port->port_lock, flags); up(sem); return ret; exit_unlock_dev: spin_unlock_irqrestore(&dev->dev_lock, flags); up(sem); return ret; } /* * gs_close */ #define GS_WRITE_FINISHED_EVENT_SAFELY(p) \ ({ \ int cond; \ \ spin_lock_irq(&(p)->port_lock); \ cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf); \ spin_unlock_irq(&(p)->port_lock); \ cond; \ }) static void gs_close(struct tty_struct *tty, struct file *file) { struct gs_port *port = tty->driver_data; struct semaphore *sem; if (port == NULL) { printk(KERN_ERR "gs_close: NULL port pointer\n"); return; } gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file); sem = &gs_open_close_sem[port->port_num]; down(sem); spin_lock_irq(&port->port_lock); if (port->port_open_count == 0) { printk(KERN_ERR "gs_close: (%d,%p,%p) port is already closed\n", port->port_num, tty, file); goto exit; } if (port->port_open_count > 1) { --port->port_open_count; goto exit; } /* free disconnected port on final close */ if (port->port_dev == NULL) { kfree(port); goto exit; } /* mark port as closed but in use, we can drop port lock */ /* and sleep if necessary */ port->port_in_use = 1; port->port_open_count = 0; /* wait for write buffer to drain, or */ /* at most GS_CLOSE_TIMEOUT seconds */ if (gs_buf_data_avail(port->port_write_buf) > 0) { spin_unlock_irq(&port->port_lock); wait_event_interruptible_timeout(port->port_write_wait, GS_WRITE_FINISHED_EVENT_SAFELY(port), GS_CLOSE_TIMEOUT * HZ); spin_lock_irq(&port->port_lock); } /* free disconnected port on final close */ /* (might have happened during the above sleep) */ if (port->port_dev == NULL) { kfree(port); goto exit; } gs_buf_clear(port->port_write_buf); tty->driver_data = NULL; port->port_tty = NULL; port->port_in_use = 0; gs_debug("gs_close: (%d,%p,%p) completed\n", port->port_num, tty, file); exit: spin_unlock_irq(&port->port_lock); up(sem); } /* * gs_write */ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) { unsigned long flags; struct gs_port *port = tty->driver_data; int ret; if (port == NULL) { printk(KERN_ERR "gs_write: NULL port pointer\n"); return -EIO; } gs_debug("gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty, count); if (count == 0) return 0; spin_lock_irqsave(&port->port_lock, flags); if (port->port_dev == NULL) { printk(KERN_ERR "gs_write: (%d,%p) port is not connected\n", port->port_num, tty); ret = -EIO; goto exit; } if (port->port_open_count == 0) { printk(KERN_ERR "gs_write: (%d,%p) port is closed\n", port->port_num, tty); ret = -EBADF; goto exit; } count = gs_buf_put(port->port_write_buf, buf, count); spin_unlock_irqrestore(&port->port_lock, flags); gs_send(gs_device); gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty, count); return count; exit: spin_unlock_irqrestore(&port->port_lock, flags); return ret; } /* * gs_put_char */ static void gs_put_char(struct tty_struct *tty, unsigned char ch) { unsigned long flags; struct gs_port *port = tty->driver_data; if (port == NULL) { printk(KERN_ERR "gs_put_char: NULL port pointer\n"); return; } gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p, %p, %p\n", port->port_num, tty, ch, __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2)); spin_lock_irqsave(&port->port_lock, flags); if (port->port_dev == NULL) { printk(KERN_ERR "gs_put_char: (%d,%p) port is not connected\n", port->port_num, tty); goto exit; } if (port->port_open_count == 0) { printk(KERN_ERR "gs_put_char: (%d,%p) port is closed\n", port->port_num, tty); goto exit; } gs_buf_put(port->port_write_buf, &ch, 1); exit: spin_unlock_irqrestore(&port->port_lock, flags); } /* * gs_flush_chars */ static void gs_flush_chars(struct tty_struct *tty) { unsigned long flags; struct gs_port *port = tty->driver_data; if (port == NULL) { printk(KERN_ERR "gs_flush_chars: NULL port pointer\n"); return; } gs_debug("gs_flush_chars: (%d,%p)\n", port->port_num, tty); spin_lock_irqsave(&port->port_lock, flags); if (port->port_dev == NULL) { printk(KERN_ERR "gs_flush_chars: (%d,%p) port is not connected\n", port->port_num, tty); goto exit; } if (port->port_open_count == 0) { printk(KERN_ERR "gs_flush_chars: (%d,%p) port is closed\n", port->port_num, tty); goto exit; } spin_unlock_irqrestore(&port->port_lock, flags); gs_send(gs_device); return; exit: spin_unlock_irqrestore(&port->port_lock, flags); } /* * gs_write_room */ static int gs_write_room(struct tty_struct *tty) { int room = 0; unsigned long flags; struct gs_port *port = tty->driver_data; if (port == NULL) return 0; spin_lock_irqsave(&port->port_lock, flags); if (port->port_dev != NULL && port->port_open_count > 0 && port->port_write_buf != NULL) room = gs_buf_space_avail(port->port_write_buf); spin_unlock_irqrestore(&port->port_lock, flags); gs_debug("gs_write_room: (%d,%p) room=%d\n", port->port_num, tty, room); return room; } /* * gs_chars_in_buffer */ static int gs_chars_in_buffer(struct tty_struct *tty) { int chars = 0; unsigned long flags; struct gs_port *port = tty->driver_data; if (port == NULL) return 0; spin_lock_irqsave(&port->port_lock, flags); if (port->port_dev != NULL && port->port_open_count > 0 && port->port_write_buf != NULL) chars = gs_buf_data_avail(port->port_write_buf); spin_unlock_irqrestore(&port->port_lock, flags); gs_debug("gs_chars_in_buffer: (%d,%p) chars=%d\n", port->port_num, tty, chars); return chars; } /* * gs_throttle */ static void gs_throttle(struct tty_struct *tty) { } /* * gs_unthrottle */ static void gs_unthrottle(struct tty_struct *tty) { } /* * gs_break */ static void gs_break(struct tty_struct *tty, int break_state) { } /* * gs_ioctl */ static int gs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct gs_port *port = tty->driver_data; if (port == NULL) { printk(KERN_ERR "gs_ioctl: NULL port pointer\n"); return -EIO; } gs_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n", port->port_num, tty, file, cmd, arg); /* handle ioctls */ /* could not handle ioctl */ return -ENOIOCTLCMD; } /* * gs_set_termios */ static void gs_set_termios(struct tty_struct *tty, struct ktermios *old) { } /* * gs_send * * This function finds available write requests, calls * gs_send_packet to fill these packets with data, and * continues until either there are no more write requests * available or no more data to send. This function is * run whenever data arrives or write requests are available. */ static int gs_send(struct gs_dev *dev) { int ret,len; unsigned long flags; struct usb_ep *ep; struct usb_request *req; struct gs_req_entry *req_entry; if (dev == NULL) { printk(KERN_ERR "gs_send: NULL device pointer\n"); return -ENODEV; } spin_lock_irqsave(&dev->dev_lock, flags); ep = dev->dev_in_ep; while(!list_empty(&dev->dev_req_list)) { req_entry = list_entry(dev->dev_req_list.next, struct gs_req_entry, re_entry); req = req_entry->re_req; len = gs_send_packet(dev, req->buf, ep->maxpacket); if (len > 0) { gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2)); list_del(&req_entry->re_entry); req->length = len; spin_unlock_irqrestore(&dev->dev_lock, flags); if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { printk(KERN_ERR "gs_send: cannot queue read request, ret=%d\n", ret); spin_lock_irqsave(&dev->dev_lock, flags); break; } spin_lock_irqsave(&dev->dev_lock, flags); } else { break; } } spin_unlock_irqrestore(&dev->dev_lock, flags); return 0; } /* * gs_send_packet * * If there is data to send, a packet is built in the given * buffer and the size is returned. If there is no data to * send, 0 is returned. If there is any error a negative * error number is returned. * * Called during USB completion routine, on interrupt time. * * We assume that disconnect will not happen until all completion * routines have completed, so we can assume that the dev_port * array does not change during the lifetime of this function. */ static int gs_send_packet(struct gs_dev *dev, char *packet, unsigned int size) { unsigned int len; struct gs_port *port; /* TEMPORARY -- only port 0 is supported right now */ port = dev->dev_port[0]; if (port == NULL) { printk(KERN_ERR "gs_send_packet: port=%d, NULL port pointer\n", 0); return -EIO; } spin_lock(&port->port_lock); len = gs_buf_data_avail(port->port_write_buf); if (len < size) size = len; if (size == 0) goto exit; size = gs_buf_get(port->port_write_buf, packet, size); if (port->port_tty) wake_up_interruptible(&port->port_tty->write_wait); exit: spin_unlock(&port->port_lock); return size; } /* * gs_recv_packet * * Called for each USB packet received. Reads the packet * header and stuffs the data in the appropriate tty buffer. * Returns 0 if successful, or a negative error number. * * Called during USB completion routine, on interrupt time. * * We assume that disconnect will not happen until all completion * routines have completed, so we can assume that the dev_port * array does not change during the lifetime of this function. */ static int gs_recv_packet(struct gs_dev *dev, char *packet, unsigned int size) { unsigned int len; struct gs_port *port; int ret; struct tty_struct *tty; /* TEMPORARY -- only port 0 is supported right now */ port = dev->dev_port[0]; if (port == NULL) { printk(KERN_ERR "gs_recv_packet: port=%d, NULL port pointer\n", port->port_num); return -EIO; } spin_lock(&port->port_lock); if (port->port_open_count == 0) { printk(KERN_ERR "gs_recv_packet: port=%d, port is closed\n", port->port_num); ret = -EIO; goto exit; } tty = port->port_tty; if (tty == NULL) { printk(KERN_ERR "gs_recv_packet: port=%d, NULL tty pointer\n", port->port_num); ret = -EIO; goto exit; } if (port->port_tty->magic != TTY_MAGIC) { printk(KERN_ERR "gs_recv_packet: port=%d, bad tty magic\n", port->port_num); ret = -EIO; goto exit; } len = tty_buffer_request_room(tty, size); if (len > 0) { tty_insert_flip_string(tty, packet, len); tty_flip_buffer_push(port->port_tty); wake_up_interruptible(&port->port_tty->read_wait); } ret = 0; exit: spin_unlock(&port->port_lock); return ret; } /* * gs_read_complete */ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) { int ret; struct gs_dev *dev = ep->driver_data; if (dev == NULL) { printk(KERN_ERR "gs_read_complete: NULL device pointer\n"); return; } switch(req->status) { case 0: /* normal completion */ gs_recv_packet(dev, req->buf, req->actual); requeue: req->length = ep->maxpacket; if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { printk(KERN_ERR "gs_read_complete: cannot queue read request, ret=%d\n", ret); } break; case -ESHUTDOWN: /* disconnect */ gs_debug("gs_read_complete: shutdown\n"); gs_free_req(ep, req); break; default: /* unexpected */ printk(KERN_ERR "gs_read_complete: unexpected status error, status=%d\n", req->status); goto requeue; break; } } /* * gs_write_complete */ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) { struct gs_dev *dev = ep->driver_data; struct gs_req_entry *gs_req = req->context; if (dev == NULL) { printk(KERN_ERR "gs_write_complete: NULL device pointer\n"); return; } switch(req->status) { case 0: /* normal completion */ requeue: if (gs_req == NULL) { printk(KERN_ERR "gs_write_complete: NULL request pointer\n"); return; } spin_lock(&dev->dev_lock); list_add(&gs_req->re_entry, &dev->dev_req_list); spin_unlock(&dev->dev_lock); gs_send(dev); break; case -ESHUTDOWN: /* disconnect */ gs_debug("gs_write_complete: shutdown\n"); gs_free_req(ep, req); break; default: printk(KERN_ERR "gs_write_complete: unexpected status error, status=%d\n", req->status); goto requeue; break; } } /* Gadget Driver */ /* * gs_bind * * Called on module load. Allocates and initializes the device * structure and a control request. */ static int __init gs_bind(struct usb_gadget *gadget) { int ret; struct usb_ep *ep; struct gs_dev *dev; int gcnum; /* Some controllers can't support CDC ACM: * - sh doesn't support multiple interfaces or configs; * - sa1100 doesn't have a third interrupt endpoint */ if (gadget_is_sh(gadget) || gadget_is_sa1100(gadget)) use_acm = 0; gcnum = usb_gadget_controller_number(gadget); if (gcnum >= 0) gs_device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum); else { printk(KERN_WARNING "gs_bind: controller '%s' not recognized\n", gadget->name); /* unrecognized, but safe unless bulk is REALLY quirky */ gs_device_desc.bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); } usb_ep_autoconfig_reset(gadget); ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); if (!ep) goto autoconf_fail; EP_IN_NAME = ep->name; ep->driver_data = ep; /* claim the endpoint */ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); if (!ep) goto autoconf_fail; EP_OUT_NAME = ep->name; ep->driver_data = ep; /* claim the endpoint */ if (use_acm) { ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); if (!ep) { printk(KERN_ERR "gs_bind: cannot run ACM on %s\n", gadget->name); goto autoconf_fail; } gs_device_desc.idProduct = __constant_cpu_to_le16( GS_CDC_PRODUCT_ID), EP_NOTIFY_NAME = ep->name; ep->driver_data = ep; /* claim the endpoint */ } gs_device_desc.bDeviceClass = use_acm ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; #ifdef CONFIG_USB_GADGET_DUALSPEED gs_qualifier_desc.bDeviceClass = use_acm ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; /* assume ep0 uses the same packet size for both speeds */ gs_qualifier_desc.bMaxPacketSize0 = gs_device_desc.bMaxPacketSize0; /* assume endpoints are dual-speed */ gs_highspeed_notify_desc.bEndpointAddress = gs_fullspeed_notify_desc.bEndpointAddress; gs_highspeed_in_desc.bEndpointAddress = gs_fullspeed_in_desc.bEndpointAddress; gs_highspeed_out_desc.bEndpointAddress = gs_fullspeed_out_desc.bEndpointAddress; #endif /* CONFIG_USB_GADGET_DUALSPEED */ usb_gadget_set_selfpowered(gadget); if (gadget->is_otg) { gs_otg_descriptor.bmAttributes |= USB_OTG_HNP, gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } gs_device = dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); if (dev == NULL) return -ENOMEM; snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", init_utsname()->sysname, init_utsname()->release, gadget->name); dev->dev_gadget = gadget; spin_lock_init(&dev->dev_lock); INIT_LIST_HEAD(&dev->dev_req_list); set_gadget_data(gadget, dev); if ((ret=gs_alloc_ports(dev, GFP_KERNEL)) != 0) { printk(KERN_ERR "gs_bind: cannot allocate ports\n"); gs_unbind(gadget); return ret; } /* preallocate control response and buffer */ dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN, GFP_KERNEL); if (dev->dev_ctrl_req == NULL) { gs_unbind(gadget); return -ENOMEM; } dev->dev_ctrl_req->complete = gs_setup_complete; gadget->ep0->driver_data = dev; printk(KERN_INFO "gs_bind: %s %s bound\n", GS_LONG_NAME, GS_VERSION_STR); return 0; autoconf_fail: printk(KERN_ERR "gs_bind: cannot autoconfigure on %s\n", gadget->name); return -ENODEV; } /* * gs_unbind * * Called on module unload. Frees the control request and device * structure. */ static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget) { struct gs_dev *dev = get_gadget_data(gadget); gs_device = NULL; /* read/write requests already freed, only control request remains */ if (dev != NULL) { if (dev->dev_ctrl_req != NULL) { gs_free_req(gadget->ep0, dev->dev_ctrl_req); dev->dev_ctrl_req = NULL; } gs_free_ports(dev); kfree(dev); set_gadget_data(gadget, NULL); } printk(KERN_INFO "gs_unbind: %s %s unbound\n", GS_LONG_NAME, GS_VERSION_STR); } /* * gs_setup * * Implements all the control endpoint functionality that's not * handled in hardware or the hardware driver. * * Returns the size of the data sent to the host, or a negative * error number. */ static int gs_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { int ret = -EOPNOTSUPP; struct gs_dev *dev = get_gadget_data(gadget); struct usb_request *req = dev->dev_ctrl_req; u16 wIndex = le16_to_cpu(ctrl->wIndex); u16 wValue = le16_to_cpu(ctrl->wValue); u16 wLength = le16_to_cpu(ctrl->wLength); switch (ctrl->bRequestType & USB_TYPE_MASK) { case USB_TYPE_STANDARD: ret = gs_setup_standard(gadget,ctrl); break; case USB_TYPE_CLASS: ret = gs_setup_class(gadget,ctrl); break; default: printk(KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); break; } /* respond with data transfer before status phase? */ if (ret >= 0) { req->length = ret; req->zero = ret < wLength && (ret % gadget->ep0->maxpacket) == 0; ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); if (ret < 0) { printk(KERN_ERR "gs_setup: cannot queue response, ret=%d\n", ret); req->status = 0; gs_setup_complete(gadget->ep0, req); } } /* device either stalls (ret < 0) or reports success */ return ret; } static int gs_setup_standard(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { int ret = -EOPNOTSUPP; struct gs_dev *dev = get_gadget_data(gadget); struct usb_request *req = dev->dev_ctrl_req; u16 wIndex = le16_to_cpu(ctrl->wIndex); u16 wValue = le16_to_cpu(ctrl->wValue); u16 wLength = le16_to_cpu(ctrl->wLength); switch (ctrl->bRequest) { case USB_REQ_GET_DESCRIPTOR: if (ctrl->bRequestType != USB_DIR_IN) break; switch (wValue >> 8) { case USB_DT_DEVICE: ret = min(wLength, (u16)sizeof(struct usb_device_descriptor)); memcpy(req->buf, &gs_device_desc, ret); break; #ifdef CONFIG_USB_GADGET_DUALSPEED case USB_DT_DEVICE_QUALIFIER: if (!gadget->is_dualspeed) break; ret = min(wLength, (u16)sizeof(struct usb_qualifier_descriptor)); memcpy(req->buf, &gs_qualifier_desc, ret); break; case USB_DT_OTHER_SPEED_CONFIG: if (!gadget->is_dualspeed) break; /* fall through */ #endif /* CONFIG_USB_GADGET_DUALSPEED */ case USB_DT_CONFIG: ret = gs_build_config_buf(req->buf, gadget->speed, wValue >> 8, wValue & 0xff, gadget->is_otg); if (ret >= 0) ret = min(wLength, (u16)ret); break; case USB_DT_STRING: /* wIndex == language code. */ ret = usb_gadget_get_string(&gs_string_table, wValue & 0xff, req->buf); if (ret >= 0) ret = min(wLength, (u16)ret); break; } break; case USB_REQ_SET_CONFIGURATION: if (ctrl->bRequestType != 0) break; spin_lock(&dev->dev_lock); ret = gs_set_config(dev, wValue); spin_unlock(&dev->dev_lock); break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) break; *(u8 *)req->buf = dev->dev_config; ret = min(wLength, (u16)1); break; case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE || !dev->dev_config || wIndex >= GS_MAX_NUM_INTERFACES) break; if (dev->dev_config == GS_BULK_CONFIG_ID && wIndex != GS_BULK_INTERFACE_ID) break; /* no alternate interface settings */ if (wValue != 0) break; spin_lock(&dev->dev_lock); /* PXA hardware partially handles SET_INTERFACE; * we need to kluge around that interference. */ if (gadget_is_pxa(gadget)) { ret = gs_set_config(dev, use_acm ? GS_ACM_CONFIG_ID : GS_BULK_CONFIG_ID); goto set_interface_done; } if (dev->dev_config != GS_BULK_CONFIG_ID && wIndex == GS_CONTROL_INTERFACE_ID) { if (dev->dev_notify_ep) { usb_ep_disable(dev->dev_notify_ep); usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc); } } else { usb_ep_disable(dev->dev_in_ep); usb_ep_disable(dev->dev_out_ep); usb_ep_enable(dev->dev_in_ep, dev->dev_in_ep_desc); usb_ep_enable(dev->dev_out_ep, dev->dev_out_ep_desc); } ret = 0; set_interface_done: spin_unlock(&dev->dev_lock); break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) || dev->dev_config == GS_NO_CONFIG_ID) break; if (wIndex >= GS_MAX_NUM_INTERFACES || (dev->dev_config == GS_BULK_CONFIG_ID && wIndex != GS_BULK_INTERFACE_ID)) { ret = -EDOM; break; } /* no alternate interface settings */ *(u8 *)req->buf = 0; ret = min(wLength, (u16)1); break; default: printk(KERN_ERR "gs_setup: unknown standard request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); break; } return ret; } static int gs_setup_class(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { int ret = -EOPNOTSUPP; struct gs_dev *dev = get_gadget_data(gadget); struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ struct usb_request *req = dev->dev_ctrl_req; u16 wIndex = le16_to_cpu(ctrl->wIndex); u16 wValue = le16_to_cpu(ctrl->wValue); u16 wLength = le16_to_cpu(ctrl->wLength); switch (ctrl->bRequest) { case USB_CDC_REQ_SET_LINE_CODING: ret = min(wLength, (u16)sizeof(struct usb_cdc_line_coding)); if (port) { spin_lock(&port->port_lock); memcpy(&port->port_line_coding, req->buf, ret); spin_unlock(&port->port_lock); } ret = 0; break; case USB_CDC_REQ_GET_LINE_CODING: port = dev->dev_port[0]; /* ACM only has one port */ ret = min(wLength, (u16)sizeof(struct usb_cdc_line_coding)); if (port) { spin_lock(&port->port_lock); memcpy(req->buf, &port->port_line_coding, ret); spin_unlock(&port->port_lock); } break; case USB_CDC_REQ_SET_CONTROL_LINE_STATE: ret = 0; break; default: printk(KERN_ERR "gs_setup: unknown class request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); break; } return ret; } /* * gs_setup_complete */ static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req) { if (req->status || req->actual != req->length) { printk(KERN_ERR "gs_setup_complete: status error, status=%d, actual=%d, length=%d\n", req->status, req->actual, req->length); } } /* * gs_disconnect * * Called when the device is disconnected. Frees the closed * ports and disconnects open ports. Open ports will be freed * on close. Then reallocates the ports for the next connection. */ static void gs_disconnect(struct usb_gadget *gadget) { unsigned long flags; struct gs_dev *dev = get_gadget_data(gadget); spin_lock_irqsave(&dev->dev_lock, flags); gs_reset_config(dev); /* free closed ports and disconnect open ports */ /* (open ports will be freed when closed) */ gs_free_ports(dev); /* re-allocate ports for the next connection */ if (gs_alloc_ports(dev, GFP_ATOMIC) != 0) printk(KERN_ERR "gs_disconnect: cannot re-allocate ports\n"); spin_unlock_irqrestore(&dev->dev_lock, flags); printk(KERN_INFO "gs_disconnect: %s disconnected\n", GS_LONG_NAME); } /* * gs_set_config * * Configures the device by enabling device specific * optimizations, setting up the endpoints, allocating * read and write requests and queuing read requests. * * The device lock must be held when calling this function. */ static int gs_set_config(struct gs_dev *dev, unsigned config) { int i; int ret = 0; struct usb_gadget *gadget = dev->dev_gadget; struct usb_ep *ep; struct usb_endpoint_descriptor *ep_desc; struct usb_request *req; struct gs_req_entry *req_entry; if (dev == NULL) { printk(KERN_ERR "gs_set_config: NULL device pointer\n"); return 0; } if (config == dev->dev_config) return 0; gs_reset_config(dev); switch (config) { case GS_NO_CONFIG_ID: return 0; case GS_BULK_CONFIG_ID: if (use_acm) return -EINVAL; /* device specific optimizations */ if (gadget_is_net2280(gadget)) net2280_set_fifo_mode(gadget, 1); break; case GS_ACM_CONFIG_ID: if (!use_acm) return -EINVAL; /* device specific optimizations */ if (gadget_is_net2280(gadget)) net2280_set_fifo_mode(gadget, 1); break; default: return -EINVAL; } dev->dev_config = config; gadget_for_each_ep(ep, gadget) { if (EP_NOTIFY_NAME && strcmp(ep->name, EP_NOTIFY_NAME) == 0) { ep_desc = GS_SPEED_SELECT( gadget->speed == USB_SPEED_HIGH, &gs_highspeed_notify_desc, &gs_fullspeed_notify_desc); ret = usb_ep_enable(ep,ep_desc); if (ret == 0) { ep->driver_data = dev; dev->dev_notify_ep = ep; dev->dev_notify_ep_desc = ep_desc; } else { printk(KERN_ERR "gs_set_config: cannot enable notify endpoint %s, ret=%d\n", ep->name, ret); goto exit_reset_config; } } else if (strcmp(ep->name, EP_IN_NAME) == 0) { ep_desc = GS_SPEED_SELECT( gadget->speed == USB_SPEED_HIGH, &gs_highspeed_in_desc, &gs_fullspeed_in_desc); ret = usb_ep_enable(ep,ep_desc); if (ret == 0) { ep->driver_data = dev; dev->dev_in_ep = ep; dev->dev_in_ep_desc = ep_desc; } else { printk(KERN_ERR "gs_set_config: cannot enable in endpoint %s, ret=%d\n", ep->name, ret); goto exit_reset_config; } } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { ep_desc = GS_SPEED_SELECT( gadget->speed == USB_SPEED_HIGH, &gs_highspeed_out_desc, &gs_fullspeed_out_desc); ret = usb_ep_enable(ep,ep_desc); if (ret == 0) { ep->driver_data = dev; dev->dev_out_ep = ep; dev->dev_out_ep_desc = ep_desc; } else { printk(KERN_ERR "gs_set_config: cannot enable out endpoint %s, ret=%d\n", ep->name, ret); goto exit_reset_config; } } } if (dev->dev_in_ep == NULL || dev->dev_out_ep == NULL || (config != GS_BULK_CONFIG_ID && dev->dev_notify_ep == NULL)) { printk(KERN_ERR "gs_set_config: cannot find endpoints\n"); ret = -ENODEV; goto exit_reset_config; } /* allocate and queue read requests */ ep = dev->dev_out_ep; for (i=0; i<read_q_size && ret == 0; i++) { if ((req=gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC))) { req->complete = gs_read_complete; if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { printk(KERN_ERR "gs_set_config: cannot queue read request, ret=%d\n", ret); } } else { printk(KERN_ERR "gs_set_config: cannot allocate read requests\n"); ret = -ENOMEM; goto exit_reset_config; } } /* allocate write requests, and put on free list */ ep = dev->dev_in_ep; for (i=0; i<write_q_size; i++) { if ((req_entry=gs_alloc_req_entry(ep, ep->maxpacket, GFP_ATOMIC))) { req_entry->re_req->complete = gs_write_complete; list_add(&req_entry->re_entry, &dev->dev_req_list); } else { printk(KERN_ERR "gs_set_config: cannot allocate write requests\n"); ret = -ENOMEM; goto exit_reset_config; } } printk(KERN_INFO "gs_set_config: %s configured, %s speed %s config\n", GS_LONG_NAME, gadget->speed == USB_SPEED_HIGH ? "high" : "full", config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM"); return 0; exit_reset_config: gs_reset_config(dev); return ret; } /* * gs_reset_config * * Mark the device as not configured, disable all endpoints, * which forces completion of pending I/O and frees queued * requests, and free the remaining write requests on the * free list. * * The device lock must be held when calling this function. */ static void gs_reset_config(struct gs_dev *dev) { struct gs_req_entry *req_entry; if (dev == NULL) { printk(KERN_ERR "gs_reset_config: NULL device pointer\n"); return; } if (dev->dev_config == GS_NO_CONFIG_ID) return; dev->dev_config = GS_NO_CONFIG_ID; /* free write requests on the free list */ while(!list_empty(&dev->dev_req_list)) { req_entry = list_entry(dev->dev_req_list.next, struct gs_req_entry, re_entry); list_del(&req_entry->re_entry); gs_free_req_entry(dev->dev_in_ep, req_entry); } /* disable endpoints, forcing completion of pending i/o; */ /* completion handlers free their requests in this case */ if (dev->dev_notify_ep) { usb_ep_disable(dev->dev_notify_ep); dev->dev_notify_ep = NULL; } if (dev->dev_in_ep) { usb_ep_disable(dev->dev_in_ep); dev->dev_in_ep = NULL; } if (dev->dev_out_ep) { usb_ep_disable(dev->dev_out_ep); dev->dev_out_ep = NULL; } } /* * gs_build_config_buf * * Builds the config descriptors in the given buffer and returns the * length, or a negative error number. */ static int gs_build_config_buf(u8 *buf, enum usb_device_speed speed, u8 type, unsigned int index, int is_otg) { int len; int high_speed; const struct usb_config_descriptor *config_desc; const struct usb_descriptor_header **function; if (index >= gs_device_desc.bNumConfigurations) return -EINVAL; /* other speed switches high and full speed */ high_speed = (speed == USB_SPEED_HIGH); if (type == USB_DT_OTHER_SPEED_CONFIG) high_speed = !high_speed; if (use_acm) { config_desc = &gs_acm_config_desc; function = GS_SPEED_SELECT(high_speed, gs_acm_highspeed_function, gs_acm_fullspeed_function); } else { config_desc = &gs_bulk_config_desc; function = GS_SPEED_SELECT(high_speed, gs_bulk_highspeed_function, gs_bulk_fullspeed_function); } /* for now, don't advertise srp-only devices */ if (!is_otg) function++; len = usb_gadget_config_buf(config_desc, buf, GS_MAX_DESC_LEN, function); if (len < 0) return len; ((struct usb_config_descriptor *)buf)->bDescriptorType = type; return len; } /* * gs_alloc_req * * Allocate a usb_request and its buffer. Returns a pointer to the * usb_request or NULL if there is an error. */ static struct usb_request * gs_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t kmalloc_flags) { struct usb_request *req; if (ep == NULL) return NULL; req = usb_ep_alloc_request(ep, kmalloc_flags); if (req != NULL) { req->length = len; req->buf = kmalloc(len, kmalloc_flags); if (req->buf == NULL) { usb_ep_free_request(ep, req); return NULL; } } return req; } /* * gs_free_req * * Free a usb_request and its buffer. */ static void gs_free_req(struct usb_ep *ep, struct usb_request *req) { if (ep != NULL && req != NULL) { kfree(req->buf); usb_ep_free_request(ep, req); } } /* * gs_alloc_req_entry * * Allocates a request and its buffer, using the given * endpoint, buffer len, and kmalloc flags. */ static struct gs_req_entry * gs_alloc_req_entry(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) { struct gs_req_entry *req; req = kmalloc(sizeof(struct gs_req_entry), kmalloc_flags); if (req == NULL) return NULL; req->re_req = gs_alloc_req(ep, len, kmalloc_flags); if (req->re_req == NULL) { kfree(req); return NULL; } req->re_req->context = req; return req; } /* * gs_free_req_entry * * Frees a request and its buffer. */ static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req) { if (ep != NULL && req != NULL) { if (req->re_req != NULL) gs_free_req(ep, req->re_req); kfree(req); } } /* * gs_alloc_ports * * Allocate all ports and set the gs_dev struct to point to them. * Return 0 if successful, or a negative error number. * * The device lock is normally held when calling this function. */ static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags) { int i; struct gs_port *port; if (dev == NULL) return -EIO; for (i=0; i<GS_NUM_PORTS; i++) { if ((port=kzalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL) return -ENOMEM; port->port_dev = dev; port->port_num = i; port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE); port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT; port->port_line_coding.bParityType = GS_DEFAULT_PARITY; port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS; spin_lock_init(&port->port_lock); init_waitqueue_head(&port->port_write_wait); dev->dev_port[i] = port; } return 0; } /* * gs_free_ports * * Free all closed ports. Open ports are disconnected by * freeing their write buffers, setting their device pointers * and the pointers to them in the device to NULL. These * ports will be freed when closed. * * The device lock is normally held when calling this function. */ static void gs_free_ports(struct gs_dev *dev) { int i; unsigned long flags; struct gs_port *port; if (dev == NULL) return; for (i=0; i<GS_NUM_PORTS; i++) { if ((port=dev->dev_port[i]) != NULL) { dev->dev_port[i] = NULL; spin_lock_irqsave(&port->port_lock, flags); if (port->port_write_buf != NULL) { gs_buf_free(port->port_write_buf); port->port_write_buf = NULL; } if (port->port_open_count > 0 || port->port_in_use) { port->port_dev = NULL; wake_up_interruptible(&port->port_write_wait); if (port->port_tty) { wake_up_interruptible(&port->port_tty->read_wait); wake_up_interruptible(&port->port_tty->write_wait); } spin_unlock_irqrestore(&port->port_lock, flags); } else { spin_unlock_irqrestore(&port->port_lock, flags); kfree(port); } } } } /* Circular Buffer */ /* * gs_buf_alloc * * Allocate a circular buffer and all associated memory. */ static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags) { struct gs_buf *gb; if (size == 0) return NULL; gb = kmalloc(sizeof(struct gs_buf), kmalloc_flags); if (gb == NULL) return NULL; gb->buf_buf = kmalloc(size, kmalloc_flags); if (gb->buf_buf == NULL) { kfree(gb); return NULL; } gb->buf_size = size; gb->buf_get = gb->buf_put = gb->buf_buf; return gb; } /* * gs_buf_free * * Free the buffer and all associated memory. */ static void gs_buf_free(struct gs_buf *gb) { if (gb) { kfree(gb->buf_buf); kfree(gb); } } /* * gs_buf_clear * * Clear out all data in the circular buffer. */ static void gs_buf_clear(struct gs_buf *gb) { if (gb != NULL) gb->buf_get = gb->buf_put; /* equivalent to a get of all data available */ } /* * gs_buf_data_avail * * Return the number of bytes of data available in the circular * buffer. */ static unsigned int gs_buf_data_avail(struct gs_buf *gb) { if (gb != NULL) return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; else return 0; } /* * gs_buf_space_avail * * Return the number of bytes of space available in the circular * buffer. */ static unsigned int gs_buf_space_avail(struct gs_buf *gb) { if (gb != NULL) return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; else return 0; } /* * gs_buf_put * * Copy data data from a user buffer and put it into the circular buffer. * Restrict to the amount of space available. * * Return the number of bytes copied. */ static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) { unsigned int len; if (gb == NULL) return 0; len = gs_buf_space_avail(gb); if (count > len) count = len; if (count == 0) return 0; len = gb->buf_buf + gb->buf_size - gb->buf_put; if (count > len) { memcpy(gb->buf_put, buf, len); memcpy(gb->buf_buf, buf+len, count - len); gb->buf_put = gb->buf_buf + count - len; } else { memcpy(gb->buf_put, buf, count); if (count < len) gb->buf_put += count; else /* count == len */ gb->buf_put = gb->buf_buf; } return count; } /* * gs_buf_get * * Get data from the circular buffer and copy to the given buffer. * Restrict to the amount of data available. * * Return the number of bytes copied. */ static unsigned int gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count) { unsigned int len; if (gb == NULL) return 0; len = gs_buf_data_avail(gb); if (count > len) count = len; if (count == 0) return 0; len = gb->buf_buf + gb->buf_size - gb->buf_get; if (count > len) { memcpy(buf, gb->buf_get, len); memcpy(buf+len, gb->buf_buf, count - len); gb->buf_get = gb->buf_buf + count - len; } else { memcpy(buf, gb->buf_get, count); if (count < len) gb->buf_get += count; else /* count == len */ gb->buf_get = gb->buf_buf; } return count; }