diff options
Diffstat (limited to 'drivers/usb/gadget/f_acm.c')
-rw-r--r-- | drivers/usb/gadget/f_acm.c | 194 |
1 files changed, 183 insertions, 11 deletions
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index f3bf5612af2d..5ee1590b8e9c 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c | |||
@@ -47,16 +47,37 @@ struct f_acm { | |||
47 | u8 ctrl_id, data_id; | 47 | u8 ctrl_id, data_id; |
48 | u8 port_num; | 48 | u8 port_num; |
49 | 49 | ||
50 | u8 pending; | ||
51 | |||
52 | /* lock is mostly for pending and notify_req ... they get accessed | ||
53 | * by callbacks both from tty (open/close/break) under its spinlock, | ||
54 | * and notify_req.complete() which can't use that lock. | ||
55 | */ | ||
56 | spinlock_t lock; | ||
57 | |||
50 | struct acm_ep_descs fs; | 58 | struct acm_ep_descs fs; |
51 | struct acm_ep_descs hs; | 59 | struct acm_ep_descs hs; |
52 | 60 | ||
53 | struct usb_ep *notify; | 61 | struct usb_ep *notify; |
54 | struct usb_endpoint_descriptor *notify_desc; | 62 | struct usb_endpoint_descriptor *notify_desc; |
63 | struct usb_request *notify_req; | ||
55 | 64 | ||
56 | struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ | 65 | struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ |
66 | |||
67 | /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */ | ||
57 | u16 port_handshake_bits; | 68 | u16 port_handshake_bits; |
58 | #define RS232_RTS (1 << 1) /* unused with full duplex */ | 69 | #define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */ |
59 | #define RS232_DTR (1 << 0) /* host is ready for data r/w */ | 70 | #define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */ |
71 | |||
72 | /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */ | ||
73 | u16 serial_state; | ||
74 | #define ACM_CTRL_OVERRUN (1 << 6) | ||
75 | #define ACM_CTRL_PARITY (1 << 5) | ||
76 | #define ACM_CTRL_FRAMING (1 << 4) | ||
77 | #define ACM_CTRL_RI (1 << 3) | ||
78 | #define ACM_CTRL_BRK (1 << 2) | ||
79 | #define ACM_CTRL_DSR (1 << 1) | ||
80 | #define ACM_CTRL_DCD (1 << 0) | ||
60 | }; | 81 | }; |
61 | 82 | ||
62 | static inline struct f_acm *func_to_acm(struct usb_function *f) | 83 | static inline struct f_acm *func_to_acm(struct usb_function *f) |
@@ -64,12 +85,17 @@ static inline struct f_acm *func_to_acm(struct usb_function *f) | |||
64 | return container_of(f, struct f_acm, port.func); | 85 | return container_of(f, struct f_acm, port.func); |
65 | } | 86 | } |
66 | 87 | ||
88 | static inline struct f_acm *port_to_acm(struct gserial *p) | ||
89 | { | ||
90 | return container_of(p, struct f_acm, port); | ||
91 | } | ||
92 | |||
67 | /*-------------------------------------------------------------------------*/ | 93 | /*-------------------------------------------------------------------------*/ |
68 | 94 | ||
69 | /* notification endpoint uses smallish and infrequent fixed-size messages */ | 95 | /* notification endpoint uses smallish and infrequent fixed-size messages */ |
70 | 96 | ||
71 | #define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ | 97 | #define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ |
72 | #define GS_NOTIFY_MAXPACKET 8 | 98 | #define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */ |
73 | 99 | ||
74 | /* interface and class descriptors: */ | 100 | /* interface and class descriptors: */ |
75 | 101 | ||
@@ -115,7 +141,7 @@ static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { | |||
115 | .bLength = sizeof(acm_descriptor), | 141 | .bLength = sizeof(acm_descriptor), |
116 | .bDescriptorType = USB_DT_CS_INTERFACE, | 142 | .bDescriptorType = USB_DT_CS_INTERFACE, |
117 | .bDescriptorSubType = USB_CDC_ACM_TYPE, | 143 | .bDescriptorSubType = USB_CDC_ACM_TYPE, |
118 | .bmCapabilities = (1 << 1), | 144 | .bmCapabilities = USB_CDC_CAP_LINE, |
119 | }; | 145 | }; |
120 | 146 | ||
121 | static struct usb_cdc_union_desc acm_union_desc __initdata = { | 147 | static struct usb_cdc_union_desc acm_union_desc __initdata = { |
@@ -275,6 +301,11 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) | |||
275 | 301 | ||
276 | /* composite driver infrastructure handles everything except | 302 | /* composite driver infrastructure handles everything except |
277 | * CDC class messages; interface activation uses set_alt(). | 303 | * CDC class messages; interface activation uses set_alt(). |
304 | * | ||
305 | * Note CDC spec table 4 lists the ACM request profile. It requires | ||
306 | * encapsulated command support ... we don't handle any, and respond | ||
307 | * to them by stalling. Options include get/set/clear comm features | ||
308 | * (not that useful) and SEND_BREAK. | ||
278 | */ | 309 | */ |
279 | switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { | 310 | switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { |
280 | 311 | ||
@@ -310,7 +341,7 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) | |||
310 | value = 0; | 341 | value = 0; |
311 | 342 | ||
312 | /* FIXME we should not allow data to flow until the | 343 | /* FIXME we should not allow data to flow until the |
313 | * host sets the RS232_DTR bit; and when it clears | 344 | * host sets the ACM_CTRL_DTR bit; and when it clears |
314 | * that bit, we should return to that no-flow state. | 345 | * that bit, we should return to that no-flow state. |
315 | */ | 346 | */ |
316 | acm->port_handshake_bits = w_value; | 347 | acm->port_handshake_bits = w_value; |
@@ -348,9 +379,6 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | |||
348 | /* we know alt == 0, so this is an activation or a reset */ | 379 | /* we know alt == 0, so this is an activation or a reset */ |
349 | 380 | ||
350 | if (intf == acm->ctrl_id) { | 381 | if (intf == acm->ctrl_id) { |
351 | /* REVISIT this may need more work when we start to | ||
352 | * send notifications ... | ||
353 | */ | ||
354 | if (acm->notify->driver_data) { | 382 | if (acm->notify->driver_data) { |
355 | VDBG(cdev, "reset acm control interface %d\n", intf); | 383 | VDBG(cdev, "reset acm control interface %d\n", intf); |
356 | usb_ep_disable(acm->notify); | 384 | usb_ep_disable(acm->notify); |
@@ -395,6 +423,128 @@ static void acm_disable(struct usb_function *f) | |||
395 | 423 | ||
396 | /*-------------------------------------------------------------------------*/ | 424 | /*-------------------------------------------------------------------------*/ |
397 | 425 | ||
426 | /** | ||
427 | * acm_cdc_notify - issue CDC notification to host | ||
428 | * @acm: wraps host to be notified | ||
429 | * @type: notification type | ||
430 | * @value: Refer to cdc specs, wValue field. | ||
431 | * @data: data to be sent | ||
432 | * @length: size of data | ||
433 | * Context: irqs blocked, acm->lock held, acm_notify_req non-null | ||
434 | * | ||
435 | * Returns zero on sucess or a negative errno. | ||
436 | * | ||
437 | * See section 6.3.5 of the CDC 1.1 specification for information | ||
438 | * about the only notification we issue: SerialState change. | ||
439 | */ | ||
440 | static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value, | ||
441 | void *data, unsigned length) | ||
442 | { | ||
443 | struct usb_ep *ep = acm->notify; | ||
444 | struct usb_request *req; | ||
445 | struct usb_cdc_notification *notify; | ||
446 | const unsigned len = sizeof(*notify) + length; | ||
447 | void *buf; | ||
448 | int status; | ||
449 | |||
450 | req = acm->notify_req; | ||
451 | acm->notify_req = NULL; | ||
452 | acm->pending = false; | ||
453 | |||
454 | req->length = len; | ||
455 | notify = req->buf; | ||
456 | buf = notify + 1; | ||
457 | |||
458 | notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS | ||
459 | | USB_RECIP_INTERFACE; | ||
460 | notify->bNotificationType = type; | ||
461 | notify->wValue = cpu_to_le16(value); | ||
462 | notify->wIndex = cpu_to_le16(acm->ctrl_id); | ||
463 | notify->wLength = cpu_to_le16(length); | ||
464 | memcpy(buf, data, length); | ||
465 | |||
466 | status = usb_ep_queue(ep, req, GFP_ATOMIC); | ||
467 | if (status < 0) { | ||
468 | ERROR(acm->port.func.config->cdev, | ||
469 | "acm ttyGS%d can't notify serial state, %d\n", | ||
470 | acm->port_num, status); | ||
471 | acm->notify_req = req; | ||
472 | } | ||
473 | |||
474 | return status; | ||
475 | } | ||
476 | |||
477 | static int acm_notify_serial_state(struct f_acm *acm) | ||
478 | { | ||
479 | struct usb_composite_dev *cdev = acm->port.func.config->cdev; | ||
480 | int status; | ||
481 | |||
482 | spin_lock(&acm->lock); | ||
483 | if (acm->notify_req) { | ||
484 | DBG(cdev, "acm ttyGS%d serial state %04x\n", | ||
485 | acm->port_num, acm->serial_state); | ||
486 | status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE, | ||
487 | 0, &acm->serial_state, sizeof(acm->serial_state)); | ||
488 | } else { | ||
489 | acm->pending = true; | ||
490 | status = 0; | ||
491 | } | ||
492 | spin_unlock(&acm->lock); | ||
493 | return status; | ||
494 | } | ||
495 | |||
496 | static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req) | ||
497 | { | ||
498 | struct f_acm *acm = req->context; | ||
499 | u8 doit = false; | ||
500 | |||
501 | /* on this call path we do NOT hold the port spinlock, | ||
502 | * which is why ACM needs its own spinlock | ||
503 | */ | ||
504 | spin_lock(&acm->lock); | ||
505 | if (req->status != -ESHUTDOWN) | ||
506 | doit = acm->pending; | ||
507 | acm->notify_req = req; | ||
508 | spin_unlock(&acm->lock); | ||
509 | |||
510 | if (doit) | ||
511 | acm_notify_serial_state(acm); | ||
512 | } | ||
513 | |||
514 | /* connect == the TTY link is open */ | ||
515 | |||
516 | static void acm_connect(struct gserial *port) | ||
517 | { | ||
518 | struct f_acm *acm = port_to_acm(port); | ||
519 | |||
520 | acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD; | ||
521 | acm_notify_serial_state(acm); | ||
522 | } | ||
523 | |||
524 | static void acm_disconnect(struct gserial *port) | ||
525 | { | ||
526 | struct f_acm *acm = port_to_acm(port); | ||
527 | |||
528 | acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD); | ||
529 | acm_notify_serial_state(acm); | ||
530 | } | ||
531 | |||
532 | static int acm_send_break(struct gserial *port, int duration) | ||
533 | { | ||
534 | struct f_acm *acm = port_to_acm(port); | ||
535 | u16 state; | ||
536 | |||
537 | state = acm->serial_state; | ||
538 | state &= ~ACM_CTRL_BRK; | ||
539 | if (duration) | ||
540 | state |= ACM_CTRL_BRK; | ||
541 | |||
542 | acm->serial_state = state; | ||
543 | return acm_notify_serial_state(acm); | ||
544 | } | ||
545 | |||
546 | /*-------------------------------------------------------------------------*/ | ||
547 | |||
398 | /* ACM function driver setup/binding */ | 548 | /* ACM function driver setup/binding */ |
399 | static int __init | 549 | static int __init |
400 | acm_bind(struct usb_configuration *c, struct usb_function *f) | 550 | acm_bind(struct usb_configuration *c, struct usb_function *f) |
@@ -443,8 +593,20 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) | |||
443 | acm->notify = ep; | 593 | acm->notify = ep; |
444 | ep->driver_data = cdev; /* claim */ | 594 | ep->driver_data = cdev; /* claim */ |
445 | 595 | ||
596 | /* allocate notification */ | ||
597 | acm->notify_req = gs_alloc_req(ep, | ||
598 | sizeof(struct usb_cdc_notification) + 2, | ||
599 | GFP_KERNEL); | ||
600 | if (!acm->notify_req) | ||
601 | goto fail; | ||
602 | |||
603 | acm->notify_req->complete = acm_cdc_notify_complete; | ||
604 | acm->notify_req->context = acm; | ||
605 | |||
446 | /* copy descriptors, and track endpoint copies */ | 606 | /* copy descriptors, and track endpoint copies */ |
447 | f->descriptors = usb_copy_descriptors(acm_fs_function); | 607 | f->descriptors = usb_copy_descriptors(acm_fs_function); |
608 | if (!f->descriptors) | ||
609 | goto fail; | ||
448 | 610 | ||
449 | acm->fs.in = usb_find_endpoint(acm_fs_function, | 611 | acm->fs.in = usb_find_endpoint(acm_fs_function, |
450 | f->descriptors, &acm_fs_in_desc); | 612 | f->descriptors, &acm_fs_in_desc); |
@@ -476,8 +638,6 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) | |||
476 | f->hs_descriptors, &acm_hs_notify_desc); | 638 | f->hs_descriptors, &acm_hs_notify_desc); |
477 | } | 639 | } |
478 | 640 | ||
479 | /* FIXME provide a callback for triggering notifications */ | ||
480 | |||
481 | DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", | 641 | DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", |
482 | acm->port_num, | 642 | acm->port_num, |
483 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", | 643 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", |
@@ -486,6 +646,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) | |||
486 | return 0; | 646 | return 0; |
487 | 647 | ||
488 | fail: | 648 | fail: |
649 | if (acm->notify_req) | ||
650 | gs_free_req(acm->notify, acm->notify_req); | ||
651 | |||
489 | /* we might as well release our claims on endpoints */ | 652 | /* we might as well release our claims on endpoints */ |
490 | if (acm->notify) | 653 | if (acm->notify) |
491 | acm->notify->driver_data = NULL; | 654 | acm->notify->driver_data = NULL; |
@@ -502,10 +665,13 @@ fail: | |||
502 | static void | 665 | static void |
503 | acm_unbind(struct usb_configuration *c, struct usb_function *f) | 666 | acm_unbind(struct usb_configuration *c, struct usb_function *f) |
504 | { | 667 | { |
668 | struct f_acm *acm = func_to_acm(f); | ||
669 | |||
505 | if (gadget_is_dualspeed(c->cdev->gadget)) | 670 | if (gadget_is_dualspeed(c->cdev->gadget)) |
506 | usb_free_descriptors(f->hs_descriptors); | 671 | usb_free_descriptors(f->hs_descriptors); |
507 | usb_free_descriptors(f->descriptors); | 672 | usb_free_descriptors(f->descriptors); |
508 | kfree(func_to_acm(f)); | 673 | gs_free_req(acm->notify, acm->notify_req); |
674 | kfree(acm); | ||
509 | } | 675 | } |
510 | 676 | ||
511 | /* Some controllers can't support CDC ACM ... */ | 677 | /* Some controllers can't support CDC ACM ... */ |
@@ -569,8 +735,14 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num) | |||
569 | if (!acm) | 735 | if (!acm) |
570 | return -ENOMEM; | 736 | return -ENOMEM; |
571 | 737 | ||
738 | spin_lock_init(&acm->lock); | ||
739 | |||
572 | acm->port_num = port_num; | 740 | acm->port_num = port_num; |
573 | 741 | ||
742 | acm->port.connect = acm_connect; | ||
743 | acm->port.disconnect = acm_disconnect; | ||
744 | acm->port.send_break = acm_send_break; | ||
745 | |||
574 | acm->port.func.name = "acm"; | 746 | acm->port.func.name = "acm"; |
575 | acm->port.func.strings = acm_strings; | 747 | acm->port.func.strings = acm_strings; |
576 | /* descriptors are per-instance copies */ | 748 | /* descriptors are per-instance copies */ |