reactos/drivers/usb/nt4compat/usbdriver/etd.c
Amine Khaldi c424146e2c Create a branch for cmake bringup.
svn path=/branches/cmake-bringup/; revision=48236
2010-07-24 18:52:44 +00:00

644 lines
17 KiB
C

/**
* 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 <usbdriver.h>
#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;
}
//----------------------------------------------------------