/** * td.c - USB driver stack project for Windows NT 4.0 * * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program/include file is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program (in the main directory of the distribution, the file * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "usbdriver.h" #define UHCI_MIN_TD_POOLS 4 BOOLEAN free_td_to_pool(PUHCI_TD_POOL ptd_pool, PUHCI_TD ptd); //add tds till pnext == NULL PUHCI_QH alloc_qh(PUHCI_QH_POOL pqh_pool); //null if failed BOOLEAN init_td_pool(PUHCI_TD_POOL ptd_pool) { int i, pages; PTD_EXTENSION ptde; if (ptd_pool == NULL) return FALSE; if (ptd_pool->padapter == NULL) return FALSE; pages = sizeof(UHCI_TD) * UHCI_MAX_POOL_TDS / PAGE_SIZE; RtlZeroMemory(ptd_pool->td_array, sizeof(ptd_pool->td_array)); RtlZeroMemory(ptd_pool->logic_addr, sizeof(ptd_pool->logic_addr)); for(i = 0; i < pages; i++) { ptd_pool->td_array[i] = HalAllocateCommonBuffer(ptd_pool->padapter, PAGE_SIZE, &ptd_pool->logic_addr[i], FALSE); if (ptd_pool->td_array[i] == NULL) goto failed; } ptd_pool->tde_array = (PTD_EXTENSION) usb_alloc_mem(NonPagedPool, sizeof(TD_EXTENSION) * UHCI_MAX_POOL_TDS); if (ptd_pool->tde_array == NULL) goto failed; for(i = 0; i < pages; i++) { RtlZeroMemory(ptd_pool->td_array[i], PAGE_SIZE); } RtlZeroMemory(ptd_pool->tde_array, sizeof(TD_EXTENSION) * UHCI_MAX_POOL_TDS); ptde = ptd_pool->tde_array; ptd_pool->free_count = 0; ptd_pool->total_count = UHCI_MAX_POOL_TDS; InitializeListHead(&ptd_pool->free_que); for(i = 0; i < UHCI_MAX_POOL_TDS; i++) { //link tde and the td one by one, fixed since this init ptd_pool->td_array[i >> 7][i & 0x7f].ptde = &ptde[i]; ptde[i].ptd = &ptd_pool->td_array[i >> 7][i & 0x7f]; ptde[i].flags = UHCI_ITEM_FLAG_TD; ptd_pool->td_array[i >> 7][i & 0x7f].phy_addr = ptd_pool->logic_addr[i >> 7].LowPart + (i & 0x7f) * sizeof(UHCI_TD); ptd_pool->td_array[i >> 7][i & 0x7f].pool = ptd_pool; ptd_pool->td_array[i >> 7][i & 0x7f].purb = NULL; free_td_to_pool(ptd_pool, &ptd_pool->td_array[i >> 7][i & 0x7f]); } return TRUE; failed: for(i = 0; i < pages; i++) { if (ptd_pool->td_array[i]) { HalFreeCommonBuffer(ptd_pool->padapter, PAGE_SIZE, ptd_pool->logic_addr[i], ptd_pool->td_array[i], FALSE); ptd_pool->td_array[i] = NULL; ptd_pool->logic_addr[i].QuadPart = 0; } } if (ptd_pool->tde_array) usb_free_mem(ptd_pool->tde_array); uhci_dbg_print(DBGLVL_MAXIMUM, ("init_td_pool(): failed to init the td pool\n")); TRAP(); ptd_pool->free_count = ptd_pool->total_count = 0; return FALSE; } //add tds till pnext == NULL BOOLEAN free_td_to_pool(PUHCI_TD_POOL ptd_pool, PUHCI_TD ptd) { if (ptd_pool == NULL || ptd == NULL) { return FALSE; } ptd->link = ptd->status = ptd->info = ptd->buffer = 0; ptd->purb = NULL; ptd_pool->free_count++; InsertTailList(&ptd_pool->free_que, &ptd->ptde->vert_link); return TRUE; } // qh routines //null if failed PUHCI_TD alloc_td_from_pool(PUHCI_TD_POOL ptd_pool) { PTD_EXTENSION ptde; PLIST_ENTRY temp; if (ptd_pool == NULL) return FALSE; if (IsListEmpty(&ptd_pool->free_que)) return FALSE; temp = RemoveHeadList(&ptd_pool->free_que); if (temp == NULL) return FALSE; ptde = struct_ptr(temp, TD_EXTENSION, vert_link); ptd_pool->free_count--; InitializeListHead(&ptde->vert_link); InitializeListHead(&ptde->hori_link); return ptde->ptd; } //test whether the pool is all free BOOLEAN is_pool_free(PUHCI_TD_POOL pool) { if (pool == NULL) return FALSE; if (pool->free_count == pool->total_count) return TRUE; return FALSE; } BOOLEAN is_pool_empty(PUHCI_TD_POOL pool) { if (pool == NULL) return FALSE; return (BOOLEAN) (pool->free_count == 0); } BOOLEAN destroy_td_pool(PUHCI_TD_POOL ptd_pool) { int i, pages; PADAPTER_OBJECT padapter; //we need this garbage for allocation padapter = ptd_pool->padapter; pages = sizeof(UHCI_TD) * UHCI_MAX_POOL_TDS / PAGE_SIZE; if (ptd_pool && ptd_pool->padapter) { usb_free_mem(ptd_pool->tde_array); ptd_pool->tde_array = NULL; for(i = 0; i < pages; i++) { if (ptd_pool->td_array[i]) { HalFreeCommonBuffer(ptd_pool->padapter, PAGE_SIZE, ptd_pool->logic_addr[i], ptd_pool->td_array[i], FALSE); ptd_pool->td_array[i] = NULL; ptd_pool->logic_addr[i].QuadPart = 0; } } RtlZeroMemory(ptd_pool, sizeof(UHCI_TD_POOL)); ptd_pool->padapter = padapter; ptd_pool->free_count = ptd_pool->total_count = 0; } else return FALSE; return TRUE; } BOOLEAN init_td_pool_list(PUHCI_TD_POOL_LIST pool_list, PADAPTER_OBJECT padapter) { int i; RtlZeroMemory(pool_list, sizeof(UHCI_TD_POOL_LIST)); InitializeListHead(&pool_list->busy_pools); InitializeListHead(&pool_list->free_pools); pool_list->free_count = UHCI_MAX_TD_POOLS; pool_list->free_tds = 0; for(i = 0; i < UHCI_MAX_TD_POOLS; i++) { pool_list->pool_array[i].padapter = padapter; InsertTailList(&pool_list->free_pools, &pool_list->pool_array[i].pool_link); } KeInitializeSpinLock(&pool_list->pool_lock); return expand_pool_list(pool_list, UHCI_MIN_TD_POOLS); } BOOLEAN destroy_td_pool_list(PUHCI_TD_POOL_LIST pool_list) { PUHCI_TD_POOL pool; while (IsListEmpty(&pool_list->busy_pools) == FALSE) { pool = (PUHCI_TD_POOL) RemoveHeadList(&pool_list->busy_pools); destroy_td_pool(pool); } RtlZeroMemory(pool_list, sizeof(UHCI_TD_POOL_LIST)); return TRUE; } BOOLEAN expand_pool_list(PUHCI_TD_POOL_LIST pool_list, LONG pool_count) //private { PUHCI_TD_POOL pool; int i; if (IsListEmpty(&pool_list->free_pools) == TRUE) return FALSE; if (pool_list->free_count < pool_count) return FALSE; for(i = 0; i < pool_count; i++) { pool = (PUHCI_TD_POOL) RemoveHeadList(&pool_list->free_pools); if (init_td_pool(pool) == FALSE) { //reverse the allocation InsertHeadList(&pool_list->free_pools, &pool->pool_link); // collect_garbage( pool_list ); return FALSE; } InsertTailList(&pool_list->busy_pools, &pool->pool_link); pool_list->free_tds += UHCI_MAX_POOL_TDS; pool_list->free_count--; } return TRUE; } BOOLEAN collect_garbage(PUHCI_TD_POOL_LIST pool_list) { PLIST_ENTRY prev, next; // no garbage if (pool_list->free_count >= UHCI_MAX_TD_POOLS - UHCI_MIN_TD_POOLS) return TRUE; ListFirstPrev(&pool_list->busy_pools, prev); ListNext(&pool_list->busy_pools, prev, next); while (next && next != &pool_list->busy_pools) { if (is_pool_free((PUHCI_TD_POOL) next)) { RemoveEntryList(next); destroy_td_pool((PUHCI_TD_POOL) next); InsertTailList(&pool_list->free_pools, next); pool_list->free_count++; pool_list->free_tds -= UHCI_MAX_POOL_TDS; ListNext(&pool_list->busy_pools, prev, next); if (pool_list->free_count >= UHCI_MAX_TD_POOLS - UHCI_MIN_TD_POOLS) break; } else { prev = next; ListNext(&pool_list->busy_pools, prev, next); } } return TRUE; } //private LONG get_num_free_tds(PUHCI_TD_POOL_LIST pool_list) { return pool_list->free_tds; } //private LONG get_max_free_tds(PUHCI_TD_POOL_LIST pool_list) { return pool_list->free_tds + pool_list->free_count * UHCI_MAX_POOL_TDS; } //add tds till pnext == NULL BOOLEAN free_td(PUHCI_TD_POOL_LIST pool_list, PUHCI_TD ptd) { if (pool_list == NULL || ptd == NULL) return FALSE; if (free_td_to_pool(ptd->pool, ptd) == FALSE) return FALSE; pool_list->free_tds++; if (is_pool_free(ptd->pool)) { collect_garbage(pool_list); } return TRUE; } //null if failed PUHCI_TD alloc_td(PUHCI_TD_POOL_LIST pool_list) { PLIST_ENTRY prev, next; PUHCI_TD new_td; if (pool_list == NULL) return NULL; if (pool_list->free_tds == 0) { if (expand_pool_list(pool_list, 1) == FALSE) return NULL; } ListFirst(&pool_list->busy_pools, prev); while (prev && prev != &pool_list->busy_pools) { if (is_pool_empty((PUHCI_TD_POOL) prev) == FALSE) { new_td = alloc_td_from_pool((PUHCI_TD_POOL) prev); if (new_td == NULL) TRAP(); pool_list->free_tds--; return new_td; } ListNext(&pool_list->busy_pools, prev, next); prev = next; } return NULL; } PUHCI_TD alloc_tds(PUHCI_TD_POOL_LIST pool_list, LONG count) { //return value is a list of tds, vert_link chain. LONG i; PUHCI_TD ptd, pnext; if (pool_list == NULL || count <= 0) return NULL; if (count >= get_max_free_tds(pool_list)) return NULL; ptd = alloc_td(pool_list); if (!ptd) return NULL; for(i = 1; i < count; i++) { pnext = alloc_td(pool_list); if (pnext) { InsertTailList(&ptd->ptde->vert_link, &pnext->ptde->vert_link); } else TRAP(); } uhci_dbg_print(DBGLVL_MEDIUM, ("alloc_tds(): td pool-list free_tds=0x%x, free pools=0x%x\n", pool_list->free_tds, pool_list->free_count)); return ptd; } VOID free_tds(PUHCI_TD_POOL_LIST pool_list, PUHCI_TD ptd) { PUHCI_TD ptofree; PLIST_ENTRY pthis; if (pool_list == NULL || ptd == NULL) return; while (IsListEmpty(&ptd->ptde->vert_link) == FALSE) { pthis = RemoveHeadList(&ptd->ptde->vert_link); ptofree = ((PTD_EXTENSION) pthis)->ptd; free_td(pool_list, ptofree); } free_td(pool_list, ptd); return; } BOOLEAN can_transfer(PUHCI_TD_POOL_LIST pool_list, LONG td_count) { if (td_count > get_max_free_tds(pool_list)) return FALSE; return TRUE; } VOID lock_td_pool(PUHCI_TD_POOL_LIST pool_list, BOOLEAN at_dpc) { //if( !at_dpc ) // KeAcquireSpinLock( &pool_list->pool_lock ); //else // KeAcquireSpinLockAtDpcLevel( &pool_list->pool_lock ); } VOID unlock_td_pool(PUHCI_TD_POOL_LIST pool_list, BOOLEAN at_dpc) { //if( !at_dpc ) // KeReleaseSpinLock( &pool_list->pool_lock ); //else // KeReleaseSpinLockFromDpcLevel( &pool_list->pool_lock ); } BOOLEAN init_qh_pool(PUHCI_QH_POOL pqh_pool, PADAPTER_OBJECT padapter) { PQH_EXTENSION pqhe; LONG i; if (pqh_pool == NULL || padapter == NULL) return FALSE; pqh_pool->padapter = padapter; pqh_pool->qhe_array = (PQH_EXTENSION) usb_alloc_mem(NonPagedPool, sizeof(QH_EXTENSION) * UHCI_MAX_POOL_QHS); if (pqh_pool->qhe_array == NULL) return FALSE; pqh_pool->qh_array = (PUHCI_QH) HalAllocateCommonBuffer(padapter, sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS, &pqh_pool->logic_addr, FALSE); if (pqh_pool->qh_array == NULL) { usb_free_mem(pqh_pool->qhe_array); pqh_pool->qhe_array = NULL; return FALSE; } pqhe = pqh_pool->qhe_array; pqh_pool->free_count = 0; pqh_pool->total_count = UHCI_MAX_POOL_TDS; KeInitializeSpinLock(&pqh_pool->pool_lock); InitializeListHead(&pqh_pool->free_que); for(i = 0; i < UHCI_MAX_POOL_QHS; i++) { pqh_pool->qh_array[i].pqhe = &pqhe[i]; pqhe[i].pqh = &pqh_pool->qh_array[i]; pqh_pool->qh_array[i].phy_addr = (pqh_pool->logic_addr.LowPart + (sizeof(UHCI_QH) * i)) | UHCI_PTR_QH; //pqh_pool->qh_array[i].reserved = 0; //always breadth first pqhe[i].flags = UHCI_ITEM_FLAG_QH; free_qh(pqh_pool, &pqh_pool->qh_array[i]); } return TRUE; } //add qhs till pnext == NULL BOOLEAN free_qh(PUHCI_QH_POOL pqh_pool, PUHCI_QH pqh) { if (pqh_pool == NULL || pqh == NULL) return FALSE; pqh->link = pqh->element = 0; pqh->pqhe->purb = NULL; InsertTailList(&pqh_pool->free_que, &pqh->pqhe->vert_link); pqh_pool->free_count++; return TRUE; } //null if failed PUHCI_QH alloc_qh(PUHCI_QH_POOL pqh_pool) { PQH_EXTENSION pqhe; if (pqh_pool == NULL) return FALSE; if (IsListEmpty(&pqh_pool->free_que)) return FALSE; pqhe = (PQH_EXTENSION) RemoveHeadList(&pqh_pool->free_que); if (pqhe) { InitializeListHead(&pqhe->hori_link); InitializeListHead(&pqhe->vert_link); return pqhe->pqh; } return NULL; } BOOLEAN destroy_qh_pool(PUHCI_QH_POOL pqh_pool) { if (pqh_pool) { usb_free_mem(pqh_pool->qhe_array); HalFreeCommonBuffer(pqh_pool->padapter, sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS, pqh_pool->logic_addr, pqh_pool->qh_array, FALSE); RtlZeroMemory(pqh_pool, sizeof(UHCI_QH_POOL)); } else return FALSE; return TRUE; } VOID lock_qh_pool(PUHCI_QH_POOL pool, BOOLEAN at_dpc) { //if( !at_dpc ) // KeAcquireSpinLock( &pool->pool_lock ); //else // KeAcquireSpinLockAtDpcLevel( &pool->pool_lock ); } VOID unlock_qh_pool(PUHCI_QH_POOL pool, BOOLEAN at_dpc) { //if( !at_dpc ) // KeReleaseSpinLock( &pool->pool_lock ); //else // KeReleaseSpinLockFromDpcLevel( &pool->pool_lock ); }