diff options
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 144 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 101 | ||||
-rw-r--r-- | drivers/usb/misc/ehset.c | 20 |
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 | |||
717 | static void usb_ehset_completion(struct urb *urb) | ||
718 | { | ||
719 | struct completion *done = urb->context; | ||
720 | |||
721 | complete(done); | ||
722 | } | ||
723 | static 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 | */ | ||
735 | static 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 | |||
783 | static 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 | } | ||
843 | out1: | ||
844 | usb_free_urb(urb); | ||
845 | cleanup: | ||
846 | kfree(dr); | ||
847 | kfree(buf); | ||
848 | return retval; | ||
849 | } | ||
850 | /*-------------------------------------------------------------------------*/ | ||
715 | 851 | ||
716 | static int ehci_hub_control ( | 852 | static 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 | */ | ||
1152 | static 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 | |||
1237 | cleanup: | ||
1238 | qtd_list_free(ehci, urb, head); | ||
1239 | return -1; | ||
1240 | } | ||
1241 | |||
1242 | /*-------------------------------------------------------------------------*/ | ||
1142 | 1243 | ||
1143 | static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) | 1244 | static 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); |