aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManu Gautam <mgautam@codeaurora.org>2013-08-08 19:49:24 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-12 16:13:32 -0400
commit9841f37a1cca5357c1fd198b1068c12955aa632f (patch)
tree699645c1da5f3f926b1950a7d5136013e3625172
parentc10750b2bee7677fe76c97c53e079937802dc68c (diff)
usb: ehci: Add support for SINGLE_STEP_SET_FEATURE test of EHSET
The USB Embedded High-speed Host Electrical Test (EHSET) defines the SINGLE_STEP_SET_FEATURE test as follows: 1) The host enumerates the test device with VID:0x1A0A, PID:0x0108 2) The host sends the SETUP stage of a GetDescriptor(Device) 3) The device ACKs the request 4) The host issues SOFs for 15 seconds allowing the test operator to raise the scope trigger just above the SOF voltage level 5) The host sends the IN packet 6) The device sends data in response, triggering the scope 7) The host sends an ACK in response to the data This patch adds additional handling to the EHCI hub driver and allows the EHSET driver to initiate this test mode by issuing a a SetFeature request to the root hub with a Test Selector value of 0x06. From there it mimics ehci_urb_enqueue() but separately submits QTDs for the SETUP and DATA/STATUS stages in order to insert a delay in between. Signed-off-by: Manu Gautam <mgautam@codeaurora.org> Acked-by: Alan Stern <stern@rowland.harvard.edu> [jackp@codeaurora.org: imported from commit c2084930 on codeaurora.org; minor cleanup and updated author email] Signed-off-by: Jack Pham <jackp@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/host/ehci-hub.c144
-rw-r--r--drivers/usb/host/ehci-q.c101
-rw-r--r--drivers/usb/misc/ehset.c20
3 files changed, 263 insertions, 2 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 269a2e8ad0ab..d0d4cc151e71 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -712,6 +712,142 @@ ehci_hub_descriptor (
712} 712}
713 713
714/*-------------------------------------------------------------------------*/ 714/*-------------------------------------------------------------------------*/
715#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
716
717static void usb_ehset_completion(struct urb *urb)
718{
719 struct completion *done = urb->context;
720
721 complete(done);
722}
723static int submit_single_step_set_feature(
724 struct usb_hcd *hcd,
725 struct urb *urb,
726 int is_setup
727);
728
729/*
730 * Allocate and initialize a control URB. This request will be used by the
731 * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
732 * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
733 * Return NULL if failed.
734 */
735static struct urb *request_single_step_set_feature_urb(
736 struct usb_device *udev,
737 void *dr,
738 void *buf,
739 struct completion *done
740) {
741 struct urb *urb;
742 struct usb_hcd *hcd = bus_to_hcd(udev->bus);
743 struct usb_host_endpoint *ep;
744
745 urb = usb_alloc_urb(0, GFP_KERNEL);
746 if (!urb)
747 return NULL;
748
749 urb->pipe = usb_rcvctrlpipe(udev, 0);
750 ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
751 [usb_pipeendpoint(urb->pipe)];
752 if (!ep) {
753 usb_free_urb(urb);
754 return NULL;
755 }
756
757 urb->ep = ep;
758 urb->dev = udev;
759 urb->setup_packet = (void *)dr;
760 urb->transfer_buffer = buf;
761 urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
762 urb->complete = usb_ehset_completion;
763 urb->status = -EINPROGRESS;
764 urb->actual_length = 0;
765 urb->transfer_flags = URB_DIR_IN;
766 usb_get_urb(urb);
767 atomic_inc(&urb->use_count);
768 atomic_inc(&urb->dev->urbnum);
769 urb->setup_dma = dma_map_single(
770 hcd->self.controller,
771 urb->setup_packet,
772 sizeof(struct usb_ctrlrequest),
773 DMA_TO_DEVICE);
774 urb->transfer_dma = dma_map_single(
775 hcd->self.controller,
776 urb->transfer_buffer,
777 urb->transfer_buffer_length,
778 DMA_FROM_DEVICE);
779 urb->context = done;
780 return urb;
781}
782
783static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
784{
785 int retval = -ENOMEM;
786 struct usb_ctrlrequest *dr;
787 struct urb *urb;
788 struct usb_device *udev;
789 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
790 struct usb_device_descriptor *buf;
791 DECLARE_COMPLETION_ONSTACK(done);
792
793 /* Obtain udev of the rhub's child port */
794 udev = usb_hub_find_child(hcd->self.root_hub, port);
795 if (!udev) {
796 ehci_err(ehci, "No device attached to the RootHub\n");
797 return -ENODEV;
798 }
799 buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
800 if (!buf)
801 return -ENOMEM;
802
803 dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
804 if (!dr) {
805 kfree(buf);
806 return -ENOMEM;
807 }
808
809 /* Fill Setup packet for GetDescriptor */
810 dr->bRequestType = USB_DIR_IN;
811 dr->bRequest = USB_REQ_GET_DESCRIPTOR;
812 dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
813 dr->wIndex = 0;
814 dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
815 urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
816 if (!urb)
817 goto cleanup;
818
819 /* Submit just the SETUP stage */
820 retval = submit_single_step_set_feature(hcd, urb, 1);
821 if (retval)
822 goto out1;
823 if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
824 usb_kill_urb(urb);
825 retval = -ETIMEDOUT;
826 ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
827 goto out1;
828 }
829 msleep(15 * 1000);
830
831 /* Complete remaining DATA and STATUS stages using the same URB */
832 urb->status = -EINPROGRESS;
833 usb_get_urb(urb);
834 atomic_inc(&urb->use_count);
835 atomic_inc(&urb->dev->urbnum);
836 retval = submit_single_step_set_feature(hcd, urb, 0);
837 if (!retval && !wait_for_completion_timeout(&done,
838 msecs_to_jiffies(2000))) {
839 usb_kill_urb(urb);
840 retval = -ETIMEDOUT;
841 ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
842 }
843out1:
844 usb_free_urb(urb);
845cleanup:
846 kfree(dr);
847 kfree(buf);
848 return retval;
849}
850/*-------------------------------------------------------------------------*/
715 851
716static int ehci_hub_control ( 852static int ehci_hub_control (
717 struct usb_hcd *hcd, 853 struct usb_hcd *hcd,
@@ -1086,7 +1222,13 @@ static int ehci_hub_control (
1086 * about the EHCI-specific stuff. 1222 * about the EHCI-specific stuff.
1087 */ 1223 */
1088 case USB_PORT_FEAT_TEST: 1224 case USB_PORT_FEAT_TEST:
1089 if (!selector || selector > 5) 1225 if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
1226 spin_unlock_irqrestore(&ehci->lock, flags);
1227 retval = ehset_single_step_set_feature(hcd,
1228 wIndex);
1229 spin_lock_irqsave(&ehci->lock, flags);
1230 break;
1231 } else if (!selector || selector > 5)
1090 goto error; 1232 goto error;
1091 spin_unlock_irqrestore(&ehci->lock, flags); 1233 spin_unlock_irqrestore(&ehci->lock, flags);
1092 ehci_quiesce(ehci); 1234 ehci_quiesce(ehci);
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index b637a65e1e52..903d00dd6683 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1139,6 +1139,107 @@ submit_async (
1139} 1139}
1140 1140
1141/*-------------------------------------------------------------------------*/ 1141/*-------------------------------------------------------------------------*/
1142/*
1143 * This function creates the qtds and submits them for the
1144 * SINGLE_STEP_SET_FEATURE Test.
1145 * This is done in two parts: first SETUP req for GetDesc is sent then
1146 * 15 seconds later, the IN stage for GetDesc starts to req data from dev
1147 *
1148 * is_setup : i/p arguement decides which of the two stage needs to be
1149 * performed; TRUE - SETUP and FALSE - IN+STATUS
1150 * Returns 0 if success
1151 */
1152static int submit_single_step_set_feature(
1153 struct usb_hcd *hcd,
1154 struct urb *urb,
1155 int is_setup
1156) {
1157 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
1158 struct list_head qtd_list;
1159 struct list_head *head;
1160
1161 struct ehci_qtd *qtd, *qtd_prev;
1162 dma_addr_t buf;
1163 int len, maxpacket;
1164 u32 token;
1165
1166 INIT_LIST_HEAD(&qtd_list);
1167 head = &qtd_list;
1168
1169 /* URBs map to sequences of QTDs: one logical transaction */
1170 qtd = ehci_qtd_alloc(ehci, GFP_KERNEL);
1171 if (unlikely(!qtd))
1172 return -1;
1173 list_add_tail(&qtd->qtd_list, head);
1174 qtd->urb = urb;
1175
1176 token = QTD_STS_ACTIVE;
1177 token |= (EHCI_TUNE_CERR << 10);
1178
1179 len = urb->transfer_buffer_length;
1180 /*
1181 * Check if the request is to perform just the SETUP stage (getDesc)
1182 * as in SINGLE_STEP_SET_FEATURE test, DATA stage (IN) happens
1183 * 15 secs after the setup
1184 */
1185 if (is_setup) {
1186 /* SETUP pid */
1187 qtd_fill(ehci, qtd, urb->setup_dma,
1188 sizeof(struct usb_ctrlrequest),
1189 token | (2 /* "setup" */ << 8), 8);
1190
1191 submit_async(ehci, urb, &qtd_list, GFP_ATOMIC);
1192 return 0; /*Return now; we shall come back after 15 seconds*/
1193 }
1194
1195 /*
1196 * IN: data transfer stage: buffer setup : start the IN txn phase for
1197 * the get_Desc SETUP which was sent 15seconds back
1198 */
1199 token ^= QTD_TOGGLE; /*We need to start IN with DATA-1 Pid-sequence*/
1200 buf = urb->transfer_dma;
1201
1202 token |= (1 /* "in" */ << 8); /*This is IN stage*/
1203
1204 maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, 0));
1205
1206 qtd_fill(ehci, qtd, buf, len, token, maxpacket);
1207
1208 /*
1209 * Our IN phase shall always be a short read; so keep the queue running
1210 * and let it advance to the next qtd which zero length OUT status
1211 */
1212 qtd->hw_alt_next = EHCI_LIST_END(ehci);
1213
1214 /* STATUS stage for GetDesc control request */
1215 token ^= 0x0100; /* "in" <--> "out" */
1216 token |= QTD_TOGGLE; /* force DATA1 */
1217
1218 qtd_prev = qtd;
1219 qtd = ehci_qtd_alloc(ehci, GFP_ATOMIC);
1220 if (unlikely(!qtd))
1221 goto cleanup;
1222 qtd->urb = urb;
1223 qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
1224 list_add_tail(&qtd->qtd_list, head);
1225
1226 /* dont fill any data in such packets */
1227 qtd_fill(ehci, qtd, 0, 0, token, 0);
1228
1229 /* by default, enable interrupt on urb completion */
1230 if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT)))
1231 qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
1232
1233 submit_async(ehci, urb, &qtd_list, GFP_KERNEL);
1234
1235 return 0;
1236
1237cleanup:
1238 qtd_list_free(ehci, urb, head);
1239 return -1;
1240}
1241
1242/*-------------------------------------------------------------------------*/
1142 1243
1143static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) 1244static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
1144{ 1245{
diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c
index d2864b3f41fe..c31b4a33e6bb 100644
--- a/drivers/usb/misc/ehset.c
+++ b/drivers/usb/misc/ehset.c
@@ -96,7 +96,25 @@ static int ehset_probe(struct usb_interface *intf,
96 kfree(buf); 96 kfree(buf);
97 break; 97 break;
98 case TEST_SINGLE_STEP_SET_FEATURE: 98 case TEST_SINGLE_STEP_SET_FEATURE:
99 /* unsupported for now */ 99 /*
100 * GetDescriptor SETUP request -> 15secs delay -> IN & STATUS
101 *
102 * Note, this test is only supported on root hubs since the
103 * SetPortFeature handling can only be done inside the HCD's
104 * hub_control callback function.
105 */
106 if (hub_udev != dev->bus->root_hub) {
107 dev_err(&intf->dev, "SINGLE_STEP_SET_FEATURE test only supported on root hub\n");
108 break;
109 }
110
111 ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
112 USB_REQ_SET_FEATURE, USB_RT_PORT,
113 USB_PORT_FEAT_TEST,
114 (6 << 8) | portnum,
115 NULL, 0, 60 * 1000);
116
117 break;
100 default: 118 default:
101 dev_err(&intf->dev, "%s: unsupported PID: 0x%x\n", 119 dev_err(&intf->dev, "%s: unsupported PID: 0x%x\n",
102 __func__, test_pid); 120 __func__, test_pid);