1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** USBX Component                                                        */
16 /**                                                                       */
17 /**   OHCI Controller Driver                                              */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 
23 /* Include necessary system files.  */
24 
25 #define UX_SOURCE_CODE
26 
27 #include "ux_api.h"
28 #include "ux_hcd_ohci.h"
29 #include "ux_host_stack.h"
30 
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _ux_hcd_ohci_request_control_transfer               PORTABLE C      */
37 /*                                                           6.1.10       */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*     This function performs a control transfer from a transfer request. */
45 /*     The USB control transfer is in 3 phases (setup, data, status).     */
46 /*     This function will chain all phases of the control sequence before */
47 /*     setting the OHCI endpoint as a candidate for transfer.             */
48 /*                                                                        */
49 /*     The maximum aggregated size of a data payload in OHCI is 4K. We    */
50 /*     are assuming that this size will be sufficient to contain the      */
51 /*     control packet.                                                    */
52 /*                                                                        */
53 /*  INPUT                                                                 */
54 /*                                                                        */
55 /*    hcd_ohci                              Pointer to OHCI controller    */
56 /*    transfer_request                      Pointer to transfer request   */
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    Completion Status                                                   */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _ux_hcd_ohci_register_read            Read OHCI register            */
65 /*    _ux_hcd_ohci_register_write           Write OHCI register           */
66 /*    _ux_hcd_ohci_regular_td_obtain        Get regular TD                */
67 /*    _ux_host_stack_transfer_request_abort Abort transfer request        */
68 /*    _ux_utility_memory_allocate           Allocate memory block         */
69 /*    _ux_utility_memory_free               Release memory block          */
70 /*    _ux_utility_physical_address          Get physical address          */
71 /*    _ux_host_semaphore_get                Get semaphore                 */
72 /*    _ux_utility_short_put                 Write 16-bit value            */
73 /*    _ux_utility_virtual_address           Get virtual address           */
74 /*                                                                        */
75 /*  CALLED BY                                                             */
76 /*                                                                        */
77 /*    OHCI Controller Driver                                              */
78 /*                                                                        */
79 /*  RELEASE HISTORY                                                       */
80 /*                                                                        */
81 /*    DATE              NAME                      DESCRIPTION             */
82 /*                                                                        */
83 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
84 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
85 /*                                            prefixed UX to MS_TO_TICK,  */
86 /*                                            resulting in version 6.1    */
87 /*  11-09-2020     Chaoqiong Xiao           Modified comment(s),          */
88 /*                                            fixed compile warnings,     */
89 /*                                            resulting in version 6.1.2  */
90 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
91 /*                                            refined macros names,       */
92 /*                                            resulting in version 6.1.10 */
93 /*                                                                        */
94 /**************************************************************************/
_ux_hcd_ohci_request_control_transfer(UX_HCD_OHCI * hcd_ohci,UX_TRANSFER * transfer_request)95 UINT  _ux_hcd_ohci_request_control_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request)
96 {
97 
98 UX_DEVICE       *device;
99 UX_ENDPOINT     *endpoint;
100 UCHAR *         setup_request;
101 UX_OHCI_ED      *ed;
102 UX_OHCI_TD      *setup_td;
103 UX_OHCI_TD      *chain_td;
104 UX_OHCI_TD      *data_td;
105 UX_OHCI_TD      *tail_td;
106 UX_OHCI_TD      *status_td;
107 ULONG           ohci_register;
108 UINT            status;
109 
110 
111     /* Get the pointer to the Endpoint and the Device.  */
112     endpoint =  (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint;
113     device =    endpoint -> ux_endpoint_device;
114 
115     /* Now get the physical ED attached to this endpoint.  */
116     ed =  endpoint -> ux_endpoint_ed;
117 
118     /* Build the SETUP packet (phase 1 of the control transfer).  */
119     setup_request =  _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_SETUP_SIZE);
120     if (setup_request == UX_NULL)
121         return(UX_MEMORY_INSUFFICIENT);
122 
123     *setup_request =                            (UCHAR)transfer_request -> ux_transfer_request_function;
124     *(setup_request + UX_SETUP_REQUEST_TYPE) =  (UCHAR)transfer_request -> ux_transfer_request_type;
125     *(setup_request + UX_SETUP_REQUEST) =       (UCHAR)transfer_request -> ux_transfer_request_function;
126     _ux_utility_short_put(setup_request + UX_SETUP_VALUE, (USHORT)transfer_request -> ux_transfer_request_value);
127     _ux_utility_short_put(setup_request + UX_SETUP_INDEX, (USHORT)transfer_request -> ux_transfer_request_index);
128     _ux_utility_short_put(setup_request + UX_SETUP_LENGTH, (USHORT) transfer_request -> ux_transfer_request_requested_length);
129 
130     /* Set the ED address and MPS values since they may have changed.
131        The ED direction will be set from the TD.  */
132     ed -> ux_ohci_ed_dw0 =  device -> ux_device_address |  ((ULONG) endpoint -> ux_endpoint_descriptor.bEndpointAddress << 7) |
133                                                            ((ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize << 16);
134 
135     /* Refresh the speed.  */
136     if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE)
137         ed -> ux_ohci_ed_dw0 |=  UX_OHCI_ED_LOW_SPEED;
138 
139     /* Use the TD pointer by ed -> tail for our setup TD and chain from this one on.  */
140     setup_td =  _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td);
141     setup_td -> ux_ohci_td_dw0 =  UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_DATA0 | UX_OHCI_TD_R;
142     setup_td -> ux_ohci_td_cbp =  _ux_utility_physical_address(setup_request);
143     setup_td -> ux_ohci_td_be =   setup_td -> ux_ohci_td_cbp + UX_SETUP_SIZE - 1;
144     chain_td =  setup_td;
145 
146     /* Attach the endpoint and transfer request to the TD.  */
147     setup_td -> ux_ohci_td_transfer_request =  transfer_request;
148     setup_td -> ux_ohci_td_ed =  ed;
149 
150     /* Mark the TD with the SETUP phase.  */
151     setup_td -> ux_ohci_td_status |=  UX_OHCI_TD_SETUP_PHASE;
152 
153     /* Check if there is a data phase, if not jump to status phase.  */
154     data_td =  UX_NULL;
155     if (transfer_request -> ux_transfer_request_requested_length != 0)
156     {
157 
158         data_td =  _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
159         if (data_td == UX_NULL)
160         {
161 
162             _ux_utility_memory_free(setup_request);
163             return(UX_NO_TD_AVAILABLE);
164         }
165 
166         /* Attach the endpoint and transfer request to the TD.  */
167         data_td -> ux_ohci_td_transfer_request =  transfer_request;
168         data_td -> ux_ohci_td_ed =  ed;
169 
170         /* Mark the TD with the DATA phase.  */
171         data_td -> ux_ohci_td_status |=  UX_OHCI_TD_DATA_PHASE;
172 
173         /* Program the control bits of the TD.  */
174         if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
175 
176             data_td -> ux_ohci_td_dw0 =  UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_IN | UX_OHCI_TD_DATA1 | UX_OHCI_TD_R;
177         else
178 
179             data_td -> ux_ohci_td_dw0 =  UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_OUT | UX_OHCI_TD_DATA1 | UX_OHCI_TD_R;
180 
181         /* Attach the CBP and BE values to the TD.  */
182         data_td -> ux_ohci_td_cbp =  _ux_utility_physical_address(transfer_request -> ux_transfer_request_data_pointer);
183         data_td -> ux_ohci_td_be =   data_td -> ux_ohci_td_cbp + transfer_request -> ux_transfer_request_requested_length - 1;
184 
185         /* Update the length of the transfer for this TD.  */
186         data_td -> ux_ohci_td_length =  transfer_request -> ux_transfer_request_requested_length;
187 
188         /* Chain the TD.  */
189         chain_td -> ux_ohci_td_next_td =  _ux_utility_physical_address(data_td);
190         chain_td =  data_td;
191     }
192 
193     /* Now, program the status phase.  */
194     status_td =  _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
195     if (status_td == UX_NULL)
196     {
197 
198         _ux_utility_memory_free(setup_request);
199         if (data_td != UX_NULL)
200             data_td -> ux_ohci_td_status =  UX_UNUSED;
201         return(UX_NO_TD_AVAILABLE);
202     }
203 
204     /* Attach the endpoint and transfer request to the TD.  */
205     status_td -> ux_ohci_td_transfer_request =  transfer_request;
206     status_td -> ux_ohci_td_ed =  ed;
207 
208     /* Mark the TD with the STATUS phase.  */
209     status_td -> ux_ohci_td_status |=  UX_OHCI_TD_STATUS_PHASE;
210 
211     /* The direction of the status phase is IN if data phase is OUT and
212        vice versa.  */
213     if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
214 
215         status_td -> ux_ohci_td_dw0 =  UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_OUT | UX_OHCI_TD_DATA1;
216     else
217 
218         status_td -> ux_ohci_td_dw0 =  UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_IN | UX_OHCI_TD_DATA1;
219 
220     /* No data payload for the status phase.  */
221     status_td -> ux_ohci_td_cbp =  0;
222     status_td -> ux_ohci_td_be =  0;
223 
224     /* Hook the status phase to the previous TD.  */
225     chain_td -> ux_ohci_td_next_td =  _ux_utility_physical_address(status_td);
226 
227     /* Since we have consumed out tail TD for the setup packet, we must get another
228        one and hook it to the ED's tail.  */
229     tail_td =  _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
230     if (tail_td == UX_NULL)
231     {
232 
233         _ux_utility_memory_free(setup_request);
234         if (data_td != UX_NULL)
235             data_td -> ux_ohci_td_status =  UX_UNUSED;
236         status_td -> ux_ohci_td_status =  UX_UNUSED;
237         return(UX_NO_TD_AVAILABLE);
238     }
239 
240     /* Hook the new TD to the status TD.  */
241     status_td -> ux_ohci_td_next_td =  _ux_utility_physical_address(tail_td);
242 
243     /* At this stage, the Head and Tail in the ED are still the same and
244        the OHCI controller will skip this ED until we have hooked the new
245        tail TD.  */
246     ed -> ux_ohci_ed_tail_td =  _ux_utility_physical_address(tail_td);
247 
248     /* Now, we must tell the OHCI controller that there is something in the
249        control queue.  */
250     ohci_register =  _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_COMMAND_STATUS);
251     ohci_register |=  OHCI_HC_CS_CLF;
252     _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, ohci_register);
253 
254    /* Wait for the completion of the transfer request.  */
255     status =  _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT));
256 
257     /* If the semaphore did not succeed we probably have a time out.  */
258     if (status != UX_SUCCESS)
259     {
260 
261         /* All transfers pending need to abort. There may have been a partial transfer. */
262         _ux_host_stack_transfer_request_abort(transfer_request);
263 
264         /* There was an error, return to the caller.  */
265         transfer_request -> ux_transfer_request_completion_code =  UX_TRANSFER_TIMEOUT;
266 
267         /* Error trap. */
268         _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_TIMEOUT);
269 
270         /* If trace is enabled, insert this event into the trace buffer.  */
271         UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0)
272 
273     }
274 
275     /* Free the resources.  */
276     _ux_utility_memory_free(setup_request);
277 
278     /* Return the completion status.  */
279     return(transfer_request -> ux_transfer_request_completion_code);
280 }
281 
282