[INF]
[reactos.git] / drivers / usb / nt4compat / usbdrv / td.c
1 /**
2 * td.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
24 #define UHCI_MIN_TD_POOLS 4
25
26 BOOLEAN free_td_to_pool(PUHCI_TD_POOL ptd_pool, PUHCI_TD ptd); //add tds till pnext == NULL
27
28
29 PUHCI_QH alloc_qh(PUHCI_QH_POOL pqh_pool); //null if failed
30
31 BOOLEAN
32 init_td_pool(PUHCI_TD_POOL ptd_pool)
33 {
34 int i, pages;
35 PTD_EXTENSION ptde;
36
37 if (ptd_pool == NULL)
38 return FALSE;
39
40 if (ptd_pool->padapter == NULL)
41 return FALSE;
42
43 pages = sizeof(UHCI_TD) * UHCI_MAX_POOL_TDS / PAGE_SIZE;
44 RtlZeroMemory(ptd_pool->td_array, sizeof(ptd_pool->td_array));
45 RtlZeroMemory(ptd_pool->logic_addr, sizeof(ptd_pool->logic_addr));
46
47 for(i = 0; i < pages; i++)
48 {
49 ptd_pool->td_array[i] =
50 HalAllocateCommonBuffer(ptd_pool->padapter, PAGE_SIZE, &ptd_pool->logic_addr[i], FALSE);
51 if (ptd_pool->td_array[i] == NULL)
52 goto failed;
53 }
54
55 ptd_pool->tde_array = (PTD_EXTENSION) usb_alloc_mem(NonPagedPool,
56 sizeof(TD_EXTENSION) * UHCI_MAX_POOL_TDS);
57
58 if (ptd_pool->tde_array == NULL)
59 goto failed;
60
61 for(i = 0; i < pages; i++)
62 {
63 RtlZeroMemory(ptd_pool->td_array[i], PAGE_SIZE);
64 }
65
66 RtlZeroMemory(ptd_pool->tde_array, sizeof(TD_EXTENSION) * UHCI_MAX_POOL_TDS);
67
68 ptde = ptd_pool->tde_array;
69 ptd_pool->free_count = 0;
70 ptd_pool->total_count = UHCI_MAX_POOL_TDS;
71 InitializeListHead(&ptd_pool->free_que);
72
73 for(i = 0; i < UHCI_MAX_POOL_TDS; i++)
74 {
75 //link tde and the td one by one, fixed since this init
76 ptd_pool->td_array[i >> 7][i & 0x7f].ptde = &ptde[i];
77 ptde[i].ptd = &ptd_pool->td_array[i >> 7][i & 0x7f];
78 ptde[i].flags = UHCI_ITEM_FLAG_TD;
79 ptd_pool->td_array[i >> 7][i & 0x7f].phy_addr =
80 ptd_pool->logic_addr[i >> 7].LowPart + (i & 0x7f) * sizeof(UHCI_TD);
81 ptd_pool->td_array[i >> 7][i & 0x7f].pool = ptd_pool;
82 ptd_pool->td_array[i >> 7][i & 0x7f].purb = NULL;
83 free_td_to_pool(ptd_pool, &ptd_pool->td_array[i >> 7][i & 0x7f]);
84
85 }
86 return TRUE;
87
88 failed:
89 for(i = 0; i < pages; i++)
90 {
91 if (ptd_pool->td_array[i])
92 {
93 HalFreeCommonBuffer(ptd_pool->padapter,
94 PAGE_SIZE, ptd_pool->logic_addr[i], ptd_pool->td_array[i], FALSE);
95 ptd_pool->td_array[i] = NULL;
96 ptd_pool->logic_addr[i].QuadPart = 0;
97 }
98 }
99
100 if (ptd_pool->tde_array)
101 usb_free_mem(ptd_pool->tde_array);
102
103 uhci_dbg_print(DBGLVL_MAXIMUM, ("init_td_pool(): failed to init the td pool\n"));
104 TRAP();
105
106 ptd_pool->free_count = ptd_pool->total_count = 0;
107 return FALSE;
108 }
109
110 //add tds till pnext == NULL
111 BOOLEAN
112 free_td_to_pool(PUHCI_TD_POOL ptd_pool, PUHCI_TD ptd)
113 {
114 if (ptd_pool == NULL || ptd == NULL)
115 {
116 return FALSE;
117 }
118
119 ptd->link = ptd->status = ptd->info = ptd->buffer = 0;
120 ptd->purb = NULL;
121 ptd_pool->free_count++;
122
123 InsertTailList(&ptd_pool->free_que, &ptd->ptde->vert_link);
124
125 return TRUE;
126
127 }
128
129 // qh routines
130
131 //null if failed
132 PUHCI_TD
133 alloc_td_from_pool(PUHCI_TD_POOL ptd_pool)
134 {
135 PTD_EXTENSION ptde;
136 PLIST_ENTRY temp;
137
138 if (ptd_pool == NULL)
139 return FALSE;
140
141 if (IsListEmpty(&ptd_pool->free_que))
142 return FALSE;
143
144 temp = RemoveHeadList(&ptd_pool->free_que);
145
146 if (temp == NULL)
147 return FALSE;
148
149 ptde = struct_ptr(temp, TD_EXTENSION, vert_link);
150
151 ptd_pool->free_count--;
152
153 InitializeListHead(&ptde->vert_link);
154 InitializeListHead(&ptde->hori_link);
155
156 return ptde->ptd;
157
158 }
159
160 //test whether the pool is all free
161 BOOLEAN
162 is_pool_free(PUHCI_TD_POOL pool)
163 {
164 if (pool == NULL)
165 return FALSE;
166
167 if (pool->free_count == pool->total_count)
168 return TRUE;
169
170 return FALSE;
171 }
172
173 BOOLEAN
174 is_pool_empty(PUHCI_TD_POOL pool)
175 {
176 if (pool == NULL)
177 return FALSE;
178
179 return (BOOLEAN) (pool->free_count == 0);
180 }
181
182 BOOLEAN
183 destroy_td_pool(PUHCI_TD_POOL ptd_pool)
184 {
185 int i, pages;
186 PADAPTER_OBJECT padapter; //we need this garbage for allocation
187
188 padapter = ptd_pool->padapter;
189
190 pages = sizeof(UHCI_TD) * UHCI_MAX_POOL_TDS / PAGE_SIZE;
191 if (ptd_pool && ptd_pool->padapter)
192 {
193 usb_free_mem(ptd_pool->tde_array);
194 ptd_pool->tde_array = NULL;
195 for(i = 0; i < pages; i++)
196 {
197 if (ptd_pool->td_array[i])
198 {
199 HalFreeCommonBuffer(ptd_pool->padapter,
200 PAGE_SIZE, ptd_pool->logic_addr[i], ptd_pool->td_array[i], FALSE);
201 ptd_pool->td_array[i] = NULL;
202 ptd_pool->logic_addr[i].QuadPart = 0;
203 }
204 }
205 RtlZeroMemory(ptd_pool, sizeof(UHCI_TD_POOL));
206 ptd_pool->padapter = padapter;
207 ptd_pool->free_count = ptd_pool->total_count = 0;
208 }
209 else
210 return FALSE;
211
212 return TRUE;
213 }
214
215 BOOLEAN
216 init_td_pool_list(PUHCI_TD_POOL_LIST pool_list, PADAPTER_OBJECT padapter)
217 {
218 int i;
219 RtlZeroMemory(pool_list, sizeof(UHCI_TD_POOL_LIST));
220 InitializeListHead(&pool_list->busy_pools);
221 InitializeListHead(&pool_list->free_pools);
222
223 pool_list->free_count = UHCI_MAX_TD_POOLS;
224 pool_list->free_tds = 0;
225
226 for(i = 0; i < UHCI_MAX_TD_POOLS; i++)
227 {
228 pool_list->pool_array[i].padapter = padapter;
229 InsertTailList(&pool_list->free_pools, &pool_list->pool_array[i].pool_link);
230 }
231
232 KeInitializeSpinLock(&pool_list->pool_lock);
233 return expand_pool_list(pool_list, UHCI_MIN_TD_POOLS);
234 }
235
236 BOOLEAN
237 destroy_td_pool_list(PUHCI_TD_POOL_LIST pool_list)
238 {
239 PUHCI_TD_POOL pool;
240 while (IsListEmpty(&pool_list->busy_pools) == FALSE)
241 {
242 pool = (PUHCI_TD_POOL) RemoveHeadList(&pool_list->busy_pools);
243 destroy_td_pool(pool);
244 }
245
246 RtlZeroMemory(pool_list, sizeof(UHCI_TD_POOL_LIST));
247 return TRUE;
248 }
249
250 BOOLEAN
251 expand_pool_list(PUHCI_TD_POOL_LIST pool_list, LONG pool_count) //private
252 {
253 PUHCI_TD_POOL pool;
254 int i;
255
256 if (IsListEmpty(&pool_list->free_pools) == TRUE)
257 return FALSE;
258
259 if (pool_list->free_count < pool_count)
260 return FALSE;
261
262 for(i = 0; i < pool_count; i++)
263 {
264 pool = (PUHCI_TD_POOL) RemoveHeadList(&pool_list->free_pools);
265
266 if (init_td_pool(pool) == FALSE)
267 {
268 //reverse the allocation
269 InsertHeadList(&pool_list->free_pools, &pool->pool_link);
270 // collect_garbage( pool_list );
271 return FALSE;
272 }
273
274 InsertTailList(&pool_list->busy_pools, &pool->pool_link);
275 pool_list->free_tds += UHCI_MAX_POOL_TDS;
276 pool_list->free_count--;
277 }
278 return TRUE;
279 }
280
281 BOOLEAN
282 collect_garbage(PUHCI_TD_POOL_LIST pool_list)
283 {
284 PLIST_ENTRY prev, next;
285
286 // no garbage
287 if (pool_list->free_count >= UHCI_MAX_TD_POOLS - UHCI_MIN_TD_POOLS)
288 return TRUE;
289
290 ListFirstPrev(&pool_list->busy_pools, prev);
291 ListNext(&pool_list->busy_pools, prev, next);
292
293 while (next && next != &pool_list->busy_pools)
294 {
295 if (is_pool_free((PUHCI_TD_POOL) next))
296 {
297 RemoveEntryList(next);
298 destroy_td_pool((PUHCI_TD_POOL) next);
299 InsertTailList(&pool_list->free_pools, next);
300 pool_list->free_count++;
301 pool_list->free_tds -= UHCI_MAX_POOL_TDS;
302 ListNext(&pool_list->busy_pools, prev, next);
303 if (pool_list->free_count >= UHCI_MAX_TD_POOLS - UHCI_MIN_TD_POOLS)
304 break;
305 }
306 else
307 {
308 prev = next;
309 ListNext(&pool_list->busy_pools, prev, next);
310 }
311 }
312 return TRUE;
313
314 }
315
316 //private
317 LONG
318 get_num_free_tds(PUHCI_TD_POOL_LIST pool_list)
319 {
320 return pool_list->free_tds;
321 }
322
323 //private
324 LONG
325 get_max_free_tds(PUHCI_TD_POOL_LIST pool_list)
326 {
327 return pool_list->free_tds + pool_list->free_count * UHCI_MAX_POOL_TDS;
328 }
329
330 //add tds till pnext == NULL
331 BOOLEAN
332 free_td(PUHCI_TD_POOL_LIST pool_list, PUHCI_TD ptd)
333 {
334 if (pool_list == NULL || ptd == NULL)
335 return FALSE;
336
337 if (free_td_to_pool(ptd->pool, ptd) == FALSE)
338 return FALSE;
339
340 pool_list->free_tds++;
341
342 if (is_pool_free(ptd->pool))
343 {
344 collect_garbage(pool_list);
345 }
346 return TRUE;
347 }
348
349 //null if failed
350 PUHCI_TD
351 alloc_td(PUHCI_TD_POOL_LIST pool_list)
352 {
353 PLIST_ENTRY prev, next;
354 PUHCI_TD new_td;
355
356 if (pool_list == NULL)
357 return NULL;
358
359 if (pool_list->free_tds == 0)
360 {
361 if (expand_pool_list(pool_list, 1) == FALSE)
362 return NULL;
363 }
364
365 ListFirst(&pool_list->busy_pools, prev);
366
367 while (prev && prev != &pool_list->busy_pools)
368 {
369 if (is_pool_empty((PUHCI_TD_POOL) prev) == FALSE)
370 {
371 new_td = alloc_td_from_pool((PUHCI_TD_POOL) prev);
372
373 if (new_td == NULL)
374 TRAP();
375
376 pool_list->free_tds--;
377
378 return new_td;
379 }
380
381 ListNext(&pool_list->busy_pools, prev, next);
382 prev = next;
383 }
384
385 return NULL;
386 }
387
388 PUHCI_TD
389 alloc_tds(PUHCI_TD_POOL_LIST pool_list, LONG count)
390 {
391 //return value is a list of tds, vert_link chain.
392
393 LONG i;
394 PUHCI_TD ptd, pnext;
395
396 if (pool_list == NULL || count <= 0)
397 return NULL;
398
399 if (count >= get_max_free_tds(pool_list))
400 return NULL;
401
402 ptd = alloc_td(pool_list);
403 if (!ptd) return NULL;
404
405 for(i = 1; i < count; i++)
406 {
407 pnext = alloc_td(pool_list);
408
409 if (pnext)
410 {
411 InsertTailList(&ptd->ptde->vert_link, &pnext->ptde->vert_link);
412 }
413 else
414 TRAP();
415 }
416
417 uhci_dbg_print(DBGLVL_MEDIUM, ("alloc_tds(): td pool-list free_tds=0x%x, free pools=0x%x\n",
418 pool_list->free_tds, pool_list->free_count));
419
420 return ptd;
421
422 }
423
424 VOID
425 free_tds(PUHCI_TD_POOL_LIST pool_list, PUHCI_TD ptd)
426 {
427 PUHCI_TD ptofree;
428 PLIST_ENTRY pthis;
429
430 if (pool_list == NULL || ptd == NULL)
431 return;
432
433 while (IsListEmpty(&ptd->ptde->vert_link) == FALSE)
434 {
435 pthis = RemoveHeadList(&ptd->ptde->vert_link);
436 ptofree = ((PTD_EXTENSION) pthis)->ptd;
437 free_td(pool_list, ptofree);
438 }
439
440 free_td(pool_list, ptd);
441 return;
442 }
443
444
445
446 BOOLEAN
447 can_transfer(PUHCI_TD_POOL_LIST pool_list, LONG td_count)
448 {
449 if (td_count > get_max_free_tds(pool_list))
450 return FALSE;
451
452 return TRUE;
453 }
454
455 VOID
456 lock_td_pool(PUHCI_TD_POOL_LIST pool_list, BOOLEAN at_dpc)
457 {
458 //if( !at_dpc )
459 // KeAcquireSpinLock( &pool_list->pool_lock );
460 //else
461 // KeAcquireSpinLockAtDpcLevel( &pool_list->pool_lock );
462 }
463
464 VOID
465 unlock_td_pool(PUHCI_TD_POOL_LIST pool_list, BOOLEAN at_dpc)
466 {
467 //if( !at_dpc )
468 // KeReleaseSpinLock( &pool_list->pool_lock );
469 //else
470 // KeReleaseSpinLockFromDpcLevel( &pool_list->pool_lock );
471 }
472
473 BOOLEAN
474 init_qh_pool(PUHCI_QH_POOL pqh_pool, PADAPTER_OBJECT padapter)
475 {
476 PQH_EXTENSION pqhe;
477 LONG i;
478
479 if (pqh_pool == NULL || padapter == NULL)
480 return FALSE;
481
482 pqh_pool->padapter = padapter;
483
484 pqh_pool->qhe_array = (PQH_EXTENSION) usb_alloc_mem(NonPagedPool,
485 sizeof(QH_EXTENSION) * UHCI_MAX_POOL_QHS);
486
487 if (pqh_pool->qhe_array == NULL)
488 return FALSE;
489
490 pqh_pool->qh_array =
491 (PUHCI_QH) HalAllocateCommonBuffer(padapter,
492 sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS, &pqh_pool->logic_addr, FALSE);
493
494 if (pqh_pool->qh_array == NULL)
495 {
496 usb_free_mem(pqh_pool->qhe_array);
497 pqh_pool->qhe_array = NULL;
498 return FALSE;
499 }
500
501 pqhe = pqh_pool->qhe_array;
502
503 pqh_pool->free_count = 0;
504 pqh_pool->total_count = UHCI_MAX_POOL_TDS;
505
506 KeInitializeSpinLock(&pqh_pool->pool_lock);
507 InitializeListHead(&pqh_pool->free_que);
508
509
510 for(i = 0; i < UHCI_MAX_POOL_QHS; i++)
511 {
512 pqh_pool->qh_array[i].pqhe = &pqhe[i];
513 pqhe[i].pqh = &pqh_pool->qh_array[i];
514
515 pqh_pool->qh_array[i].phy_addr = (pqh_pool->logic_addr.LowPart + (sizeof(UHCI_QH) * i)) | UHCI_PTR_QH;
516 //pqh_pool->qh_array[i].reserved = 0;
517
518 //always breadth first
519 pqhe[i].flags = UHCI_ITEM_FLAG_QH;
520
521 free_qh(pqh_pool, &pqh_pool->qh_array[i]);
522
523 }
524 return TRUE;
525
526 }
527
528 //add qhs till pnext == NULL
529 BOOLEAN
530 free_qh(PUHCI_QH_POOL pqh_pool, PUHCI_QH pqh)
531 {
532 if (pqh_pool == NULL || pqh == NULL)
533 return FALSE;
534
535 pqh->link = pqh->element = 0;
536 pqh->pqhe->purb = NULL;
537 InsertTailList(&pqh_pool->free_que, &pqh->pqhe->vert_link);
538 pqh_pool->free_count++;
539
540 return TRUE;
541 }
542
543 //null if failed
544 PUHCI_QH
545 alloc_qh(PUHCI_QH_POOL pqh_pool)
546 {
547 PQH_EXTENSION pqhe;
548
549 if (pqh_pool == NULL)
550 return FALSE;
551
552 if (IsListEmpty(&pqh_pool->free_que))
553 return FALSE;
554
555 pqhe = (PQH_EXTENSION) RemoveHeadList(&pqh_pool->free_que);
556
557 if (pqhe)
558 {
559 InitializeListHead(&pqhe->hori_link);
560 InitializeListHead(&pqhe->vert_link);
561 return pqhe->pqh;
562 }
563 return NULL;
564
565 }
566
567 BOOLEAN
568 destroy_qh_pool(PUHCI_QH_POOL pqh_pool)
569 {
570 if (pqh_pool)
571 {
572 usb_free_mem(pqh_pool->qhe_array);
573
574 HalFreeCommonBuffer(pqh_pool->padapter,
575 sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS,
576 pqh_pool->logic_addr, pqh_pool->qh_array, FALSE);
577
578 RtlZeroMemory(pqh_pool, sizeof(UHCI_QH_POOL));
579
580 }
581 else
582 return FALSE;
583
584 return TRUE;
585 }
586
587 VOID
588 lock_qh_pool(PUHCI_QH_POOL pool, BOOLEAN at_dpc)
589 {
590 //if( !at_dpc )
591 // KeAcquireSpinLock( &pool->pool_lock );
592 //else
593 // KeAcquireSpinLockAtDpcLevel( &pool->pool_lock );
594 }
595
596 VOID
597 unlock_qh_pool(PUHCI_QH_POOL pool, BOOLEAN at_dpc)
598 {
599 //if( !at_dpc )
600 // KeReleaseSpinLock( &pool->pool_lock );
601 //else
602 // KeReleaseSpinLockFromDpcLevel( &pool->pool_lock );
603 }