Sync with trunk head (part 1 of 2)
[reactos.git] / drivers / usb / nt4compat / usbdriver / etd.c
1 /**
2 * etd.c - USB driver stack project for Windows NT 4.0
3 *
4 * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
5 *
6 * This program/include file is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program/include file is distributed in the hope that it will be
12 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program (in the main directory of the distribution, the file
18 * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include <usbdriver.h>
23 #include "ehci.h"
24
25 #define init_elem( ptr, type, ehci_elem_type ) \
26 {\
27 PEHCI_ELEM_LINKS tmp;\
28 ptr = ( type* )( plist->phys_bufs[ i ] + sizeof( type ) * j );\
29 ptr->hw_next = ehci_elem_type << 1;\
30 if( ehci_elem_type == INIT_LIST_FLAG_QTD )\
31 ptr->hw_next = 0;\
32 tmp = ptr->elem_head_link = &plist->elem_head_buf[ i * elms_per_page + j ];\
33 InitializeListHead( &tmp->elem_link );\
34 InitializeListHead( &tmp->sched_link );\
35 InsertTailList( &plist->free_list, &tmp->elem_link );\
36 tmp->list_link = plist;\
37 tmp->pool_link = pinit_ctx->pool;\
38 tmp->phys_part = ( PVOID )( ( ULONG )ptr | ( ehci_elem_type << 1 ) );\
39 if( ehci_elem_type != INIT_LIST_FLAG_QTD )\
40 ptr->phys_addr = ( plist->phys_addrs[ i ].LowPart + sizeof( type ) * j ) | ( ehci_elem_type << 1 );\
41 else \
42 ptr->phys_addr = plist->phys_addrs[ i ].LowPart + sizeof( type ) * j ;\
43 }
44
45 // get the actual max list count of the pool, two limit, max_elem_pool and max_lists_pool
46 #define get_max_lists_count( pOOL, max_liSTS ) \
47 {\
48 LONG ii1=0;\
49 switch( elem_pool_get_type( pOOL ) )\
50 {\
51 case INIT_LIST_FLAG_QTD:\
52 ii1 = EHCI_MAX_QTDS_LIST;\
53 break;\
54 case INIT_LIST_FLAG_FSTN:\
55 ii1 = EHCI_MAX_QHS_LIST;\
56 break;\
57 case INIT_LIST_FLAG_SITD:\
58 ii1 = EHCI_MAX_ITDS_LIST;\
59 break;\
60 case INIT_LIST_FLAG_QH: \
61 ii1 = EHCI_MAX_SITDS_LIST;\
62 break;\
63 case INIT_LIST_FLAG_ITD: \
64 ii1 = EHCI_MAX_FSTNS_LIST;\
65 break;\
66 }\
67 max_liSTS = ( EHCI_MAX_ELEMS_POOL / ii1 ) > EHCI_MAX_LISTS_POOL ? EHCI_MAX_LISTS_POOL : ( EHCI_MAX_ELEMS_POOL / ii1 );\
68 }
69
70 VOID elem_list_destroy_elem_list(PEHCI_ELEM_LIST plist);
71
72 PLIST_ENTRY elem_list_get_list_head(PEHCI_ELEM_LIST plist);
73
74 LONG elem_list_get_total_count(PEHCI_ELEM_LIST plist);
75
76 LONG elem_list_get_elem_size(PEHCI_ELEM_LIST plist);
77
78 LONG elem_list_get_link_offset(PEHCI_ELEM_LIST plist);
79
80 LONG elem_list_add_ref(PEHCI_ELEM_LIST plist);
81
82 LONG elem_list_release_ref(PEHCI_ELEM_LIST plist);
83
84 LONG elem_list_get_ref(PEHCI_ELEM_LIST plist);
85
86 BOOLEAN
87 elem_pool_lock(PEHCI_ELEM_POOL pool, BOOLEAN at_dpc)
88 {
89 UNREFERENCED_PARAMETER(pool);
90 UNREFERENCED_PARAMETER(at_dpc);
91
92 return TRUE;
93 }
94
95 BOOLEAN
96 elem_pool_unlock(PEHCI_ELEM_POOL pool, BOOLEAN at_dpc)
97 {
98 UNREFERENCED_PARAMETER(pool);
99 UNREFERENCED_PARAMETER(at_dpc);
100
101 return TRUE;
102 }
103
104 LONG
105 get_elem_phys_part_size(ULONG type)
106 {
107 // type is INIT_LIST_FLAG_XXX
108 LONG size;
109
110 size = 0;
111 switch (type)
112 {
113 case INIT_LIST_FLAG_ITD:
114 size = 64;
115 break;
116 case INIT_LIST_FLAG_SITD:
117 size = 28;
118 break;
119 case INIT_LIST_FLAG_QTD:
120 size = 32;
121 break;
122 case INIT_LIST_FLAG_QH:
123 size = 48;
124 break;
125 case INIT_LIST_FLAG_FSTN:
126 size = 8;
127 break;
128 }
129 return size;
130 }
131
132 BOOLEAN
133 elem_list_init_elem_list(PEHCI_ELEM_LIST plist, LONG init_flags, PVOID context, LONG count)
134 {
135 LONG pages, i, j, elms_per_page;
136 PEHCI_QH pqh;
137 PEHCI_ITD pitd;
138 PEHCI_SITD psitd;
139 PEHCI_QTD pqtd;
140 PEHCI_FSTN pfstn;
141 PINIT_ELEM_LIST_CONTEXT pinit_ctx;
142
143 UNREFERENCED_PARAMETER(count);
144
145 if (plist == NULL || context == NULL)
146 return FALSE;
147
148 RtlZeroMemory(plist, sizeof(EHCI_ELEM_LIST));
149
150 pinit_ctx = context;
151
152 plist->destroy_list = elem_list_destroy_elem_list;
153 plist->get_list_head = elem_list_get_list_head;
154 plist->get_total_count = elem_list_get_total_count;
155 plist->get_elem_size = elem_list_get_elem_size;
156 plist->get_link_offset = elem_list_get_link_offset;
157 plist->add_ref = elem_list_add_ref;
158 plist->release_ref = elem_list_release_ref;
159 plist->get_ref = elem_list_get_ref;
160
161 InitializeListHead(&plist->free_list);
162
163 switch (init_flags & 0x0f)
164 {
165 case INIT_LIST_FLAG_ITD:
166 plist->total_count = EHCI_MAX_ITDS_LIST;
167 plist->elem_size = sizeof(EHCI_ITD);
168 break;
169 case INIT_LIST_FLAG_QH:
170 plist->total_count = EHCI_MAX_QHS_LIST;
171 plist->elem_size = sizeof(EHCI_QH);
172 break;
173 case INIT_LIST_FLAG_SITD:
174 plist->total_count = EHCI_MAX_SITDS_LIST;
175 plist->elem_size = sizeof(EHCI_SITD);
176 break;
177 case INIT_LIST_FLAG_FSTN:
178 plist->total_count = EHCI_MAX_FSTNS_LIST;
179 plist->elem_size = sizeof(EHCI_FSTN);
180 break;
181 case INIT_LIST_FLAG_QTD:
182 plist->total_count = EHCI_MAX_QTDS_LIST;
183 plist->elem_size = sizeof(EHCI_QTD);
184 break;
185 default:
186 goto ERROR_OUT;
187 }
188 if (plist->elem_size & 0x1f)
189 {
190 plist->total_count = 0;
191 goto ERROR_OUT;
192 }
193
194 plist->flags = init_flags;
195 plist->parent_pool = pinit_ctx->pool;
196 plist->padapter = pinit_ctx->padapter;
197 pages = ((plist->elem_size * plist->total_count) + (PAGE_SIZE - 1)) / PAGE_SIZE;
198 elms_per_page = PAGE_SIZE / plist->elem_size;
199
200 plist->phys_addrs = usb_alloc_mem(NonPagedPool,
201 (sizeof(PHYSICAL_ADDRESS) + sizeof(PBYTE)) * pages +
202 sizeof(EHCI_ELEM_LINKS) * plist->total_count);
203
204 if (plist->phys_addrs == NULL)
205 {
206 plist->total_count = 0;
207 goto ERROR_OUT;
208 }
209
210 plist->phys_bufs = (PBYTE *) & plist->phys_addrs[pages];
211 plist->elem_head_buf = (PEHCI_ELEM_LINKS) & plist->phys_bufs[pages];
212 RtlZeroMemory(plist->phys_addrs,
213 (sizeof(PHYSICAL_ADDRESS) + sizeof(PBYTE)) * pages +
214 sizeof(EHCI_ELEM_LINKS) * plist->total_count);
215
216 for(i = 0; i < pages; i++)
217 {
218 plist->phys_bufs[i] = HalAllocateCommonBuffer(plist->padapter,
219 PAGE_SIZE, &plist->phys_addrs[i], FALSE);
220
221 if (plist->phys_bufs[i] == NULL)
222 {
223 // failed, roll back
224 for(j = i - 1; j >= 0; j--)
225 HalFreeCommonBuffer(plist->padapter,
226 PAGE_SIZE, plist->phys_addrs[j], plist->phys_bufs[j], FALSE);
227 goto ERROR_OUT;
228 }
229 RtlZeroMemory(plist->phys_bufs[i], PAGE_SIZE);
230 for(j = 0; j < elms_per_page; j++)
231 {
232 switch (init_flags & 0xf)
233 {
234 case INIT_LIST_FLAG_QH:
235 {
236 init_elem(pqh, EHCI_QH, INIT_LIST_FLAG_QH);
237 break;
238 }
239 case INIT_LIST_FLAG_ITD:
240 {
241 init_elem(pitd, EHCI_ITD, INIT_LIST_FLAG_ITD);
242 break;
243 }
244 case INIT_LIST_FLAG_QTD:
245 {
246 init_elem(pqtd, EHCI_QTD, INIT_LIST_FLAG_QTD);
247 break;
248 }
249 case INIT_LIST_FLAG_SITD:
250 {
251 init_elem(psitd, EHCI_SITD, INIT_LIST_FLAG_SITD);
252 break;
253 }
254 case INIT_LIST_FLAG_FSTN:
255 {
256 init_elem(pfstn, EHCI_FSTN, INIT_LIST_FLAG_FSTN);
257 break;
258 }
259 default:
260 TRAP();
261 }
262 }
263 }
264 return TRUE;
265
266 ERROR_OUT:
267 if (plist->phys_addrs != NULL)
268 usb_free_mem(plist->phys_addrs);
269
270 RtlZeroMemory(plist, sizeof(EHCI_ELEM_LIST));
271 return FALSE;
272 }
273
274 VOID
275 elem_list_destroy_elem_list(PEHCI_ELEM_LIST plist)
276 {
277 LONG i, pages;
278
279 if (plist == NULL)
280 return;
281
282 pages = (plist->total_count * plist->elem_size + PAGE_SIZE - 1) / PAGE_SIZE;
283 for(i = 0; i < pages; i++)
284 HalFreeCommonBuffer(plist->padapter, PAGE_SIZE, plist->phys_addrs[i], plist->phys_bufs[i], FALSE);
285
286 usb_free_mem(plist->phys_addrs);
287 RtlZeroMemory(plist, sizeof(EHCI_ELEM_LIST));
288 }
289
290 PLIST_ENTRY
291 elem_list_get_list_head(PEHCI_ELEM_LIST plist)
292 {
293 if (plist == NULL)
294 return NULL;
295 return &plist->free_list;
296 }
297
298 LONG
299 elem_list_get_total_count(PEHCI_ELEM_LIST plist)
300 {
301 if (plist == NULL)
302 return 0;
303 return plist->total_count;
304 }
305
306 LONG
307 elem_list_get_elem_size(PEHCI_ELEM_LIST plist)
308 {
309 if (plist == NULL)
310 return 0;
311 return plist->elem_size;
312 }
313
314 LONG
315 elem_list_get_link_offset(PEHCI_ELEM_LIST plist)
316 {
317 if (plist == NULL)
318 return 0;
319
320 return get_elem_phys_part_size(plist->flags & 0xf);
321 }
322
323 LONG
324 elem_list_add_ref(PEHCI_ELEM_LIST plist)
325 {
326 plist->reference++;
327 return plist->reference;
328 }
329
330 LONG
331 elem_list_release_ref(PEHCI_ELEM_LIST plist)
332 {
333 plist->reference--;
334 return plist->reference;
335 }
336
337 LONG
338 elem_list_get_ref(PEHCI_ELEM_LIST plist)
339 {
340 return plist->reference;
341 }
342
343 //
344 // pool methods
345 //
346
347 BOOLEAN
348 elem_pool_init_pool(PEHCI_ELEM_POOL pool, LONG flags, PVOID context)
349 {
350 INIT_ELEM_LIST_CONTEXT init_ctx;
351
352 if (pool == NULL || context == NULL)
353 return FALSE;
354
355 RtlZeroMemory(pool, sizeof(EHCI_ELEM_POOL));
356
357 init_ctx.pool = pool;
358 init_ctx.padapter = context;
359
360 pool->elem_lists[0] = usb_alloc_mem(NonPagedPool, sizeof(EHCI_ELEM_LIST));
361
362 if (pool->elem_lists[0] == NULL)
363 return FALSE;
364
365 if (elem_list_init_elem_list(pool->elem_lists[0], flags, &init_ctx, 0) == FALSE)
366 {
367 usb_free_mem(pool->elem_lists[0]);
368 return FALSE;
369 }
370 pool->link_offset = pool->elem_lists[0]->get_link_offset(pool->elem_lists[0]);
371 pool->free_count = pool->elem_lists[0]->get_total_count(pool->elem_lists[0]);
372 pool->list_count = 1;
373 pool->flags = flags;
374
375 return TRUE;
376 }
377
378 LONG
379 elem_pool_get_link_offset(PEHCI_ELEM_POOL elem_pool)
380 {
381 return elem_pool->link_offset;
382 }
383
384 LONG
385 elem_pool_get_total_count(PEHCI_ELEM_POOL elem_pool)
386 {
387 return elem_pool->elem_lists[0]->get_total_count(elem_pool->elem_lists[0]) * elem_pool->list_count;
388 }
389
390 VOID
391 elem_pool_destroy_pool(PEHCI_ELEM_POOL pool)
392 {
393 LONG i;
394 if (pool == NULL)
395 return;
396 for(i = pool->list_count - 1; i >= 0; i--)
397 {
398 pool->elem_lists[i]->destroy_list(pool->elem_lists[i]);
399 usb_free_mem(pool->elem_lists[i]);
400 pool->elem_lists[i] = NULL;
401 }
402 RtlZeroMemory(pool, sizeof(EHCI_ELEM_POOL));
403 return;
404 }
405
406 PEHCI_ELEM_LINKS
407 elem_pool_alloc_elem(PEHCI_ELEM_POOL pool)
408 {
409 LONG i;
410 PEHCI_ELEM_LIST pel = NULL;
411 PLIST_HEAD lh;
412 PEHCI_ELEM_LINKS elnk;
413
414 if (pool == NULL)
415 return NULL;
416
417 for(i = 0; i < pool->list_count; i++)
418 {
419 pel = pool->elem_lists[i];
420 if (pel->get_ref(pel) == pel->get_total_count(pel))
421 continue;
422 break;
423 }
424 if (i == pool->list_count)
425 {
426 if (elem_pool_expand_pool(pool, pel->get_total_count(pel)) == FALSE)
427 return NULL;
428 pel = pool->elem_lists[i];
429 }
430
431 lh = pel->get_list_head(pel);
432 elnk = (PEHCI_ELEM_LINKS) RemoveHeadList(lh);
433 InitializeListHead(&elnk->elem_link);
434 InitializeListHead(&elnk->sched_link);
435
436 pel->add_ref(pel);
437 pool->free_count--;
438
439 return elnk;
440 }
441
442 VOID
443 elem_pool_free_elem(PEHCI_ELEM_LINKS elem_link)
444 {
445 PLIST_HEAD lh;
446 LONG ref;
447 PEHCI_ELEM_POOL pool;
448 if (elem_link == NULL)
449 return;
450 pool = elem_link->pool_link;
451 lh = elem_link->list_link->get_list_head(elem_link->list_link);
452 if (lh == NULL)
453 return;
454 InsertHeadList(lh, &elem_link->elem_link);
455 ref = elem_link->list_link->release_ref(elem_link->list_link);
456 pool->free_count++;
457 if (ref == 0)
458 elem_pool_collect_garbage(pool);
459 return;
460 }
461
462 BOOLEAN
463 elem_pool_is_empty(PEHCI_ELEM_POOL pool)
464 {
465 PEHCI_ELEM_LIST pel;
466
467 if (pool == NULL)
468 return TRUE;
469 pel = pool->elem_lists[0];
470 return (BOOLEAN) (pool->list_count == 1 && pool->free_count == pel->get_total_count(pel));
471 }
472
473 LONG
474 elem_pool_get_free_count(PEHCI_ELEM_POOL pool)
475 {
476 if (pool == NULL)
477 return 0;
478 return pool->free_count;
479 }
480
481 PEHCI_ELEM_LINKS
482 elem_pool_alloc_elems(PEHCI_ELEM_POOL pool, LONG count)
483 {
484 LIST_HEAD lh;
485 PLIST_ENTRY pthis;
486 LONG i, alloc_count, max_pool_lists;
487 PEHCI_ELEM_LIST pel;
488 PEHCI_ELEM_LINKS elnk;
489 // calculate to see if the count is affordable
490
491 if (pool == NULL || count <= 0)
492 return NULL;
493
494 get_max_lists_count(pool, max_pool_lists);
495 InitializeListHead(&lh);
496 pel = pool->elem_lists[0];
497 if (count <= pool->free_count)
498 alloc_count = 0;
499 else
500 alloc_count = count - pool->free_count;
501
502 if (alloc_count > pel->get_total_count(pel) * (max_pool_lists - pool->list_count))
503 return NULL;
504
505 for(i = 0; i < count; i++)
506 {
507 if ((elnk = elem_pool_alloc_elem(pool)) == NULL)
508 {
509 // undo what we have done
510 while (IsListEmpty(&lh) == FALSE)
511 {
512 pthis = RemoveHeadList(&lh);
513 elnk = struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link);
514 elem_pool_free_elem(elnk);
515 }
516 return NULL;
517 }
518 InsertTailList(&lh, &elnk->elem_link);
519 }
520 ListFirst(&lh, pthis);
521 elnk = struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link);
522 RemoveEntryList(&lh);
523 return elnk;
524 }
525
526 BOOLEAN
527 elem_pool_free_elems(PEHCI_ELEM_LINKS elem_chains)
528 {
529 // note: no list head exists.
530 LIST_HEAD lh;
531 PEHCI_ELEM_LINKS elnk;
532
533 InsertTailList(&elem_chains->elem_link, &lh);
534 while (IsListEmpty(&lh) == FALSE)
535 {
536 elnk = (PEHCI_ELEM_LINKS) RemoveHeadList(&lh);
537 elem_pool_free_elem(elnk);
538 }
539 return TRUE;
540 }
541
542 LONG
543 elem_pool_get_type(PEHCI_ELEM_POOL pool)
544 {
545 if (pool == NULL)
546 return -1;
547 return (pool->flags & 0xf);
548 }
549
550 BOOLEAN
551 elem_pool_expand_pool(PEHCI_ELEM_POOL pool, LONG elem_count)
552 {
553 LONG elem_cnt_list, list_count, i, j;
554 INIT_ELEM_LIST_CONTEXT init_ctx;
555
556 if (pool == NULL || elem_count <= 0 || elem_count > EHCI_MAX_ELEMS_POOL)
557 return FALSE;
558
559 init_ctx.pool = pool;
560 init_ctx.padapter = pool->elem_lists[0]->padapter;
561
562 elem_cnt_list = pool->elem_lists[0]->get_total_count(pool->elem_lists[0]);
563 list_count = (elem_count + elem_cnt_list - 1) / elem_cnt_list;
564 get_max_lists_count(pool, i);
565
566 if (list_count + pool->list_count > i)
567 return FALSE;
568
569 for(i = pool->list_count; i < list_count + pool->list_count; i++)
570 {
571 pool->elem_lists[i] = usb_alloc_mem(NonPagedPool, sizeof(EHCI_ELEM_LIST));
572 if (elem_list_init_elem_list(pool->elem_lists[i], pool->flags, &init_ctx, 0) == FALSE)
573 break;
574 }
575
576 if (i < list_count + pool->list_count)
577 {
578 // undo all we have done
579 for(j = pool->list_count; j < pool->list_count + i; j++)
580 {
581 pool->elem_lists[j]->destroy_list(pool->elem_lists[j]);
582 usb_free_mem(pool->elem_lists[j]);
583 pool->elem_lists[j] = NULL;
584 }
585 return FALSE;
586 }
587
588 // update pool
589 pool->free_count += elem_cnt_list * list_count;
590 pool->list_count += list_count;
591 return TRUE;
592 }
593
594 BOOLEAN
595 elem_pool_collect_garbage(PEHCI_ELEM_POOL pool)
596 {
597 LONG i, j, k, fl;
598 LONG free_elem_lists[EHCI_MAX_LISTS_POOL - 1];
599 PEHCI_ELEM_LIST pel;
600
601 if (pool == NULL)
602 return FALSE;
603
604 for(i = 1, fl = 0; i < pool->list_count; i++)
605 {
606 if (pool->elem_lists[i]->get_ref(pool->elem_lists[i]) == 0)
607 {
608 free_elem_lists[fl++] = i;
609 }
610 }
611 for(j = fl - 1; j >= 0; j--)
612 {
613 pel = pool->elem_lists[free_elem_lists[j]];
614 pel->destroy_list(pel);
615 usb_free_mem(pel);
616
617 for(k = free_elem_lists[j] + 1; k < pool->list_count; k++)
618 {
619 // shrink the elem_lists
620 pool->elem_lists[k - 1] = pool->elem_lists[k];
621 }
622 pool->elem_lists[k] = NULL;
623 pel = pool->elem_lists[0];
624 pool->free_count -= pel->get_total_count(pel);
625 pool->list_count--;
626 }
627 return TRUE;
628 }
629
630 BOOLEAN
631 elem_pool_can_transfer(PEHCI_ELEM_POOL pool, LONG td_count)
632 {
633 LONG i;
634 if (pool == NULL || td_count <= 0)
635 return FALSE;
636 get_max_lists_count(pool, i);
637 if ((i - pool->list_count)
638 * pool->elem_lists[0]->get_total_count(pool->elem_lists[0]) + pool->free_count < td_count)
639 return FALSE;
640 return TRUE;
641 }
642
643 //----------------------------------------------------------