diff options
author | Gregory Herrero <gregory.herrero@intel.com> | 2015-01-30 03:09:28 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2015-01-30 11:30:44 -0500 |
commit | 9e14d0a566f48d33a9253096963b3b8199e4df57 (patch) | |
tree | 22e316e69be3e2b907a758ee64914782b3a20696 | |
parent | c00dd4a6ec20a58ed38be85f8e47efda40625f17 (diff) |
usb: dwc2: gadget: add TEST_MODE feature support
Handle SET_FEATURE TEST_MODE request sent by the host.
Slightly rework FEATURE request handling to allow parsing
other request types than Endpoint.
Also add a debugfs to change test mode value from user space.
Tested-by: Robert Baldyga <r.baldyga@samsung.com>
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Gregory Herrero <gregory.herrero@intel.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r-- | drivers/usb/dwc2/core.h | 4 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 189 |
2 files changed, 186 insertions, 7 deletions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index f09b3deffdee..c750fd3d88b3 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h | |||
@@ -567,12 +567,14 @@ struct dwc2_hw_params { | |||
567 | * @num_of_eps: Number of available EPs (excluding EP0) | 567 | * @num_of_eps: Number of available EPs (excluding EP0) |
568 | * @debug_root: Root directrory for debugfs. | 568 | * @debug_root: Root directrory for debugfs. |
569 | * @debug_file: Main status file for debugfs. | 569 | * @debug_file: Main status file for debugfs. |
570 | * @debug_testmode: Testmode status file for debugfs. | ||
570 | * @debug_fifo: FIFO status file for debugfs. | 571 | * @debug_fifo: FIFO status file for debugfs. |
571 | * @ep0_reply: Request used for ep0 reply. | 572 | * @ep0_reply: Request used for ep0 reply. |
572 | * @ep0_buff: Buffer for EP0 reply data, if needed. | 573 | * @ep0_buff: Buffer for EP0 reply data, if needed. |
573 | * @ctrl_buff: Buffer for EP0 control requests. | 574 | * @ctrl_buff: Buffer for EP0 control requests. |
574 | * @ctrl_req: Request for EP0 control packets. | 575 | * @ctrl_req: Request for EP0 control packets. |
575 | * @ep0_state: EP0 control transfers state | 576 | * @ep0_state: EP0 control transfers state |
577 | * @test_mode: USB test mode requested by the host | ||
576 | * @last_rst: Time of last reset | 578 | * @last_rst: Time of last reset |
577 | * @eps: The endpoints being supplied to the gadget framework | 579 | * @eps: The endpoints being supplied to the gadget framework |
578 | * @g_using_dma: Indicate if dma usage is enabled | 580 | * @g_using_dma: Indicate if dma usage is enabled |
@@ -610,6 +612,7 @@ struct dwc2_hsotg { | |||
610 | 612 | ||
611 | struct dentry *debug_root; | 613 | struct dentry *debug_root; |
612 | struct dentry *debug_file; | 614 | struct dentry *debug_file; |
615 | struct dentry *debug_testmode; | ||
613 | struct dentry *debug_fifo; | 616 | struct dentry *debug_fifo; |
614 | 617 | ||
615 | /* DWC OTG HW Release versions */ | 618 | /* DWC OTG HW Release versions */ |
@@ -706,6 +709,7 @@ struct dwc2_hsotg { | |||
706 | void *ep0_buff; | 709 | void *ep0_buff; |
707 | void *ctrl_buff; | 710 | void *ctrl_buff; |
708 | enum dwc2_ep0_state ep0_state; | 711 | enum dwc2_ep0_state ep0_state; |
712 | u8 test_mode; | ||
709 | 713 | ||
710 | struct usb_gadget gadget; | 714 | struct usb_gadget gadget; |
711 | unsigned int enabled:1; | 715 | unsigned int enabled:1; |
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 02d0e9af9242..67ea258335ad 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/usb/gadget.h> | 35 | #include <linux/usb/gadget.h> |
36 | #include <linux/usb/phy.h> | 36 | #include <linux/usb/phy.h> |
37 | #include <linux/platform_data/s3c-hsotg.h> | 37 | #include <linux/platform_data/s3c-hsotg.h> |
38 | #include <linux/uaccess.h> | ||
38 | 39 | ||
39 | #include "core.h" | 40 | #include "core.h" |
40 | #include "hw.h" | 41 | #include "hw.h" |
@@ -835,6 +836,32 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, | |||
835 | } | 836 | } |
836 | 837 | ||
837 | /** | 838 | /** |
839 | * s3c_hsotg_set_test_mode - Enable usb Test Modes | ||
840 | * @hsotg: The driver state. | ||
841 | * @testmode: requested usb test mode | ||
842 | * Enable usb Test Mode requested by the Host. | ||
843 | */ | ||
844 | static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) | ||
845 | { | ||
846 | int dctl = readl(hsotg->regs + DCTL); | ||
847 | |||
848 | dctl &= ~DCTL_TSTCTL_MASK; | ||
849 | switch (testmode) { | ||
850 | case TEST_J: | ||
851 | case TEST_K: | ||
852 | case TEST_SE0_NAK: | ||
853 | case TEST_PACKET: | ||
854 | case TEST_FORCE_EN: | ||
855 | dctl |= testmode << DCTL_TSTCTL_SHIFT; | ||
856 | break; | ||
857 | default: | ||
858 | return -EINVAL; | ||
859 | } | ||
860 | writel(dctl, hsotg->regs + DCTL); | ||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | /** | ||
838 | * s3c_hsotg_send_reply - send reply to control request | 865 | * s3c_hsotg_send_reply - send reply to control request |
839 | * @hsotg: The device state | 866 | * @hsotg: The device state |
840 | * @ep: Endpoint 0 | 867 | * @ep: Endpoint 0 |
@@ -968,19 +995,48 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, | |||
968 | struct s3c_hsotg_ep *ep; | 995 | struct s3c_hsotg_ep *ep; |
969 | int ret; | 996 | int ret; |
970 | bool halted; | 997 | bool halted; |
998 | u32 recip; | ||
999 | u32 wValue; | ||
1000 | u32 wIndex; | ||
971 | 1001 | ||
972 | dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", | 1002 | dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", |
973 | __func__, set ? "SET" : "CLEAR"); | 1003 | __func__, set ? "SET" : "CLEAR"); |
974 | 1004 | ||
975 | if (ctrl->bRequestType == USB_RECIP_ENDPOINT) { | 1005 | wValue = le16_to_cpu(ctrl->wValue); |
976 | ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex)); | 1006 | wIndex = le16_to_cpu(ctrl->wIndex); |
1007 | recip = ctrl->bRequestType & USB_RECIP_MASK; | ||
1008 | |||
1009 | switch (recip) { | ||
1010 | case USB_RECIP_DEVICE: | ||
1011 | switch (wValue) { | ||
1012 | case USB_DEVICE_TEST_MODE: | ||
1013 | if ((wIndex & 0xff) != 0) | ||
1014 | return -EINVAL; | ||
1015 | if (!set) | ||
1016 | return -EINVAL; | ||
1017 | |||
1018 | hsotg->test_mode = wIndex >> 8; | ||
1019 | ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); | ||
1020 | if (ret) { | ||
1021 | dev_err(hsotg->dev, | ||
1022 | "%s: failed to send reply\n", __func__); | ||
1023 | return ret; | ||
1024 | } | ||
1025 | break; | ||
1026 | default: | ||
1027 | return -ENOENT; | ||
1028 | } | ||
1029 | break; | ||
1030 | |||
1031 | case USB_RECIP_ENDPOINT: | ||
1032 | ep = ep_from_windex(hsotg, wIndex); | ||
977 | if (!ep) { | 1033 | if (!ep) { |
978 | dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n", | 1034 | dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n", |
979 | __func__, le16_to_cpu(ctrl->wIndex)); | 1035 | __func__, wIndex); |
980 | return -ENOENT; | 1036 | return -ENOENT; |
981 | } | 1037 | } |
982 | 1038 | ||
983 | switch (le16_to_cpu(ctrl->wValue)) { | 1039 | switch (wValue) { |
984 | case USB_ENDPOINT_HALT: | 1040 | case USB_ENDPOINT_HALT: |
985 | halted = ep->halted; | 1041 | halted = ep->halted; |
986 | 1042 | ||
@@ -1031,9 +1087,10 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, | |||
1031 | default: | 1087 | default: |
1032 | return -ENOENT; | 1088 | return -ENOENT; |
1033 | } | 1089 | } |
1034 | } else | 1090 | break; |
1035 | return -ENOENT; /* currently only deal with endpoint */ | 1091 | default: |
1036 | 1092 | return -ENOENT; | |
1093 | } | ||
1037 | return 1; | 1094 | return 1; |
1038 | } | 1095 | } |
1039 | 1096 | ||
@@ -1734,6 +1791,17 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, | |||
1734 | if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) { | 1791 | if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) { |
1735 | dev_dbg(hsotg->dev, "zlp packet sent\n"); | 1792 | dev_dbg(hsotg->dev, "zlp packet sent\n"); |
1736 | s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); | 1793 | s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); |
1794 | if (hsotg->test_mode) { | ||
1795 | int ret; | ||
1796 | |||
1797 | ret = s3c_hsotg_set_test_mode(hsotg, hsotg->test_mode); | ||
1798 | if (ret < 0) { | ||
1799 | dev_dbg(hsotg->dev, "Invalid Test #%d\n", | ||
1800 | hsotg->test_mode); | ||
1801 | s3c_hsotg_stall_ep0(hsotg); | ||
1802 | return; | ||
1803 | } | ||
1804 | } | ||
1737 | s3c_hsotg_enqueue_setup(hsotg); | 1805 | s3c_hsotg_enqueue_setup(hsotg); |
1738 | return; | 1806 | return; |
1739 | } | 1807 | } |
@@ -2045,6 +2113,7 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) | |||
2045 | return; | 2113 | return; |
2046 | 2114 | ||
2047 | hsotg->connected = 0; | 2115 | hsotg->connected = 0; |
2116 | hsotg->test_mode = 0; | ||
2048 | 2117 | ||
2049 | for (ep = 0; ep < hsotg->num_of_eps; ep++) { | 2118 | for (ep = 0; ep < hsotg->num_of_eps; ep++) { |
2050 | if (hsotg->eps_in[ep]) | 2119 | if (hsotg->eps_in[ep]) |
@@ -3257,6 +3326,103 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) | |||
3257 | } | 3326 | } |
3258 | 3327 | ||
3259 | /** | 3328 | /** |
3329 | * testmode_write - debugfs: change usb test mode | ||
3330 | * @seq: The seq file to write to. | ||
3331 | * @v: Unused parameter. | ||
3332 | * | ||
3333 | * This debugfs entry modify the current usb test mode. | ||
3334 | */ | ||
3335 | static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t | ||
3336 | count, loff_t *ppos) | ||
3337 | { | ||
3338 | struct seq_file *s = file->private_data; | ||
3339 | struct dwc2_hsotg *hsotg = s->private; | ||
3340 | unsigned long flags; | ||
3341 | u32 testmode = 0; | ||
3342 | char buf[32]; | ||
3343 | |||
3344 | if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) | ||
3345 | return -EFAULT; | ||
3346 | |||
3347 | if (!strncmp(buf, "test_j", 6)) | ||
3348 | testmode = TEST_J; | ||
3349 | else if (!strncmp(buf, "test_k", 6)) | ||
3350 | testmode = TEST_K; | ||
3351 | else if (!strncmp(buf, "test_se0_nak", 12)) | ||
3352 | testmode = TEST_SE0_NAK; | ||
3353 | else if (!strncmp(buf, "test_packet", 11)) | ||
3354 | testmode = TEST_PACKET; | ||
3355 | else if (!strncmp(buf, "test_force_enable", 17)) | ||
3356 | testmode = TEST_FORCE_EN; | ||
3357 | else | ||
3358 | testmode = 0; | ||
3359 | |||
3360 | spin_lock_irqsave(&hsotg->lock, flags); | ||
3361 | s3c_hsotg_set_test_mode(hsotg, testmode); | ||
3362 | spin_unlock_irqrestore(&hsotg->lock, flags); | ||
3363 | return count; | ||
3364 | } | ||
3365 | |||
3366 | /** | ||
3367 | * testmode_show - debugfs: show usb test mode state | ||
3368 | * @seq: The seq file to write to. | ||
3369 | * @v: Unused parameter. | ||
3370 | * | ||
3371 | * This debugfs entry shows which usb test mode is currently enabled. | ||
3372 | */ | ||
3373 | static int testmode_show(struct seq_file *s, void *unused) | ||
3374 | { | ||
3375 | struct dwc2_hsotg *hsotg = s->private; | ||
3376 | unsigned long flags; | ||
3377 | int dctl; | ||
3378 | |||
3379 | spin_lock_irqsave(&hsotg->lock, flags); | ||
3380 | dctl = readl(hsotg->regs + DCTL); | ||
3381 | dctl &= DCTL_TSTCTL_MASK; | ||
3382 | dctl >>= DCTL_TSTCTL_SHIFT; | ||
3383 | spin_unlock_irqrestore(&hsotg->lock, flags); | ||
3384 | |||
3385 | switch (dctl) { | ||
3386 | case 0: | ||
3387 | seq_puts(s, "no test\n"); | ||
3388 | break; | ||
3389 | case TEST_J: | ||
3390 | seq_puts(s, "test_j\n"); | ||
3391 | break; | ||
3392 | case TEST_K: | ||
3393 | seq_puts(s, "test_k\n"); | ||
3394 | break; | ||
3395 | case TEST_SE0_NAK: | ||
3396 | seq_puts(s, "test_se0_nak\n"); | ||
3397 | break; | ||
3398 | case TEST_PACKET: | ||
3399 | seq_puts(s, "test_packet\n"); | ||
3400 | break; | ||
3401 | case TEST_FORCE_EN: | ||
3402 | seq_puts(s, "test_force_enable\n"); | ||
3403 | break; | ||
3404 | default: | ||
3405 | seq_printf(s, "UNKNOWN %d\n", dctl); | ||
3406 | } | ||
3407 | |||
3408 | return 0; | ||
3409 | } | ||
3410 | |||
3411 | static int testmode_open(struct inode *inode, struct file *file) | ||
3412 | { | ||
3413 | return single_open(file, testmode_show, inode->i_private); | ||
3414 | } | ||
3415 | |||
3416 | static const struct file_operations testmode_fops = { | ||
3417 | .owner = THIS_MODULE, | ||
3418 | .open = testmode_open, | ||
3419 | .write = testmode_write, | ||
3420 | .read = seq_read, | ||
3421 | .llseek = seq_lseek, | ||
3422 | .release = single_release, | ||
3423 | }; | ||
3424 | |||
3425 | /** | ||
3260 | * state_show - debugfs: show overall driver and device state. | 3426 | * state_show - debugfs: show overall driver and device state. |
3261 | * @seq: The seq file to write to. | 3427 | * @seq: The seq file to write to. |
3262 | * @v: Unused parameter. | 3428 | * @v: Unused parameter. |
@@ -3490,6 +3656,14 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) | |||
3490 | if (IS_ERR(hsotg->debug_file)) | 3656 | if (IS_ERR(hsotg->debug_file)) |
3491 | dev_err(hsotg->dev, "%s: failed to create state\n", __func__); | 3657 | dev_err(hsotg->dev, "%s: failed to create state\n", __func__); |
3492 | 3658 | ||
3659 | hsotg->debug_testmode = debugfs_create_file("testmode", | ||
3660 | S_IRUGO | S_IWUSR, root, | ||
3661 | hsotg, &testmode_fops); | ||
3662 | |||
3663 | if (IS_ERR(hsotg->debug_testmode)) | ||
3664 | dev_err(hsotg->dev, "%s: failed to create testmode\n", | ||
3665 | __func__); | ||
3666 | |||
3493 | hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root, | 3667 | hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root, |
3494 | hsotg, &fifo_fops); | 3668 | hsotg, &fifo_fops); |
3495 | 3669 | ||
@@ -3544,6 +3718,7 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) | |||
3544 | } | 3718 | } |
3545 | 3719 | ||
3546 | debugfs_remove(hsotg->debug_file); | 3720 | debugfs_remove(hsotg->debug_file); |
3721 | debugfs_remove(hsotg->debug_testmode); | ||
3547 | debugfs_remove(hsotg->debug_fifo); | 3722 | debugfs_remove(hsotg->debug_fifo); |
3548 | debugfs_remove(hsotg->debug_root); | 3723 | debugfs_remove(hsotg->debug_root); |
3549 | } | 3724 | } |