/** * etd.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 #include "ehci.h" #define init_elem( ptr, type, ehci_elem_type ) \ {\ PEHCI_ELEM_LINKS tmp;\ ptr = ( type* )( plist->phys_bufs[ i ] + sizeof( type ) * j );\ ptr->hw_next = ehci_elem_type << 1;\ if( ehci_elem_type == INIT_LIST_FLAG_QTD )\ ptr->hw_next = 0;\ tmp = ptr->elem_head_link = &plist->elem_head_buf[ i * elms_per_page + j ];\ InitializeListHead( &tmp->elem_link );\ InitializeListHead( &tmp->sched_link );\ InsertTailList( &plist->free_list, &tmp->elem_link );\ tmp->list_link = plist;\ tmp->pool_link = pinit_ctx->pool;\ tmp->phys_part = ( PVOID )( ( ULONG )ptr | ( ehci_elem_type << 1 ) );\ if( ehci_elem_type != INIT_LIST_FLAG_QTD )\ ptr->phys_addr = ( plist->phys_addrs[ i ].LowPart + sizeof( type ) * j ) | ( ehci_elem_type << 1 );\ else \ ptr->phys_addr = plist->phys_addrs[ i ].LowPart + sizeof( type ) * j ;\ } // get the actual max list count of the pool, two limit, max_elem_pool and max_lists_pool #define get_max_lists_count( pOOL, max_liSTS ) \ {\ LONG ii1=0;\ switch( elem_pool_get_type( pOOL ) )\ {\ case INIT_LIST_FLAG_QTD:\ ii1 = EHCI_MAX_QTDS_LIST;\ break;\ case INIT_LIST_FLAG_FSTN:\ ii1 = EHCI_MAX_QHS_LIST;\ break;\ case INIT_LIST_FLAG_SITD:\ ii1 = EHCI_MAX_ITDS_LIST;\ break;\ case INIT_LIST_FLAG_QH: \ ii1 = EHCI_MAX_SITDS_LIST;\ break;\ case INIT_LIST_FLAG_ITD: \ ii1 = EHCI_MAX_FSTNS_LIST;\ break;\ }\ max_liSTS = ( EHCI_MAX_ELEMS_POOL / ii1 ) > EHCI_MAX_LISTS_POOL ? EHCI_MAX_LISTS_POOL : ( EHCI_MAX_ELEMS_POOL / ii1 );\ } VOID elem_list_destroy_elem_list(PEHCI_ELEM_LIST plist); PLIST_ENTRY elem_list_get_list_head(PEHCI_ELEM_LIST plist); LONG elem_list_get_total_count(PEHCI_ELEM_LIST plist); LONG elem_list_get_elem_size(PEHCI_ELEM_LIST plist); LONG elem_list_get_link_offset(PEHCI_ELEM_LIST plist); LONG elem_list_add_ref(PEHCI_ELEM_LIST plist); LONG elem_list_release_ref(PEHCI_ELEM_LIST plist); LONG elem_list_get_ref(PEHCI_ELEM_LIST plist); BOOLEAN elem_pool_lock(PEHCI_ELEM_POOL pool, BOOLEAN at_dpc) { UNREFERENCED_PARAMETER(pool); UNREFERENCED_PARAMETER(at_dpc); return TRUE; } BOOLEAN elem_pool_unlock(PEHCI_ELEM_POOL pool, BOOLEAN at_dpc) { UNREFERENCED_PARAMETER(pool); UNREFERENCED_PARAMETER(at_dpc); return TRUE; } LONG get_elem_phys_part_size(ULONG type) { // type is INIT_LIST_FLAG_XXX LONG size; size = 0; switch (type) { case INIT_LIST_FLAG_ITD: size = 64; break; case INIT_LIST_FLAG_SITD: size = 28; break; case INIT_LIST_FLAG_QTD: size = 32; break; case INIT_LIST_FLAG_QH: size = 48; break; case INIT_LIST_FLAG_FSTN: size = 8; break; } return size; } BOOLEAN elem_list_init_elem_list(PEHCI_ELEM_LIST plist, LONG init_flags, PVOID context, LONG count) { LONG pages, i, j, elms_per_page; PEHCI_QH pqh; PEHCI_ITD pitd; PEHCI_SITD psitd; PEHCI_QTD pqtd; PEHCI_FSTN pfstn; PINIT_ELEM_LIST_CONTEXT pinit_ctx; UNREFERENCED_PARAMETER(count); if (plist == NULL || context == NULL) return FALSE; RtlZeroMemory(plist, sizeof(EHCI_ELEM_LIST)); pinit_ctx = context; plist->destroy_list = elem_list_destroy_elem_list; plist->get_list_head = elem_list_get_list_head; plist->get_total_count = elem_list_get_total_count; plist->get_elem_size = elem_list_get_elem_size; plist->get_link_offset = elem_list_get_link_offset; plist->add_ref = elem_list_add_ref; plist->release_ref = elem_list_release_ref; plist->get_ref = elem_list_get_ref; InitializeListHead(&plist->free_list); switch (init_flags & 0x0f) { case INIT_LIST_FLAG_ITD: plist->total_count = EHCI_MAX_ITDS_LIST; plist->elem_size = sizeof(EHCI_ITD); break; case INIT_LIST_FLAG_QH: plist->total_count = EHCI_MAX_QHS_LIST; plist->elem_size = sizeof(EHCI_QH); break; case INIT_LIST_FLAG_SITD: plist->total_count = EHCI_MAX_SITDS_LIST; plist->elem_size = sizeof(EHCI_SITD); break; case INIT_LIST_FLAG_FSTN: plist->total_count = EHCI_MAX_FSTNS_LIST; plist->elem_size = sizeof(EHCI_FSTN); break; case INIT_LIST_FLAG_QTD: plist->total_count = EHCI_MAX_QTDS_LIST; plist->elem_size = sizeof(EHCI_QTD); break; default: goto ERROR_OUT; } if (plist->elem_size & 0x1f) { plist->total_count = 0; goto ERROR_OUT; } plist->flags = init_flags; plist->parent_pool = pinit_ctx->pool; plist->padapter = pinit_ctx->padapter; pages = ((plist->elem_size * plist->total_count) + (PAGE_SIZE - 1)) / PAGE_SIZE; elms_per_page = PAGE_SIZE / plist->elem_size; plist->phys_addrs = usb_alloc_mem(NonPagedPool, (sizeof(PHYSICAL_ADDRESS) + sizeof(PBYTE)) * pages + sizeof(EHCI_ELEM_LINKS) * plist->total_count); if (plist->phys_addrs == NULL) { plist->total_count = 0; goto ERROR_OUT; } plist->phys_bufs = (PBYTE *) & plist->phys_addrs[pages]; plist->elem_head_buf = (PEHCI_ELEM_LINKS) & plist->phys_bufs[pages]; RtlZeroMemory(plist->phys_addrs, (sizeof(PHYSICAL_ADDRESS) + sizeof(PBYTE)) * pages + sizeof(EHCI_ELEM_LINKS) * plist->total_count); for(i = 0; i < pages; i++) { plist->phys_bufs[i] = HalAllocateCommonBuffer(plist->padapter, PAGE_SIZE, &plist->phys_addrs[i], FALSE); if (plist->phys_bufs[i] == NULL) { // failed, roll back for(j = i - 1; j >= 0; j--) HalFreeCommonBuffer(plist->padapter, PAGE_SIZE, plist->phys_addrs[j], plist->phys_bufs[j], FALSE); goto ERROR_OUT; } RtlZeroMemory(plist->phys_bufs[i], PAGE_SIZE); for(j = 0; j < elms_per_page; j++) { switch (init_flags & 0xf) { case INIT_LIST_FLAG_QH: { init_elem(pqh, EHCI_QH, INIT_LIST_FLAG_QH); break; } case INIT_LIST_FLAG_ITD: { init_elem(pitd, EHCI_ITD, INIT_LIST_FLAG_ITD); break; } case INIT_LIST_FLAG_QTD: { init_elem(pqtd, EHCI_QTD, INIT_LIST_FLAG_QTD); break; } case INIT_LIST_FLAG_SITD: { init_elem(psitd, EHCI_SITD, INIT_LIST_FLAG_SITD); break; } case INIT_LIST_FLAG_FSTN: { init_elem(pfstn, EHCI_FSTN, INIT_LIST_FLAG_FSTN); break; } default: TRAP(); } } } return TRUE; ERROR_OUT: if (plist->phys_addrs != NULL) usb_free_mem(plist->phys_addrs); RtlZeroMemory(plist, sizeof(EHCI_ELEM_LIST)); return FALSE; } VOID elem_list_destroy_elem_list(PEHCI_ELEM_LIST plist) { LONG i, pages; if (plist == NULL) return; pages = (plist->total_count * plist->elem_size + PAGE_SIZE - 1) / PAGE_SIZE; for(i = 0; i < pages; i++) HalFreeCommonBuffer(plist->padapter, PAGE_SIZE, plist->phys_addrs[i], plist->phys_bufs[i], FALSE); usb_free_mem(plist->phys_addrs); RtlZeroMemory(plist, sizeof(EHCI_ELEM_LIST)); } PLIST_ENTRY elem_list_get_list_head(PEHCI_ELEM_LIST plist) { if (plist == NULL) return NULL; return &plist->free_list; } LONG elem_list_get_total_count(PEHCI_ELEM_LIST plist) { if (plist == NULL) return 0; return plist->total_count; } LONG elem_list_get_elem_size(PEHCI_ELEM_LIST plist) { if (plist == NULL) return 0; return plist->elem_size; } LONG elem_list_get_link_offset(PEHCI_ELEM_LIST plist) { if (plist == NULL) return 0; return get_elem_phys_part_size(plist->flags & 0xf); } LONG elem_list_add_ref(PEHCI_ELEM_LIST plist) { plist->reference++; return plist->reference; } LONG elem_list_release_ref(PEHCI_ELEM_LIST plist) { plist->reference--; return plist->reference; } LONG elem_list_get_ref(PEHCI_ELEM_LIST plist) { return plist->reference; } // // pool methods // BOOLEAN elem_pool_init_pool(PEHCI_ELEM_POOL pool, LONG flags, PVOID context) { INIT_ELEM_LIST_CONTEXT init_ctx; if (pool == NULL || context == NULL) return FALSE; RtlZeroMemory(pool, sizeof(EHCI_ELEM_POOL)); init_ctx.pool = pool; init_ctx.padapter = context; pool->elem_lists[0] = usb_alloc_mem(NonPagedPool, sizeof(EHCI_ELEM_LIST)); if (pool->elem_lists[0] == NULL) return FALSE; if (elem_list_init_elem_list(pool->elem_lists[0], flags, &init_ctx, 0) == FALSE) { usb_free_mem(pool->elem_lists[0]); return FALSE; } pool->link_offset = pool->elem_lists[0]->get_link_offset(pool->elem_lists[0]); pool->free_count = pool->elem_lists[0]->get_total_count(pool->elem_lists[0]); pool->list_count = 1; pool->flags = flags; return TRUE; } LONG elem_pool_get_link_offset(PEHCI_ELEM_POOL elem_pool) { return elem_pool->link_offset; } LONG elem_pool_get_total_count(PEHCI_ELEM_POOL elem_pool) { return elem_pool->elem_lists[0]->get_total_count(elem_pool->elem_lists[0]) * elem_pool->list_count; } VOID elem_pool_destroy_pool(PEHCI_ELEM_POOL pool) { LONG i; if (pool == NULL) return; for(i = pool->list_count - 1; i >= 0; i--) { pool->elem_lists[i]->destroy_list(pool->elem_lists[i]); usb_free_mem(pool->elem_lists[i]); pool->elem_lists[i] = NULL; } RtlZeroMemory(pool, sizeof(EHCI_ELEM_POOL)); return; } PEHCI_ELEM_LINKS elem_pool_alloc_elem(PEHCI_ELEM_POOL pool) { LONG i; PEHCI_ELEM_LIST pel = NULL; PLIST_HEAD lh; PEHCI_ELEM_LINKS elnk; if (pool == NULL) return NULL; for(i = 0; i < pool->list_count; i++) { pel = pool->elem_lists[i]; if (pel->get_ref(pel) == pel->get_total_count(pel)) continue; break; } if (i == pool->list_count) { if (elem_pool_expand_pool(pool, pel->get_total_count(pel)) == FALSE) return NULL; pel = pool->elem_lists[i]; } lh = pel->get_list_head(pel); elnk = (PEHCI_ELEM_LINKS) RemoveHeadList(lh); InitializeListHead(&elnk->elem_link); InitializeListHead(&elnk->sched_link); pel->add_ref(pel); pool->free_count--; return elnk; } VOID elem_pool_free_elem(PEHCI_ELEM_LINKS elem_link) { PLIST_HEAD lh; LONG ref; PEHCI_ELEM_POOL pool; if (elem_link == NULL) return; pool = elem_link->pool_link; lh = elem_link->list_link->get_list_head(elem_link->list_link); if (lh == NULL) return; InsertHeadList(lh, &elem_link->elem_link); ref = elem_link->list_link->release_ref(elem_link->list_link); pool->free_count++; if (ref == 0) elem_pool_collect_garbage(pool); return; } BOOLEAN elem_pool_is_empty(PEHCI_ELEM_POOL pool) { PEHCI_ELEM_LIST pel; if (pool == NULL) return TRUE; pel = pool->elem_lists[0]; return (BOOLEAN) (pool->list_count == 1 && pool->free_count == pel->get_total_count(pel)); } LONG elem_pool_get_free_count(PEHCI_ELEM_POOL pool) { if (pool == NULL) return 0; return pool->free_count; } PEHCI_ELEM_LINKS elem_pool_alloc_elems(PEHCI_ELEM_POOL pool, LONG count) { LIST_HEAD lh; PLIST_ENTRY pthis; LONG i, alloc_count, max_pool_lists; PEHCI_ELEM_LIST pel; PEHCI_ELEM_LINKS elnk; // calculate to see if the count is affordable if (pool == NULL || count <= 0) return NULL; get_max_lists_count(pool, max_pool_lists); InitializeListHead(&lh); pel = pool->elem_lists[0]; if (count <= pool->free_count) alloc_count = 0; else alloc_count = count - pool->free_count; if (alloc_count > pel->get_total_count(pel) * (max_pool_lists - pool->list_count)) return NULL; for(i = 0; i < count; i++) { if ((elnk = elem_pool_alloc_elem(pool)) == NULL) { // undo what we have done while (IsListEmpty(&lh) == FALSE) { pthis = RemoveHeadList(&lh); elnk = struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link); elem_pool_free_elem(elnk); } return NULL; } InsertTailList(&lh, &elnk->elem_link); } ListFirst(&lh, pthis); elnk = struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link); RemoveEntryList(&lh); return elnk; } BOOLEAN elem_pool_free_elems(PEHCI_ELEM_LINKS elem_chains) { // note: no list head exists. LIST_HEAD lh; PEHCI_ELEM_LINKS elnk; InsertTailList(&elem_chains->elem_link, &lh); while (IsListEmpty(&lh) == FALSE) { elnk = (PEHCI_ELEM_LINKS) RemoveHeadList(&lh); elem_pool_free_elem(elnk); } return TRUE; } LONG elem_pool_get_type(PEHCI_ELEM_POOL pool) { if (pool == NULL) return -1; return (pool->flags & 0xf); } BOOLEAN elem_pool_expand_pool(PEHCI_ELEM_POOL pool, LONG elem_count) { LONG elem_cnt_list, list_count, i, j; INIT_ELEM_LIST_CONTEXT init_ctx; if (pool == NULL || elem_count <= 0 || elem_count > EHCI_MAX_ELEMS_POOL) return FALSE; init_ctx.pool = pool; init_ctx.padapter = pool->elem_lists[0]->padapter; elem_cnt_list = pool->elem_lists[0]->get_total_count(pool->elem_lists[0]); list_count = (elem_count + elem_cnt_list - 1) / elem_cnt_list; get_max_lists_count(pool, i); if (list_count + pool->list_count > i) return FALSE; for(i = pool->list_count; i < list_count + pool->list_count; i++) { pool->elem_lists[i] = usb_alloc_mem(NonPagedPool, sizeof(EHCI_ELEM_LIST)); if (elem_list_init_elem_list(pool->elem_lists[i], pool->flags, &init_ctx, 0) == FALSE) break; } if (i < list_count + pool->list_count) { // undo all we have done for(j = pool->list_count; j < pool->list_count + i; j++) { pool->elem_lists[j]->destroy_list(pool->elem_lists[j]); usb_free_mem(pool->elem_lists[j]); pool->elem_lists[j] = NULL; } return FALSE; } // update pool pool->free_count += elem_cnt_list * list_count; pool->list_count += list_count; return TRUE; } BOOLEAN elem_pool_collect_garbage(PEHCI_ELEM_POOL pool) { LONG i, j, k, fl; LONG free_elem_lists[EHCI_MAX_LISTS_POOL - 1]; PEHCI_ELEM_LIST pel; if (pool == NULL) return FALSE; for(i = 1, fl = 0; i < pool->list_count; i++) { if (pool->elem_lists[i]->get_ref(pool->elem_lists[i]) == 0) { free_elem_lists[fl++] = i; } } for(j = fl - 1; j >= 0; j--) { pel = pool->elem_lists[free_elem_lists[j]]; pel->destroy_list(pel); usb_free_mem(pel); for(k = free_elem_lists[j] + 1; k < pool->list_count; k++) { // shrink the elem_lists pool->elem_lists[k - 1] = pool->elem_lists[k]; } pool->elem_lists[k] = NULL; pel = pool->elem_lists[0]; pool->free_count -= pel->get_total_count(pel); pool->list_count--; } return TRUE; } BOOLEAN elem_pool_can_transfer(PEHCI_ELEM_POOL pool, LONG td_count) { LONG i; if (pool == NULL || td_count <= 0) return FALSE; get_max_lists_count(pool, i); if ((i - pool->list_count) * pool->elem_lists[0]->get_total_count(pool->elem_lists[0]) + pool->free_count < td_count) return FALSE; return TRUE; } //----------------------------------------------------------