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_bulk_transfer                  PORTABLE C      */
37 /*                                                           6.1          */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*     This function performs a bulk transfer request. A bulk transfer    */
45 /*     can be larger than the size of the OHCI buffer so it may be        */
46 /*     required to chain multiple tds to accommodate this transfer        */
47 /*     request. A bulk transfer is non blocking, so we return before the  */
48 /*     transfer request is completed.                                     */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    hcd_ohci                              Pointer to OHCI controller    */
53 /*    transfer_request                      Pointer to transfer request   */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    Completion Status                                                   */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _ux_hcd_ohci_register_read            Read OHCI register            */
62 /*    _ux_hcd_ohci_register_write           Write OHCI register           */
63 /*    _ux_hcd_ohci_regular_td_obtain        Get regular TD                */
64 /*    _ux_utility_physical_address          Get physical address          */
65 /*    _ux_utility_virtual_address           Get virtual address           */
66 /*                                                                        */
67 /*  CALLED BY                                                             */
68 /*                                                                        */
69 /*    OHCI Controller Driver                                              */
70 /*                                                                        */
71 /*  RELEASE HISTORY                                                       */
72 /*                                                                        */
73 /*    DATE              NAME                      DESCRIPTION             */
74 /*                                                                        */
75 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
76 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
77 /*                                            resulting in version 6.1    */
78 /*                                                                        */
79 /**************************************************************************/
_ux_hcd_ohci_request_bulk_transfer(UX_HCD_OHCI * hcd_ohci,UX_TRANSFER * transfer_request)80 UINT  _ux_hcd_ohci_request_bulk_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request)
81 {
82 
83 UX_ENDPOINT     *endpoint;
84 UX_OHCI_TD      *data_td;
85 UX_OHCI_TD      *start_data_td;
86 UX_OHCI_TD      *next_data_td;
87 UX_OHCI_TD      *previous_td;
88 UX_OHCI_TD      *tail_td;
89 UX_OHCI_ED      *ed;
90 ULONG           transfer_request_payload_length;
91 ULONG           bulk_packet_payload_length;
92 UCHAR *         data_pointer;
93 ULONG           ohci_register;
94 ULONG           zlp_flag;
95 
96 
97     /* Get the pointer to the Endpoint.  */
98     endpoint =  (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint;
99 
100     /* Now get the physical ED attached to this endpoint.  */
101     ed =  endpoint -> ux_endpoint_ed;
102 
103     /* Use the TD pointer by ed -> tail for the first TD of this transfer
104         and chain from this one on.  */
105     data_td =  _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td);
106     previous_td =  data_td;
107 
108     /* Reset the first obtained data TD in case there is a TD shortage while building the list of tds.  */
109     start_data_td =  0;
110 
111     /* It may take more than one TD if the transfer_request length is more than the
112        maximum length for a OHCI TD (this is irrelevant of the MaxPacketSize value in
113        the endpoint descriptor). OHCI data payload has a maximum size of 4K.  */
114     transfer_request_payload_length =  transfer_request -> ux_transfer_request_requested_length;
115     data_pointer =  transfer_request -> ux_transfer_request_data_pointer;
116 
117     /* Check for ZLP condition.  */
118     if (transfer_request_payload_length == 0)
119 
120         /* We have a zlp condition.  */
121         zlp_flag = UX_TRUE;
122     else
123 
124         /* We do not have a zlp.  */
125         zlp_flag = UX_FALSE;
126 
127     /* Build all necessary TDs.  */
128     while ((transfer_request_payload_length != 0) || zlp_flag == UX_TRUE)
129     {
130 
131         /* Reset ZLP now.  */
132         zlp_flag = UX_FALSE;
133 
134         /* Check if we are exceeding the max payload. */
135         if (transfer_request_payload_length > UX_OHCI_MAX_PAYLOAD)
136 
137             bulk_packet_payload_length =  UX_OHCI_MAX_PAYLOAD;
138         else
139 
140             bulk_packet_payload_length =  transfer_request_payload_length;
141 
142         /* IN transfer ? */
143         if ((transfer_request -> ux_transfer_request_type&UX_REQUEST_DIRECTION) == UX_REQUEST_IN)
144 
145             data_td -> ux_ohci_td_dw0 =  UX_OHCI_TD_IN | UX_OHCI_TD_DEFAULT_DW0;
146         else
147 
148             data_td -> ux_ohci_td_dw0 =  UX_OHCI_TD_OUT | UX_OHCI_TD_DEFAULT_DW0;
149 
150         /* Store the beginning of the buffer address in the TD.  */
151         data_td -> ux_ohci_td_cbp =  _ux_utility_physical_address(data_pointer);
152 
153         /* Store the end buffer address in the TD.  */
154         data_td -> ux_ohci_td_be =  data_td -> ux_ohci_td_cbp + bulk_packet_payload_length - 1;
155 
156         /* Update the length of the transfer for this TD.  */
157         data_td -> ux_ohci_td_length =  bulk_packet_payload_length;
158 
159         /* Attach the endpoint and transfer request to the TD.  */
160         data_td -> ux_ohci_td_transfer_request =  transfer_request;
161         data_td -> ux_ohci_td_ed =  ed;
162 
163         /* Adjust the data payload length and the data payload pointer.  */
164         transfer_request_payload_length -=  bulk_packet_payload_length;
165         data_pointer +=  bulk_packet_payload_length;
166 
167         /* Check if there will be another transaction.  */
168         if (transfer_request_payload_length != 0)
169         {
170 
171             /* Get a new TD to hook this payload.  */
172             data_td =  _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
173 
174             if (data_td == UX_NULL)
175             {
176 
177                 /* If there was already a TD chain in progress, free it.  */
178                 if (start_data_td != UX_NULL)
179                 {
180 
181                     data_td =  start_data_td;
182                     while(data_td)
183                     {
184 
185                         next_data_td =  _ux_utility_virtual_address(data_td -> ux_ohci_td_next_td);
186                         data_td -> ux_ohci_td_status =  UX_UNUSED;
187                         data_td =  next_data_td;
188                     }
189                 }
190 
191                 return(UX_NO_TD_AVAILABLE);
192             }
193 
194             /* the first obtained TD in the chain has to be remembered.  */
195             if (start_data_td == UX_NULL)
196                 start_data_td =  data_td;
197 
198             /* Attach this new TD to the previous one.  */
199             previous_td -> ux_ohci_td_next_td =  _ux_utility_physical_address(data_td);
200             previous_td -> ux_ohci_td_next_td_transfer_request =  data_td;
201             previous_td =  data_td;
202         }
203     }
204 
205     /* At this stage, the Head and Tail in the ED are still the same and the OHCI controller
206        will skip this ED until we have hooked the new tail TD.  */
207     tail_td =  _ux_hcd_ohci_regular_td_obtain(hcd_ohci);
208     if (tail_td == UX_NULL)
209     {
210 
211         /* If there was already a TD chain in progress, free it.  */
212         if (start_data_td != UX_NULL)
213         {
214 
215             data_td =  start_data_td;
216             while(data_td)
217             {
218 
219                 next_data_td =  _ux_utility_virtual_address(data_td -> ux_ohci_td_next_td);
220                 data_td -> ux_ohci_td_status =  UX_UNUSED;
221                 data_td =  next_data_td;
222             }
223         }
224 
225         return(UX_NO_TD_AVAILABLE);
226     }
227 
228     /* Attach the tail TD to the last data TD.  */
229     data_td -> ux_ohci_td_next_td =  _ux_utility_physical_address(tail_td);
230 
231     /* Store the new tail TD.  */
232     ed -> ux_ohci_ed_tail_td =  _ux_utility_physical_address(tail_td);
233 
234     /* Now, we must tell the OHCI controller that there is something in the
235        bulk queue.  */
236     ohci_register =  _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_COMMAND_STATUS);
237     ohci_register |=  OHCI_HC_CS_BLF;
238     _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, ohci_register);
239 
240     /* Return successful completion.  */
241     return(UX_SUCCESS);
242 }
243 
244