1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12
13 /**************************************************************************/
14 /**************************************************************************/
15 /** */
16 /** USBX Component */
17 /** */
18 /** EHCI Controller Driver */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23
24 /* Include necessary system files. */
25
26 #define UX_SOURCE_CODE
27
28 #include "ux_api.h"
29 #include "ux_hcd_ehci.h"
30 #include "ux_host_stack.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _ux_hcd_ehci_hsisochronous_tds_process PORTABLE C */
38 /* 6.1.12 */
39 /* AUTHOR */
40 /* */
41 /* Chaoqiong Xiao, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function processes the iTDs of an isochronous endpoint. */
46 /* */
47 /* INPUT */
48 /* */
49 /* hcd_ehci Pointer to HCD EHCI */
50 /* itd Pointer to HSISO TD */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* UX_EHCI_HSISO_TD Pointer to HSISO TD */
55 /* */
56 /* CALLS */
57 /* */
58 /* (ux_transfer_request_completion_function) */
59 /* Transfer Completion function */
60 /* _ux_hcd_ehci_register_read Read EHCI register */
61 /* _ux_host_semaphore_put Put semaphore */
62 /* _ux_utility_physical_address Get physical address */
63 /* */
64 /* CALLED BY */
65 /* */
66 /* EHCI Controller Driver */
67 /* */
68 /* RELEASE HISTORY */
69 /* */
70 /* DATE NAME DESCRIPTION */
71 /* */
72 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
73 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
74 /* resulting in version 6.1 */
75 /* 11-09-2020 Chaoqiong Xiao Modified comment(s), */
76 /* fixed compile warnings, */
77 /* resulting in version 6.1.2 */
78 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
79 /* refined macros names, */
80 /* resulting in version 6.1.10 */
81 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
82 /* improved uframe handling, */
83 /* resulting in version 6.1.12 */
84 /* */
85 /**************************************************************************/
_ux_hcd_ehci_hsisochronous_tds_process(UX_HCD_EHCI * hcd_ehci,UX_EHCI_HSISO_TD * itd)86 UX_EHCI_HSISO_TD* _ux_hcd_ehci_hsisochronous_tds_process(
87 UX_HCD_EHCI *hcd_ehci,
88 UX_EHCI_HSISO_TD* itd)
89 {
90
91 UX_EHCI_HSISO_ED *ed;
92 UX_EHCI_HSISO_TD *next_scan_td;
93 ULONG n_fr;
94 ULONG frindex;
95 ULONG frindex1;
96 UX_EHCI_HSISO_TD *fr_td;
97 UX_EHCI_HSISO_TD *fr_td1;
98 ULONG control;
99 ULONG control1;
100 ULONG trans_bytes;
101 UX_TRANSFER *transfer;
102 UX_EHCI_POINTER bp;
103 ULONG pg;
104 ULONG pg_addr;
105 ULONG pg_offset;
106 ULONG frindex_now;
107 ULONG frindex_start;
108 USHORT fr_hc;
109 USHORT fr_sw;
110 UINT i;
111
112
113 /* Get ED. */
114 ed = itd -> ux_ehci_hsiso_td_ed;
115
116 /* Get next scan TD. */
117 next_scan_td = itd -> ux_ehci_hsiso_td_next_scan_td;
118
119 /* Check if iTD is skipped. */
120 if (ed -> ux_ehci_hsiso_ed_frstart == 0xFF)
121 return(next_scan_td);
122
123 /*
124 ** 1. There is requests loaded
125 ** (0) Micro-frame is active (request loaded)
126 ** (1) Active is cleared (HC processed)
127 ** (2) Handle it
128 ** 2. There is micro-frame free
129 ** (0) Micro-frame is free
130 ** (1) Micro-frame not overlap current FRINDEX
131 ** (2) Link request
132 ** (3) Build controls to active micro-frame
133 */
134
135 /* Get number of frames (8,4,2 or 1). */
136 n_fr = 8u >> ed -> ux_ehci_hsiso_ed_frinterval_shift;
137
138 /* Process if there is requests loaded. */
139 if (ed -> ux_ehci_hsiso_ed_frload > 0)
140 {
141
142 /* Process count to target micro frames. */
143 fr_hc = (USHORT)(ed -> ux_ehci_hsiso_ed_fr_hc << ed -> ux_ehci_hsiso_ed_frinterval_shift);
144 fr_hc = (USHORT)(fr_hc + ed -> ux_ehci_hsiso_ed_frstart);
145 fr_hc &= 0x7u;
146
147 /* Process done iTDs. */
148 for (i = 0;
149 i < n_fr; /* 8, 4, 2 or 1. */
150 i += ed -> ux_ehci_hsiso_ed_frinterval)
151 {
152
153 /* Get frindex. */
154 frindex = fr_hc + i;
155 frindex &= 7u;
156
157 if ((ed -> ux_ehci_hsiso_ed_frload & (1u << frindex)) == 0)
158 {
159 break;
160 }
161
162 /* Get iTD for the micro-frame. */
163 fr_td = ed -> ux_ehci_hsiso_ed_fr_td[frindex >> 1];
164
165 /* Get control. */
166 control = fr_td -> ux_ehci_hsiso_td_control[frindex];
167
168 /* Check next frame state, if multiple micro-frames loaded. */
169 if (n_fr > 1)
170 {
171
172 /* Micro-frame is active, check next. */
173 if (control & UX_EHCI_HSISO_STATUS_ACTIVE)
174 {
175
176 /* Get next frindex. */
177 frindex1 = frindex + ed -> ux_ehci_hsiso_ed_frinterval;
178 frindex1 &= 7u;
179
180 /* Get next iTD for the micro-frame. */
181 fr_td1 = ed -> ux_ehci_hsiso_ed_fr_td[frindex1 >> 1];
182
183 /* Get control. */
184 control1 = fr_td1 -> ux_ehci_hsiso_td_control[frindex1];
185
186 /* Next micro-frame is also active, break. */
187 if (control1 & UX_EHCI_HSISO_STATUS_ACTIVE)
188 {
189 break;
190 }
191 else
192 {
193
194 /* Next micro-frame is not loaded, break. */
195 if ((ed -> ux_ehci_hsiso_ed_frload & (1u << frindex1)) == 0)
196 {
197 break;
198 }
199
200 /* We are here when micro-frame 1 handled before micro-frame 0,
201 ** which means we missed one request.
202 */
203
204 /* Disable control, discard buffer and handle request. */
205 control &= ~(UX_EHCI_HSISO_STATUS_ACTIVE |
206 UX_EHCI_HSISO_XACT_LENGTH_MASK);
207 fr_td -> ux_ehci_hsiso_td_control[frindex] = control;
208 }
209
210 } /* if (control & UX_EHCI_HSISO_STATUS_ACTIVE) */
211 }
212 else
213 {
214
215 /* If iTD is active, break. */
216 if (control & UX_EHCI_HSISO_STATUS_ACTIVE)
217 {
218 break;
219 }
220 }
221
222 /* Clear load map anyway. */
223 fr_td -> ux_ehci_hsiso_td_frload = (UCHAR)(fr_td -> ux_ehci_hsiso_td_frload & ~(1u << frindex));
224 ed -> ux_ehci_hsiso_ed_frload = (USHORT)(ed -> ux_ehci_hsiso_ed_frload & ~(1u << frindex));
225
226 /* HC processed. */
227 ed -> ux_ehci_hsiso_ed_fr_hc ++;
228
229 /* Unlink it from iTD. */
230 fr_td -> ux_ehci_hsiso_td_fr_transfer[frindex & 1u] = UX_NULL;
231
232 /* Handle the request. */
233 transfer = ed -> ux_ehci_hsiso_ed_transfer_head;
234
235 /* If there is no transfer linked to, just ignore it. */
236 if (transfer == UX_NULL)
237 break;
238
239 /* Convert error code to completion code. */
240 if (control & UX_EHCI_HSISO_STATUS_DATA_BUFFER_ERR)
241 transfer -> ux_transfer_request_completion_code = UX_TRANSFER_BUFFER_OVERFLOW;
242 else
243 {
244 if (control & UX_EHCI_HSISO_STATUS_MASK)
245 transfer -> ux_transfer_request_completion_code = UX_TRANSFER_ERROR;
246 else
247 transfer -> ux_transfer_request_completion_code = UX_SUCCESS;
248 }
249
250 /* Get transfer bytes. */
251 trans_bytes = control & UX_EHCI_HSISO_XACT_LENGTH_MASK;
252 trans_bytes >>= UX_EHCI_HSISO_XACT_LENGTH_SHIFT;
253
254 /* Save to actual length. */
255 transfer -> ux_transfer_request_actual_length = trans_bytes;
256
257 /* Unlink it from request list head. */
258 ed -> ux_ehci_hsiso_ed_transfer_head =
259 transfer -> ux_transfer_request_next_transfer_request;
260
261 /* If no more requests, also set tail to NULL. */
262 if (ed -> ux_ehci_hsiso_ed_transfer_head == UX_NULL)
263 ed -> ux_ehci_hsiso_ed_transfer_tail = UX_NULL;
264
265 /* Invoke callback. */
266 if (transfer -> ux_transfer_request_completion_function)
267 transfer -> ux_transfer_request_completion_function(transfer);
268
269 /* Put semaphore. */
270 _ux_host_semaphore_put(&transfer -> ux_transfer_request_semaphore);
271
272 } /* for (;i < n_fr;) */
273 }
274
275 /* Build request when there is new unloaded requests. */
276 if (ed -> ux_ehci_hsiso_ed_transfer_first_new)
277 {
278
279 /* Get current FRINDEX for SW process. */
280 frindex_now = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_FRAME_INDEX);
281 frindex_now &= 0x7u;
282
283 /* If transfer starts. */
284 if (ed -> ux_ehci_hsiso_ed_frstart == 0xFE)
285 {
286
287 /* 1 micro-frame in iTD. */
288 if (ed -> ux_ehci_hsiso_ed_frinterval_shift >= 3)
289 {
290
291 /* Uses this only micro-frame index. */
292 ed -> ux_ehci_hsiso_ed_frstart = ed -> ux_ehci_hsiso_ed_frindex;
293 }
294
295 /* 8,4,2 micro-frames in each iTD. */
296 else
297 {
298
299 /* Get start index for sw to link requests. */
300 if (ed -> ux_ehci_hsiso_ed_frinterval_shift == 0)
301
302 /* Interval 1, just add two. */
303 frindex_start = frindex_now + 2;
304 else
305 {
306
307 /* Interval 2, 4. */
308 /* Scan indexes, to find an index that has 2 in front or more. */
309 for (frindex_start = ed -> ux_ehci_hsiso_ed_frindex;
310 frindex_start < 9;
311 frindex_start += ed -> ux_ehci_hsiso_ed_frinterval)
312 {
313 if (frindex_start - frindex_now >= 2)
314 break;
315 }
316 }
317
318 /* Target to start index calculated and wrap around in one frame. */
319 ed -> ux_ehci_hsiso_ed_frstart = frindex_start & 7u;
320 }
321 } /* if (ed -> ux_ehci_hsiso_ed_fr_sw == 0xFE) */
322
323 /* Process count to target micro frames. */
324 fr_sw = (USHORT)(ed -> ux_ehci_hsiso_ed_fr_sw << ed -> ux_ehci_hsiso_ed_frinterval_shift);
325 fr_sw = (USHORT)(fr_sw + ed -> ux_ehci_hsiso_ed_frstart);
326 fr_sw &= 0x7u;
327
328 /* Build requests. */
329 for (i = 0;
330 i < n_fr;
331 i += ed -> ux_ehci_hsiso_ed_frinterval)
332 {
333
334 /* Get micro-frame index. */
335 frindex = i + fr_sw;
336 frindex &= 7u;
337
338 /* Get iTD. */
339 fr_td = ed -> ux_ehci_hsiso_ed_fr_td[frindex >> 1];
340
341 /* Check load status. */
342 if (fr_td -> ux_ehci_hsiso_td_frload & (1u << frindex))
343 {
344 break;
345 }
346
347 /* Get a transfer request. */
348 transfer = ed -> ux_ehci_hsiso_ed_transfer_first_new;
349 if (transfer == UX_NULL)
350 {
351 break;
352 }
353
354 /* Get control status. */
355 control = fr_td -> ux_ehci_hsiso_td_control[frindex];
356
357 /* If there are multiple micro-frames, check
358 ** (1) If target micro-frame is already loaded
359 ** (2) If the micro-frame overlapped current one
360 */
361 if (n_fr)
362 {
363
364 /* If already loaded(active), break. */
365 if (control & UX_EHCI_HSISO_STATUS_ACTIVE)
366 {
367 break;
368 }
369
370 /* If index overlap, break. */
371
372 /* Check if frindex exceeds same FRINDEX in next frame. */
373 if ((frindex == frindex_now) || (i + fr_sw >= frindex_now + 8))
374 {
375 break;
376 }
377 }
378
379 /* Now new request is linking. */
380
381 /* Process count inc. */
382 ed -> ux_ehci_hsiso_ed_fr_sw ++;
383
384 /* Sanity check, iTD is not linked. */
385 while(fr_td -> ux_ehci_hsiso_td_fr_transfer[frindex & 1u] != UX_NULL);
386
387 /* Link it to iTD. */
388 fr_td -> ux_ehci_hsiso_td_fr_transfer[frindex & 1u] = transfer;
389
390 /* Remove it from new free list. */
391 ed -> ux_ehci_hsiso_ed_transfer_first_new =
392 transfer -> ux_transfer_request_next_transfer_request;
393
394 /* Update load map. */
395 fr_td -> ux_ehci_hsiso_td_frload = (UCHAR)(fr_td -> ux_ehci_hsiso_td_frload | (1u << frindex));
396 ed -> ux_ehci_hsiso_ed_frload = (UCHAR)(ed -> ux_ehci_hsiso_ed_frload | (1u << frindex));
397
398 /* Get transfer size. */
399 trans_bytes = transfer -> ux_transfer_request_requested_length;
400
401 /* Limit max transfer size. */
402 if (trans_bytes > fr_td -> ux_ehci_hsiso_td_max_trans_size)
403 trans_bytes = fr_td -> ux_ehci_hsiso_td_max_trans_size;
404
405 /* Build the control. */
406
407 /* Keep IOC and PG. */
408 control &= UX_EHCI_HSISO_IOC | UX_EHCI_HSISO_PG_MASK;
409
410 /* New transfer size. */
411 control |= (trans_bytes << UX_EHCI_HSISO_XACT_LENGTH_SHIFT);
412
413 /* Active. */
414 control |= (UX_EHCI_HSISO_STATUS_ACTIVE);
415
416 /* Build offset & BPs (5,6/3,4). */
417
418 /* Get physical buffer address. */
419 bp.void_ptr = _ux_utility_physical_address(transfer -> ux_transfer_request_data_pointer);
420
421 /* Get page offset. */
422 pg_addr = bp.value & UX_EHCI_PAGE_ALIGN;
423 pg_offset = bp.value & UX_EHCI_HSISO_XACT_OFFSET_MASK;
424
425 /* Offset in control. */
426 control |= pg_offset;
427
428 /* Save BPs. */
429 pg = (frindex & 1u) ? 5 : 3;
430
431 /* PG in control. */
432 control |= pg << UX_EHCI_HSISO_PG_SHIFT;
433
434 /* Save BPs. */
435
436 /* First BP. */
437 bp.value = pg_addr;
438 fr_td -> ux_ehci_hsiso_td_bp[pg] = bp.void_ptr;
439
440 /* Next page information. */
441 pg ++;
442 pg_addr += UX_EHCI_PAGE_SIZE;
443
444 /* Next BP. */
445 bp.value = pg_addr;
446 fr_td -> ux_ehci_hsiso_td_bp[pg] = bp.void_ptr;
447
448 /* Save control. */
449 UX_DATA_MEMORY_BARRIER
450 fr_td -> ux_ehci_hsiso_td_control[frindex] = control;
451
452 } /* for(i = 0; i < n_fr; ) */
453
454 } /* if (ed -> ux_ehci_hsiso_ed_transfer_first_new) */
455
456 /* If there is no transfer, need start again any way. */
457 if (ed -> ux_ehci_hsiso_ed_frload == 0)
458 {
459 ed -> ux_ehci_hsiso_ed_frstart = 0xFF;
460 ed -> ux_ehci_hsiso_ed_fr_sw = 0;
461 ed -> ux_ehci_hsiso_ed_fr_hc = 0;
462 }
463
464 /* Return next iTD in scan list. */
465 return(next_scan_td);
466 }
467