mirror of
https://github.com/reactos/reactos.git
synced 2024-11-09 16:20:37 +00:00
5eb25b5c24
svn path=/branches/audio-bringup/; revision=49478
6327 lines
198 KiB
C
6327 lines
198 KiB
C
/**
|
|
* ehci.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"
|
|
|
|
//----------------------------------------------------------
|
|
// ehci routines
|
|
//#define DEMO
|
|
|
|
#define DEFAULT_ENDP( enDP ) \
|
|
( enDP->flags & USB_ENDP_FLAG_DEFAULT_ENDP )
|
|
|
|
#define dev_from_endp( enDP ) \
|
|
( DEFAULT_ENDP( enDP )\
|
|
? ( ( PUSB_DEV )( enDP )->pusb_if )\
|
|
: ( ( enDP )->pusb_if->pusb_config->pusb_dev ) )
|
|
|
|
#define endp_state( enDP ) ( ( enDP )->flags & USB_ENDP_FLAG_STAT_MASK )
|
|
|
|
#define endp_num( enDP ) \
|
|
( DEFAULT_ENDP( enDP )\
|
|
? 0 \
|
|
: ( ( enDP )->pusb_endp_desc->bEndpointAddress & 0x0f ) )
|
|
|
|
#define endp_dir( enDP ) \
|
|
( DEFAULT_ENDP( enDP )\
|
|
? 0L\
|
|
: ( ( enDP )->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) ? 1 : 0 )
|
|
|
|
#define dev_set_state( pdEV, staTE ) \
|
|
( pdEV->flags = ( ( pdEV )->flags & ( ~USB_DEV_STATE_MASK ) ) | ( staTE ) )
|
|
|
|
#define endp_max_packet_size( enDP ) \
|
|
( DEFAULT_ENDP( enDP )\
|
|
? ( ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc ? \
|
|
( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc->bMaxPacketSize0\
|
|
: 8 )\
|
|
: ( enDP->pusb_endp_desc->wMaxPacketSize & 0x7ff ) )
|
|
|
|
#define endp_mult_count( endp ) ( ( ( endp->pusb_endp_desc->wMaxPacketSize & 0x1800 ) >> 11 ) + 1 )
|
|
|
|
#define release_adapter( padapTER ) HalPutDmaAdapter(padapTER)
|
|
|
|
#define get_int_idx( _urb, _idx ) \
|
|
{\
|
|
UCHAR interVAL;\
|
|
interVAL = ( UCHAR )( ( _urb )->pipe >> 24 );\
|
|
for( _idx = 1; _idx < 9; _idx++ )\
|
|
{\
|
|
interVAL >>= 1;\
|
|
if( !interVAL )\
|
|
break;\
|
|
}\
|
|
_idx --;\
|
|
}
|
|
|
|
#define ehci_insert_urb_to_schedule( eHCI, pURB, rET ) \
|
|
{\
|
|
SYNC_PARAM sync_param;\
|
|
sync_param.ehci = eHCI;\
|
|
sync_param.context = ( pURB );\
|
|
sync_param.ret = FALSE;\
|
|
\
|
|
rET = KeSynchronizeExecution( eHCI->pdev_ext->ehci_int, ehci_sync_insert_urb_schedule, &sync_param );\
|
|
}
|
|
|
|
#define EHCI_ERROR_INT ( STS_FATAL | STS_ERR )
|
|
#define EHCI_QH_ERROR( qh_contENT ) ( ( qh_contENT )->cur_qtd.status & ( QTD_STS_HALT | QTD_STS_DBE | QTD_STS_BABBLE | QTD_STS_XACT | QTD_STS_MMF ) )
|
|
#define EHCI_QTD_ERROR( qtd_contENT ) ( ( qtd_contENT )->status & ( QTD_STS_HALT | QTD_STS_DBE | QTD_STS_BABBLE | QTD_STS_XACT | QTD_STS_MMF ) )
|
|
|
|
#define EHCI_READ_PORT_ULONG( pul ) ( *pul )
|
|
#define EHCI_WRITE_PORT_ULONG( pul, src ) \
|
|
{\
|
|
ULONG cmd_reg;\
|
|
*pul = ( ULONG )src;\
|
|
cmd_reg = EHCI_READ_PORT_ULONG( ehci->port_base + EHCI_USBCMD );\
|
|
if( cmd_reg == 0 )\
|
|
cmd_reg++;\
|
|
}
|
|
|
|
#define EHCI_READ_PORT_UCHAR( pch ) ( *pch )
|
|
#define EHCI_WRITE_PORT_UCHAR( pch, src ) ( *pch = ( UCHAR )src )
|
|
|
|
#define EHCI_READ_PORT_USHORT( psh ) ( *psh )
|
|
#define EHCI_WRITE_PORT_USHORT( psh, src ) ( *psh = ( USHORT )src )
|
|
|
|
#define press_doorbell( eHCI ) \
|
|
{\
|
|
ULONG tmp;\
|
|
tmp = EHCI_READ_PORT_ULONG( ( PULONG )( ( eHCI )->port_base + EHCI_USBCMD ) );\
|
|
tmp |= CMD_IAAD;\
|
|
EHCI_WRITE_PORT_ULONG( ( PULONG )( ( eHCI )->port_base + EHCI_USBCMD ), tmp );\
|
|
}
|
|
#define ehci_from_hcd( hCD ) ( struct_ptr( ( hCD ), EHCI_DEV, hcd_interf ) )
|
|
|
|
#define qh_from_list_entry( pentry ) ( ( PEHCI_QH )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
#define qtd_from_list_entry( pentry ) ( ( PEHCI_QTD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
#define itd_from_list_entry( pentry ) ( ( PEHCI_ITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
#define sitd_from_list_entry( pentry ) ( ( PEHCI_SITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
#define fstn_from_list_entry( pentry ) ( ( PEHCI_FSTN )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
|
|
#define qh_from_schedule( pentry ) ( ( PEHCI_QH )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
#define itd_from_schedule( pentry ) ( ( PEHCI_ITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
#define sitd_from_schedule( pentry ) ( ( PEHCI_SITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
#define fstn_from_schedule( pentry ) ( ( PEHCI_FSTN )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
|
|
|
|
#define elem_type( ptr, from_list ) ( from_list ? ( ( ( ( ULONG )struct_ptr( ptr, EHCI_ELEM_LINKS, elem_link)->phys_part ) & PHYS_PART_TYPE_MASK ) >> 1 ) \
|
|
: ( ( ( ( ULONG )struct_ptr( ptr, EHCI_ELEM_LINKS, sched_link)->phys_part ) & PHYS_PART_TYPE_MASK ) >> 1 ) )
|
|
|
|
// #define elem_type_list_entry( pentry ) ( ( qh_from_schedule( pentry )->hw_next & 0x06 ) >> 1 )
|
|
#define elem_type_list_entry( pentry ) ( elem_type( pentry, TRUE ) )
|
|
|
|
#define get_parent_hs_hub( pDEV, parent_HUB, port_IDX ) \
|
|
{\
|
|
parent_HUB = pDEV->parent_dev;\
|
|
port_IDX = pdev->port_idx;\
|
|
while( parent_HUB )\
|
|
{\
|
|
if( ( parent_HUB->flags & USB_DEV_CLASS_MASK ) != USB_DEV_CLASS_HUB )\
|
|
{\
|
|
parent_HUB = NULL;\
|
|
break;\
|
|
}\
|
|
if( ( parent_HUB->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 )\
|
|
{\
|
|
port_IDX = parent_HUB->port_idx;\
|
|
parent_HUB = parent_HUB->parent_dev;\
|
|
continue;\
|
|
}\
|
|
break;\
|
|
}\
|
|
}
|
|
|
|
#define init_elem_phys_part( pelnk ) RtlZeroMemory( ( PVOID )( ( ( ULONG )( pelnk )->phys_part ) & PHYS_PART_ADDR_MASK ), get_elem_phys_part_size( ( ( ( ULONG )( pelnk )->phys_part ) & 0x06 ) >> 1 ) )
|
|
#define REAL_INTERVAL ( 1 << pipe_content->interval )
|
|
|
|
#define elem_safe_free( ptHIS, single ) \
|
|
{\
|
|
UCHAR em_type; \
|
|
em_type = ( UCHAR )elem_type( ptHIS, TRUE ); \
|
|
if( ptHIS )\
|
|
{\
|
|
if( em_type == INIT_LIST_FLAG_QTD )\
|
|
{\
|
|
elem_pool_lock( qtd_pool, TRUE );\
|
|
if( single )\
|
|
elem_pool_free_elem( qtd_from_list_entry( ptHIS )->elem_head_link );\
|
|
else \
|
|
elem_pool_free_elems( qtd_from_list_entry( ptHIS )->elem_head_link );\
|
|
elem_pool_unlock( qtd_pool, TRUE );\
|
|
}\
|
|
else if( em_type == INIT_LIST_FLAG_ITD )\
|
|
{\
|
|
elem_pool_lock( itd_pool, TRUE );\
|
|
if( single )\
|
|
elem_pool_free_elem( itd_from_list_entry( ptHIS )->elem_head_link );\
|
|
else \
|
|
elem_pool_free_elems( itd_from_list_entry( ptHIS )->elem_head_link );\
|
|
elem_pool_unlock( itd_pool, TRUE );\
|
|
}\
|
|
else if( em_type == INIT_LIST_FLAG_SITD )\
|
|
{\
|
|
elem_pool_lock( sitd_pool, TRUE );\
|
|
if( single )\
|
|
elem_pool_free_elem( sitd_from_list_entry( ptHIS )->elem_head_link );\
|
|
else \
|
|
elem_pool_free_elems( sitd_from_list_entry( ptHIS )->elem_head_link );\
|
|
elem_pool_unlock( sitd_pool, TRUE );\
|
|
}\
|
|
else if( em_type == INIT_LIST_FLAG_FSTN )\
|
|
{\
|
|
elem_pool_lock( fstn_pool, TRUE );\
|
|
if( single )\
|
|
elem_pool_free_elem( fstn_from_list_entry( ptHIS )->elem_head_link );\
|
|
else \
|
|
elem_pool_free_elems( fstn_from_list_entry( ptHIS )->elem_head_link );\
|
|
elem_pool_unlock( fstn_pool, TRUE );\
|
|
}\
|
|
else if( em_type == INIT_LIST_FLAG_QH )\
|
|
{\
|
|
elem_pool_lock( qh_pool, TRUE );\
|
|
if( single )\
|
|
elem_pool_free_elem( qh_from_list_entry( ptHIS )->elem_head_link );\
|
|
else \
|
|
elem_pool_free_elems( qh_from_list_entry( ptHIS )->elem_head_link );\
|
|
elem_pool_unlock( qh_pool, TRUE );\
|
|
}\
|
|
}\
|
|
}
|
|
|
|
#ifndef min
|
|
#define min( a, b ) ( ( a ) > ( b ) ? ( b ) : ( a ) )
|
|
#endif
|
|
#ifndef max
|
|
#define max( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) )
|
|
#endif
|
|
|
|
#define CLR_RH2_PORTSTAT( port_idx, x ) \
|
|
{\
|
|
PULONG addr; \
|
|
addr = ( PULONG )( ehci->port_base + port_idx ); \
|
|
status = EHCI_READ_PORT_ULONG( addr ); \
|
|
status = ( status & 0xfffffd5 ) & ~( x ); \
|
|
EHCI_WRITE_PORT_ULONG( addr, ( ULONG )status ); \
|
|
}
|
|
|
|
#define SET_RH2_PORTSTAT( port_idx, x ) \
|
|
{\
|
|
PULONG addr; \
|
|
addr = ( PULONG )( ehci->port_base + port_idx ); \
|
|
status = EHCI_READ_PORT_ULONG( addr ); \
|
|
if( x & PORT_PR ) \
|
|
status = ( status & 0xffffffd1 ) | ( x ); \
|
|
else \
|
|
status = ( status & 0xffffffd5 ) | ( x ); \
|
|
EHCI_WRITE_PORT_ULONG( addr, ( ULONG )status ); \
|
|
}
|
|
|
|
#define ehci_from_hcd( hCD ) ( struct_ptr( ( hCD ), EHCI_DEV, hcd_interf ) )
|
|
#define ehci_from_dev( dEV ) ( ehci_from_hcd( dEV->hcd ) )
|
|
|
|
#define ehci_copy_overlay( pQHC, pTDC ) \
|
|
{\
|
|
LONG td_size;\
|
|
PEHCI_QH pqh1;\
|
|
PEHCI_QTD ptd1;\
|
|
pqh1 = ( PEHCI_QH )( pQHC );\
|
|
ptd1 = ( PEHCI_QTD )( pTDC );\
|
|
td_size = get_elem_phys_part_size( INIT_LIST_FLAG_QTD );\
|
|
( pQHC )->cur_qtd_ptr = ptd1->phys_addr;\
|
|
RtlZeroMemory( &( pQHC )->cur_qtd, td_size );\
|
|
( pQHC )->cur_qtd.data_toggle = ( pTDC )->data_toggle;\
|
|
pqh1->hw_qtd_next = ptd1->phys_addr;\
|
|
pqh1->hw_alt_next = EHCI_PTR_TERM;\
|
|
}
|
|
|
|
//declarations
|
|
typedef struct
|
|
{
|
|
union
|
|
{
|
|
PUHCI_DEV uhci;
|
|
PEHCI_DEV ehci;
|
|
};
|
|
PVOID context;
|
|
ULONG ret;
|
|
|
|
} SYNC_PARAM, *PSYNC_PARAM;
|
|
|
|
PDEVICE_OBJECT
|
|
ehci_alloc(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, ULONG bus_addr, PUSB_DEV_MANAGER dev_mgr);
|
|
|
|
BOOLEAN ehci_init_schedule(PEHCI_DEV ehci, PADAPTER_OBJECT padapter);
|
|
|
|
BOOLEAN ehci_release(PDEVICE_OBJECT pdev, PUSB_DEV_MANAGER dev_mgr);
|
|
|
|
static VOID ehci_stop(PEHCI_DEV ehci);
|
|
|
|
BOOLEAN ehci_destroy_schedule(PEHCI_DEV ehci);
|
|
|
|
BOOLEAN NTAPI ehci_sync_insert_urb_schedule(PVOID context);
|
|
|
|
VOID ehci_init_hcd_interface(PEHCI_DEV ehci);
|
|
|
|
NTSTATUS ehci_rh_submit_urb(PUSB_DEV rh, PURB purb);
|
|
|
|
NTSTATUS ehci_dispatch_irp(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp);
|
|
|
|
VOID ehci_generic_urb_completion(PURB purb, PVOID context);
|
|
|
|
static NTSTATUS ehci_internal_submit_bulk(PEHCI_DEV ehci, PURB purb);
|
|
|
|
static NTSTATUS ehci_internal_submit_int(PEHCI_DEV ehci, PURB purb);
|
|
|
|
static NTSTATUS ehci_internal_submit_ctrl(PEHCI_DEV ehci, PURB purb);
|
|
|
|
static NTSTATUS ehci_internal_submit_iso(PEHCI_DEV ehci, PURB purb);
|
|
|
|
static ULONG ehci_scan_iso_error(PEHCI_DEV ehci, PURB purb);
|
|
|
|
BOOLEAN ehci_claim_bandwidth(PEHCI_DEV ehci, PURB purb, BOOLEAN claim_bw); //true to claim band-width, false to free band-width
|
|
|
|
static VOID ehci_insert_bulk_schedule(PEHCI_DEV ehci, PURB purb);
|
|
|
|
#define ehci_insert_control_schedule ehci_insert_bulk_schedule
|
|
|
|
static VOID ehci_insert_int_schedule(PEHCI_DEV ehci, PURB purb);
|
|
|
|
static VOID ehci_insert_iso_schedule(PEHCI_DEV ehci, PURB purb);
|
|
|
|
#define ehci_remove_control_from_schedule ehci_remove_bulk_from_schedule
|
|
|
|
PDEVICE_OBJECT ehci_probe(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, PUSB_DEV_MANAGER dev_mgr);
|
|
|
|
PDEVICE_OBJECT ehci_create_device(PDRIVER_OBJECT drvr_obj, PUSB_DEV_MANAGER dev_mgr);
|
|
|
|
BOOLEAN ehci_delete_device(PDEVICE_OBJECT pdev, PUSB_DEV_MANAGER dev_mgr);
|
|
|
|
VOID ehci_get_capabilities(PEHCI_DEV ehci, PBYTE base);
|
|
|
|
BOOLEAN NTAPI ehci_isr(PKINTERRUPT interrupt, PVOID context);
|
|
|
|
BOOLEAN ehci_start(PHCD hcd);
|
|
|
|
extern VOID rh_timer_svc_reset_port_completion(PUSB_DEV dev, PVOID context);
|
|
|
|
extern VOID rh_timer_svc_int_completion(PUSB_DEV dev, PVOID context);
|
|
|
|
extern USB_DEV_MANAGER g_dev_mgr;
|
|
|
|
#ifndef INCLUDE_EHCI
|
|
ULONG debug_level = DBGLVL_MAXIMUM;
|
|
PDRIVER_OBJECT usb_driver_obj = NULL;
|
|
|
|
//pending endpoint pool funcs
|
|
VOID
|
|
ehci_wait_ms(PEHCI_DEV ehci, LONG ms)
|
|
{
|
|
LARGE_INTEGER lms;
|
|
if (ms <= 0)
|
|
return;
|
|
|
|
lms.QuadPart = -10 * ms;
|
|
KeSetTimer(&ehci->reset_timer, lms, NULL);
|
|
|
|
KeWaitForSingleObject(&ehci->reset_timer, Executive, KernelMode, FALSE, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
init_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool)
|
|
{
|
|
int i;
|
|
if (pool == NULL)
|
|
return FALSE;
|
|
|
|
pool->pending_endp_array =
|
|
usb_alloc_mem(NonPagedPool, sizeof(UHCI_PENDING_ENDP) * UHCI_MAX_PENDING_ENDPS);
|
|
InitializeListHead(&pool->free_que);
|
|
pool->free_count = 0;
|
|
pool->total_count = UHCI_MAX_PENDING_ENDPS;
|
|
KeInitializeSpinLock(&pool->pool_lock);
|
|
|
|
for(i = 0; i < MAX_TIMER_SVCS; i++)
|
|
{
|
|
free_pending_endp(pool, &pool->pending_endp_array[i]);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
free_pending_endp(PUHCI_PENDING_ENDP_POOL pool, PUHCI_PENDING_ENDP pending_endp)
|
|
{
|
|
if (pool == NULL || pending_endp == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(pending_endp, sizeof(UHCI_PENDING_ENDP));
|
|
InsertTailList(&pool->free_que, (PLIST_ENTRY) & pending_endp->endp_link);
|
|
pool->free_count++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PUHCI_PENDING_ENDP
|
|
alloc_pending_endp(PUHCI_PENDING_ENDP_POOL pool, LONG count)
|
|
{
|
|
PUHCI_PENDING_ENDP new;
|
|
if (pool == NULL || count != 1)
|
|
return NULL;
|
|
|
|
if (pool->free_count <= 0)
|
|
return NULL;
|
|
|
|
new = (PUHCI_PENDING_ENDP) RemoveHeadList(&pool->free_que);
|
|
pool->free_count--;
|
|
return new;
|
|
}
|
|
|
|
BOOLEAN
|
|
destroy_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool)
|
|
{
|
|
if (pool == NULL)
|
|
return FALSE;
|
|
|
|
InitializeListHead(&pool->free_que);
|
|
pool->free_count = pool->total_count = 0;
|
|
usb_free_mem(pool->pending_endp_array);
|
|
pool->pending_endp_array = NULL;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
#else
|
|
#define ehci_wait_ms uhci_wait_ms
|
|
extern VOID uhci_wait_ms(PEHCI_DEV ehci, LONG ms);
|
|
|
|
extern BOOLEAN init_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool);
|
|
|
|
extern BOOLEAN free_pending_endp(PUHCI_PENDING_ENDP_POOL pool, PUHCI_PENDING_ENDP pending_endp);
|
|
|
|
extern PUHCI_PENDING_ENDP alloc_pending_endp(PUHCI_PENDING_ENDP_POOL pool, LONG count);
|
|
|
|
extern BOOLEAN destroy_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool);
|
|
|
|
#endif
|
|
|
|
//end of pending endpoint pool funcs
|
|
|
|
static VOID NTAPI
|
|
ehci_cancel_pending_endp_urb(IN PVOID Parameter)
|
|
{
|
|
PLIST_ENTRY abort_list;
|
|
PUSB_DEV pdev;
|
|
PURB purb;
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
abort_list = (PLIST_ENTRY) Parameter;
|
|
|
|
if (abort_list == NULL)
|
|
return;
|
|
|
|
while (IsListEmpty(abort_list) == FALSE)
|
|
{
|
|
//these devs are protected by purb's ref-count
|
|
purb = (PURB) RemoveHeadList(abort_list);
|
|
pdev = purb->pdev;
|
|
// purb->status is set when they are added to abort_list
|
|
|
|
ehci_generic_urb_completion(purb, purb->context);
|
|
|
|
lock_dev(pdev, FALSE);
|
|
pdev->ref_count--;
|
|
unlock_dev(pdev, FALSE);
|
|
}
|
|
usb_free_mem(abort_list);
|
|
return;
|
|
}
|
|
|
|
static BOOLEAN
|
|
ehci_process_pending_endp(PEHCI_DEV ehci)
|
|
{
|
|
PUSB_DEV pdev;
|
|
LIST_ENTRY temp_list, abort_list;
|
|
PLIST_ENTRY pthis;
|
|
PURB purb;
|
|
PUSB_ENDPOINT pendp;
|
|
NTSTATUS can_submit = STATUS_SUCCESS;
|
|
PWORK_QUEUE_ITEM pwork_item;
|
|
PLIST_ENTRY cancel_list;
|
|
PUSB_DEV pparent = NULL;
|
|
UCHAR port_idx = 0;
|
|
BOOLEAN tt_needed;
|
|
UCHAR hub_addr = 0;
|
|
USE_BASIC_IRQL;
|
|
|
|
if (ehci == NULL)
|
|
return FALSE;
|
|
|
|
InitializeListHead(&temp_list);
|
|
InitializeListHead(&abort_list);
|
|
|
|
purb = NULL;
|
|
ehci_dbg_print(DBGLVL_MEDIUM, ("ehci_process_pending_endp(): entering..., ehci=0x%x\n", ehci));
|
|
|
|
lock_pending_endp_list(&ehci->pending_endp_list_lock);
|
|
while (IsListEmpty(&ehci->pending_endp_list) == FALSE)
|
|
{
|
|
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_process_pending_endp(): pending_endp_list=0x%x\n",
|
|
&ehci->pending_endp_list));
|
|
|
|
tt_needed = FALSE;
|
|
pthis = RemoveHeadList(&ehci->pending_endp_list);
|
|
pendp = ((PUHCI_PENDING_ENDP) pthis)->pendp;
|
|
pdev = dev_from_endp(pendp);
|
|
lock_dev(pdev, TRUE);
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
|
|
//delegate to ehci_remove_device for remiving the purb queue on the endpoint
|
|
continue;
|
|
}
|
|
if ((pdev->flags & USB_DEV_FLAG_HIGH_SPEED) == 0)
|
|
{
|
|
// prepare split transaction
|
|
unlock_dev(pdev, TRUE);
|
|
|
|
// pparent won't be removed when pending_endp_list_lock is acquired.
|
|
get_parent_hs_hub(pdev, pparent, port_idx);
|
|
|
|
if (pparent == NULL)
|
|
{
|
|
TRAP();
|
|
ehci_dbg_print(DBGLVL_MEDIUM,
|
|
("ehci_process_pending_endp(): full/low speed device with no parent!!!\n"));
|
|
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
|
|
continue;
|
|
}
|
|
|
|
if (hub_lock_tt(pparent, port_idx, (UCHAR) endp_type(pendp)) == FALSE)
|
|
{
|
|
lock_dev(pdev, TRUE);
|
|
if (dev_state(pdev) != USB_DEV_STATE_ZOMB)
|
|
{
|
|
// reinsert the pending-endp to the list
|
|
InsertTailList(&temp_list, pthis);
|
|
unlock_dev(pdev, TRUE);
|
|
}
|
|
else
|
|
{
|
|
// delegate to ehci_remove_device for purb removal
|
|
unlock_dev(pdev, TRUE);
|
|
free_pending_endp(&ehci->pending_endp_pool,
|
|
struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// backup the hub address for future use
|
|
hub_addr = pparent->dev_addr;
|
|
|
|
lock_dev(pdev, TRUE);
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
|
|
hub_unlock_tt(pparent, port_idx, (UCHAR) endp_type(pendp));
|
|
continue;
|
|
}
|
|
tt_needed = TRUE;
|
|
// go on processing
|
|
}
|
|
|
|
if (endp_state(pendp) == USB_ENDP_FLAG_STALL)
|
|
{
|
|
while (IsListEmpty(&pendp->urb_list) == FALSE)
|
|
{
|
|
purb = (PURB) RemoveHeadList(&pendp->urb_list);
|
|
purb->status = USB_STATUS_ENDPOINT_HALTED;
|
|
InsertTailList(&abort_list, (LIST_ENTRY *) purb);
|
|
}
|
|
InitializeListHead(&pendp->urb_list);
|
|
unlock_dev(pdev, TRUE);
|
|
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
|
|
if (tt_needed)
|
|
hub_unlock_tt(pparent, port_idx, (UCHAR) endp_type(pendp));
|
|
continue;
|
|
}
|
|
|
|
if (IsListEmpty(&pendp->urb_list) == FALSE)
|
|
{
|
|
purb = (PURB) RemoveHeadList(&pendp->urb_list);
|
|
ASSERT(purb);
|
|
}
|
|
else
|
|
{
|
|
InitializeListHead(&pendp->urb_list);
|
|
unlock_dev(pdev, TRUE);
|
|
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
|
|
if (tt_needed)
|
|
hub_unlock_tt(pparent, port_idx, (UCHAR) endp_type(pendp));
|
|
continue;
|
|
}
|
|
|
|
if (tt_needed)
|
|
{
|
|
((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->hub_addr = hub_addr;
|
|
((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->port_idx = port_idx;
|
|
}
|
|
|
|
// if can_submit is STATUS_SUCCESS, the purb is inserted into the schedule
|
|
switch (endp_type(pendp))
|
|
{
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
{
|
|
can_submit = ehci_internal_submit_bulk(ehci, purb);
|
|
break;
|
|
}
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
{
|
|
can_submit = ehci_internal_submit_ctrl(ehci, purb);
|
|
break;
|
|
}
|
|
case USB_ENDPOINT_XFER_INT:
|
|
{
|
|
can_submit = ehci_internal_submit_int(ehci, purb);
|
|
break;
|
|
}
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
{
|
|
can_submit = ehci_internal_submit_iso(ehci, purb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (can_submit == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
//no enough bandwidth or tds
|
|
InsertHeadList(&pendp->urb_list, &purb->urb_link);
|
|
InsertTailList(&temp_list, pthis);
|
|
}
|
|
else
|
|
{
|
|
// otherwise error or success
|
|
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
|
|
|
|
if (can_submit != STATUS_SUCCESS)
|
|
{
|
|
//abort these URBs
|
|
InsertTailList(&abort_list, (LIST_ENTRY *) purb);
|
|
purb->status = can_submit;
|
|
}
|
|
}
|
|
unlock_dev(pdev, TRUE);
|
|
if (can_submit != STATUS_SUCCESS && tt_needed)
|
|
{
|
|
hub_unlock_tt(pparent, port_idx, (UCHAR) endp_type(pendp));
|
|
}
|
|
}
|
|
|
|
if (IsListEmpty(&temp_list) == FALSE)
|
|
{
|
|
//re-append them to the pending_endp_list
|
|
ListFirst(&temp_list, pthis);
|
|
RemoveEntryList(&temp_list);
|
|
MergeList(&ehci->pending_endp_list, pthis);
|
|
}
|
|
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
|
|
|
|
if (IsListEmpty(&abort_list) == FALSE)
|
|
{
|
|
PLIST_ENTRY pthis;
|
|
cancel_list = (PLIST_ENTRY) usb_alloc_mem(NonPagedPool, sizeof(WORK_QUEUE_ITEM) + sizeof(LIST_ENTRY));
|
|
ASSERT(cancel_list);
|
|
|
|
ListFirst(&abort_list, pthis);
|
|
RemoveEntryList(&abort_list);
|
|
InsertTailList(pthis, cancel_list);
|
|
|
|
pwork_item = (PWORK_QUEUE_ITEM) & cancel_list[1];
|
|
|
|
// we do not need to worry the ehci_cancel_pending_endp_urb running when the
|
|
// driver is unloading since purb-reference count will prevent the dev_mgr to
|
|
// quit till all the reference count to the dev drop to zero.
|
|
ExInitializeWorkItem(pwork_item, ehci_cancel_pending_endp_urb, (PVOID) cancel_list);
|
|
ExQueueWorkItem(pwork_item, DelayedWorkQueue);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
ehci_submit_urb(PEHCI_DEV ehci, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
|
|
{
|
|
int i;
|
|
PUHCI_PENDING_ENDP pending_endp;
|
|
NTSTATUS status;
|
|
USE_BASIC_IRQL;
|
|
|
|
if (ehci == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (pdev == NULL || pendp == NULL || purb == NULL)
|
|
{
|
|
// give a chance to those pending urb, especially for clearing hub tt
|
|
ehci_process_pending_endp(ehci);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
lock_pending_endp_list(&ehci->pending_endp_list_lock);
|
|
lock_dev(pdev, TRUE);
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
status = purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
if (dev_class(pdev) == USB_DEV_CLASS_ROOT_HUB)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
|
|
status = ehci_rh_submit_urb(pdev, purb);
|
|
return status;
|
|
}
|
|
|
|
if (pendp)
|
|
purb->pendp = pendp;
|
|
else
|
|
purb->pendp = &pdev->default_endp;
|
|
|
|
if (dev_from_endp(purb->pendp) != pdev)
|
|
{
|
|
status = purb->status = STATUS_INVALID_PARAMETER;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
if (endp_state(purb->pendp) == USB_ENDP_FLAG_STALL)
|
|
{
|
|
status = purb->status = USB_STATUS_ENDPOINT_HALTED;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
if ((pdev->flags & USB_DEV_FLAG_HIGH_SPEED) == 0)
|
|
{
|
|
// wait one ms
|
|
usb_wait_ms_dpc(1);
|
|
}
|
|
|
|
purb->pdev = pdev;
|
|
purb->rest_bytes = purb->data_length;
|
|
|
|
if (endp_type(purb->pendp) == USB_ENDPOINT_XFER_BULK)
|
|
purb->bytes_to_transfer = (purb->data_length > EHCI_MAX_SIZE_TRANSFER ? EHCI_MAX_SIZE_TRANSFER : purb->data_length); //multiple transfer for large data block
|
|
else
|
|
purb->bytes_to_transfer = purb->data_length;
|
|
|
|
ehci_dbg_print(DBGLVL_MEDIUM, ("ehci_submit_urb(): bytes_to_transfer=0x%x\n", purb->bytes_to_transfer));
|
|
|
|
purb->bytes_transfered = 0;
|
|
InitializeListHead(&purb->trasac_list);
|
|
purb->last_finished_td = &purb->trasac_list;
|
|
purb->flags &= ~(URB_FLAG_STATE_MASK | URB_FLAG_IN_SCHEDULE | URB_FLAG_FORCE_CANCEL);
|
|
purb->flags |= URB_FLAG_STATE_PENDING;
|
|
|
|
|
|
i = IsListEmpty(&pendp->urb_list);
|
|
InsertTailList(&pendp->urb_list, &purb->urb_link);
|
|
|
|
pdev->ref_count++; //for purb reference
|
|
|
|
if (i == FALSE)
|
|
{
|
|
//there is purb pending, simply queue it and return
|
|
status = purb->status = STATUS_PENDING;
|
|
goto LBL_OUT;
|
|
}
|
|
else if (usb_endp_busy_count(purb->pendp) && endp_type(purb->pendp) != USB_ENDPOINT_XFER_ISOC)
|
|
{
|
|
//
|
|
//No purb waiting but purb overlap not allowed,
|
|
//so leave it in queue and return, will be scheduled
|
|
//later
|
|
//
|
|
status = purb->status = STATUS_PENDING;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
pending_endp = alloc_pending_endp(&ehci->pending_endp_pool, 1);
|
|
if (pending_endp == NULL)
|
|
{
|
|
//panic
|
|
status = purb->status = STATUS_UNSUCCESSFUL;
|
|
goto LBL_OUT2;
|
|
}
|
|
|
|
pending_endp->pendp = purb->pendp;
|
|
InsertTailList(&ehci->pending_endp_list, &pending_endp->endp_link);
|
|
|
|
unlock_dev(pdev, TRUE);
|
|
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
|
|
|
|
ehci_process_pending_endp(ehci);
|
|
return STATUS_PENDING;
|
|
|
|
LBL_OUT2:
|
|
pdev->ref_count--;
|
|
RemoveEntryList(&purb->urb_link);
|
|
|
|
LBL_OUT:
|
|
unlock_dev(pdev, TRUE);
|
|
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
|
|
ehci_process_pending_endp(ehci);
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS
|
|
ehci_set_error_code(PURB purb, ULONG raw_status)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
|
|
//test if the purb is canceled
|
|
if (purb->flags & URB_FLAG_FORCE_CANCEL)
|
|
{
|
|
purb->status = STATUS_CANCELLED;
|
|
}
|
|
else if (raw_status == 0)
|
|
purb->status = STATUS_SUCCESS;
|
|
|
|
else if (pipe_content->trans_type == USB_ENDPOINT_XFER_INT ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL)
|
|
{
|
|
|
|
if (raw_status & QTD_STS_BABBLE)
|
|
purb->status = USB_STATUS_DATA_OVERRUN;
|
|
|
|
else if (raw_status & QTD_STS_HALT)
|
|
purb->status = USB_STATUS_ENDPOINT_HALTED;
|
|
|
|
else if (raw_status & QTD_STS_DBE)
|
|
purb->status = USB_STATUS_BUFFER_OVERRUN;
|
|
|
|
else if (raw_status & QTD_STS_XACT)
|
|
purb->status = USB_STATUS_CRC; // crc is included in xact err.
|
|
|
|
else if (raw_status & QTD_STS_MMF)
|
|
purb->status = USB_STATUS_BTSTUFF;
|
|
|
|
else
|
|
purb->status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else if (pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC)
|
|
{
|
|
if (pipe_content->speed_high)
|
|
{
|
|
if (raw_status & ITD_STS_BUFERR)
|
|
purb->status = USB_STATUS_BUFFER_OVERRUN;
|
|
|
|
else if (raw_status & ITD_STS_BABBLE)
|
|
purb->status = USB_STATUS_BABBLE_DETECTED;
|
|
|
|
else if (raw_status & ITD_STS_XACTERR) // Xact Err
|
|
purb->status = USB_STATUS_CRC;
|
|
|
|
else
|
|
purb->status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (raw_status & SITD_STS_ERR) // ERR is received from hub's tt
|
|
purb->status = USB_STATUS_ERROR;
|
|
|
|
else if (raw_status & SITD_STS_DBE)
|
|
purb->status = USB_STATUS_BUFFER_OVERRUN;
|
|
|
|
else if (raw_status & SITD_STS_BABBLE)
|
|
purb->status = USB_STATUS_BABBLE_DETECTED;
|
|
|
|
else if (raw_status & SITD_STS_XACTERR) // Xact Error
|
|
purb->status = USB_STATUS_CRC;
|
|
|
|
else if (raw_status & SITD_STS_MISSFRM) // missing microframe
|
|
purb->status = USB_STATUS_DATA_TOGGLE_MISMATCH;
|
|
|
|
else
|
|
purb->status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
if (purb->status != STATUS_SUCCESS)
|
|
{
|
|
hcd_dbg_print(DBGLVL_MEDIUM, ("ehci_set_error_code(): error status 0x%x\n", raw_status));
|
|
}
|
|
return purb->status;
|
|
}
|
|
|
|
static BOOLEAN NTAPI
|
|
ehci_sync_remove_urb_finished(PVOID context)
|
|
{
|
|
PEHCI_DEV ehci;
|
|
PLIST_ENTRY pthis, pnext, ptemp;
|
|
PURB purb;
|
|
PSYNC_PARAM pparam;
|
|
|
|
pparam = (PSYNC_PARAM) context;
|
|
ehci = pparam->ehci;
|
|
ptemp = (PLIST_ENTRY) pparam->context;
|
|
|
|
if (ehci == NULL)
|
|
{
|
|
return (UCHAR) (pparam->ret = FALSE);
|
|
}
|
|
|
|
ListFirst(&ehci->urb_list, pthis);
|
|
while (pthis)
|
|
{
|
|
//remove urbs not in the schedule
|
|
ListNext(&ehci->urb_list, pthis, pnext);
|
|
purb = (PURB) pthis;
|
|
|
|
if ((purb->flags & URB_FLAG_IN_SCHEDULE) == 0)
|
|
{
|
|
//finished or canceled( not applied for split bulk ).
|
|
RemoveEntryList(pthis);
|
|
InsertTailList(ptemp, pthis);
|
|
}
|
|
pthis = pnext;
|
|
}
|
|
pparam->ret = TRUE;
|
|
return (UCHAR) TRUE;
|
|
}
|
|
|
|
VOID NTAPI
|
|
ehci_dpc_callback(PKDPC dpc, PVOID context, PVOID sysarg1, PVOID sysarg2)
|
|
{
|
|
PEHCI_DEV ehci;
|
|
|
|
LIST_HEAD temp_list;
|
|
PLIST_ENTRY pthis, pnext;
|
|
PURB purb;
|
|
PEHCI_QH pqh;
|
|
PEHCI_QTD ptd;
|
|
PUHCI_PENDING_ENDP pending_endp;
|
|
PUSB_DEV pdev;
|
|
PUSB_ENDPOINT pendp;
|
|
|
|
BOOLEAN finished;
|
|
LONG i;
|
|
ULONG ehci_status, urb_status;
|
|
|
|
SYNC_PARAM sync_param;
|
|
UCHAR ep_type;
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
ehci = (PEHCI_DEV) context;
|
|
if (ehci == NULL)
|
|
return;
|
|
|
|
ehci_status = (ULONG) sysarg1;
|
|
|
|
InitializeListHead(&temp_list);
|
|
|
|
sync_param.ehci = ehci;
|
|
sync_param.context = (PVOID) & temp_list;
|
|
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_dpc_callback(): entering..., ehci=0x%x\n", ehci));
|
|
//remove finished purb from ehci's purb-list
|
|
KeSynchronizeExecution(ehci->pdev_ext->ehci_int, ehci_sync_remove_urb_finished, &sync_param);
|
|
|
|
//release resources( itds, sitds, fstns, tds, and qhs ) allocated for the purb
|
|
while (IsListEmpty(&temp_list) == FALSE)
|
|
{
|
|
//not in any public queue, if do not access into dev, no race
|
|
//condition will occur
|
|
purb = (PURB) RemoveHeadList(&temp_list);
|
|
urb_status = purb->status;
|
|
ep_type = endp_type(purb->pendp);
|
|
|
|
if (ep_type == USB_ENDPOINT_XFER_ISOC)
|
|
{
|
|
// collect error for iso transfer
|
|
urb_status = ehci_scan_iso_error(ehci, purb);
|
|
}
|
|
|
|
//the only place we do not use this lock on non-pending-endp-list data
|
|
KeAcquireSpinLockAtDpcLevel(&ehci->pending_endp_list_lock);
|
|
while (IsListEmpty(&purb->trasac_list) == FALSE)
|
|
{
|
|
UCHAR em_type;
|
|
pthis = RemoveHeadList(&purb->trasac_list);
|
|
em_type = (UCHAR) elem_type(pthis, TRUE);
|
|
|
|
if (em_type == INIT_LIST_FLAG_QH)
|
|
{
|
|
pqh = qh_from_list_entry(pthis);
|
|
elem_safe_free(pthis, TRUE);
|
|
}
|
|
else
|
|
{
|
|
//must be an itd, sitd chain
|
|
InsertHeadList(&purb->trasac_list, pthis);
|
|
for(i = 0, purb->bytes_transfered = 0; i < purb->td_count; i++)
|
|
{
|
|
PEHCI_QTD_CONTENT ptdc = NULL;
|
|
PEHCI_ITD_CONTENT pitdc;
|
|
PEHCI_SITD_CONTENT psitdc;
|
|
|
|
em_type = (UCHAR) elem_type(pthis, TRUE);
|
|
|
|
// accumulate data transfered in tds
|
|
if (em_type == INIT_LIST_FLAG_QTD)
|
|
{
|
|
ptd = qtd_from_list_entry(pthis);
|
|
ptdc = (PEHCI_QTD_CONTENT) ptd;
|
|
if ((ptdc->status & QTD_STS_ACTIVE) == 0 && ((ptdc->status & QTD_ANY_ERROR) == 0))
|
|
purb->bytes_transfered += ptd->bytes_to_transfer;
|
|
}
|
|
else if (em_type == INIT_LIST_FLAG_ITD)
|
|
{
|
|
int j;
|
|
pitdc = (PEHCI_ITD_CONTENT) itd_from_list_entry(pthis);
|
|
for(j = 0; j < 8; j++)
|
|
{
|
|
if ((pitdc->status_slot[j].status & ITD_STS_ACTIVE) == 0
|
|
&& (pitdc->status_slot[j].status & ITD_ANY_ERROR) == 0)
|
|
purb->bytes_transfered += ptdc->bytes_to_transfer;
|
|
}
|
|
}
|
|
else if (em_type == INIT_LIST_FLAG_SITD)
|
|
{
|
|
psitdc = (PEHCI_SITD_CONTENT) sitd_from_list_entry(pthis);
|
|
if ((psitdc->status & SITD_STS_ACTIVE) == 0 && (psitdc->status & SITD_ANY_ERROR) == 0)
|
|
purb->bytes_transfered += ptdc->bytes_to_transfer;
|
|
}
|
|
ListNext(&purb->trasac_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
|
|
// check to see if an fstn is there
|
|
ListFirstPrev(&purb->trasac_list, pthis);
|
|
if (elem_type(pthis, TRUE) == INIT_LIST_FLAG_FSTN)
|
|
{
|
|
RemoveEntryList(pthis);
|
|
elem_safe_free(pthis, TRUE);
|
|
}
|
|
|
|
ListFirst(&purb->trasac_list, pthis);
|
|
RemoveEntryList(&purb->trasac_list);
|
|
|
|
// free the tds
|
|
elem_safe_free(pthis, FALSE);
|
|
|
|
//termination condition
|
|
InitializeListHead(&purb->trasac_list);
|
|
purb->last_finished_td = NULL;
|
|
}
|
|
}
|
|
|
|
if (ep_type == USB_ENDPOINT_XFER_ISOC || ep_type == USB_ENDPOINT_XFER_INT)
|
|
ehci_claim_bandwidth(ehci, purb, FALSE); //release band-width
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&ehci->pending_endp_list_lock);
|
|
|
|
ehci_set_error_code(purb, urb_status);
|
|
|
|
pdev = dev_from_endp(purb->pendp);
|
|
pendp = purb->pendp;
|
|
|
|
// perform clear tt buffer if error on full/low bulk/control pipe
|
|
if (ep_type == USB_ENDPOINT_XFER_BULK || ep_type == USB_ENDPOINT_XFER_CONTROL)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PUSB_DEV phub;
|
|
UCHAR port_idx;
|
|
|
|
get_parent_hs_hub(pdev, phub, port_idx);
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
|
|
if (pipe_content->speed_high == 0 && purb->status != STATUS_SUCCESS)
|
|
{
|
|
// lets schedule an event to clear the tt buffer
|
|
hub_post_clear_tt_event(phub, port_idx, purb->pipe);
|
|
}
|
|
else if (pipe_content->speed_high == 0)
|
|
{
|
|
if (phub == NULL)
|
|
TRAP();
|
|
else
|
|
{
|
|
// release tt if no error
|
|
hub_unlock_tt(phub, (UCHAR) port_idx, (UCHAR) pipe_content->trans_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
finished = TRUE;
|
|
|
|
//since the ref_count for the purb is not released, we can safely have one
|
|
//pointer to dev
|
|
|
|
if (purb->status == USB_STATUS_BABBLE_DETECTED)
|
|
{
|
|
usb_dbg_print(DBGLVL_MEDIUM,
|
|
("ehci_dpc_callback(): alert!!!, babble detected, severe error, reset the whole bus\n"));
|
|
// ehci_start( ehci );
|
|
}
|
|
|
|
if (ehci_status & STS_HALT) //&& !ehci->is_suspended
|
|
{
|
|
ehci_start(&ehci->hcd_interf);
|
|
}
|
|
|
|
//this will let the new request in ehci_generic_urb_completion to this endp
|
|
//be processed rather than queued in the pending_endp_list
|
|
lock_dev(pdev, TRUE);
|
|
usb_endp_busy_count_dec(pendp);
|
|
unlock_dev(pdev, TRUE);
|
|
|
|
if (usb_success(purb->status) == FALSE)
|
|
{
|
|
// set error code and complete the purb and purb is invalid from this point
|
|
ehci_generic_urb_completion(purb, purb->context);
|
|
}
|
|
else
|
|
{
|
|
if (ep_type == USB_ENDPOINT_XFER_BULK)
|
|
{
|
|
purb->rest_bytes -= purb->bytes_transfered;
|
|
if (purb->rest_bytes)
|
|
{
|
|
finished = FALSE;
|
|
}
|
|
else
|
|
{
|
|
ehci_generic_urb_completion(purb, purb->context);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ehci_generic_urb_completion(purb, purb->context);
|
|
// DbgBreakPoint();
|
|
//purb is now invalid
|
|
}
|
|
}
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&ehci->pending_endp_list_lock);
|
|
lock_dev(pdev, TRUE);
|
|
|
|
if (finished)
|
|
pdev->ref_count--;
|
|
|
|
if (urb_status && ((ep_type == USB_ENDPOINT_XFER_BULK) || (ep_type == USB_ENDPOINT_XFER_INT)))
|
|
{
|
|
// error on int or bulk pipe, cleared in usb_reset_pipe_completion
|
|
pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK;
|
|
pendp->flags |= USB_ENDP_FLAG_STALL;
|
|
}
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
KeReleaseSpinLockFromDpcLevel(&ehci->pending_endp_list_lock);
|
|
if (finished == FALSE)
|
|
{
|
|
|
|
purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
ehci_generic_urb_completion(purb, purb->context);
|
|
|
|
lock_dev(pdev, TRUE);
|
|
pdev->ref_count--;
|
|
unlock_dev(pdev, TRUE);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (finished && IsListEmpty(&pendp->urb_list) == TRUE)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
KeReleaseSpinLockFromDpcLevel(&ehci->pending_endp_list_lock);
|
|
continue;
|
|
}
|
|
else if (finished == TRUE)
|
|
{
|
|
//has purb in the endp's purb-list
|
|
if (usb_endp_busy_count(pendp) > 0)
|
|
{
|
|
//the urbs still have chance to be sheduled but not this time
|
|
unlock_dev(pdev, TRUE);
|
|
KeReleaseSpinLockFromDpcLevel(&ehci->pending_endp_list_lock);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (finished == FALSE)
|
|
{
|
|
//a split bulk transfer, ( not the high speed split transfer )
|
|
purb->bytes_transfered = 0;
|
|
purb->bytes_to_transfer =
|
|
EHCI_MAX_SIZE_TRANSFER > purb->rest_bytes ? purb->rest_bytes : EHCI_MAX_SIZE_TRANSFER;
|
|
|
|
//the purb is not finished
|
|
purb->flags &= ~URB_FLAG_STATE_MASK;
|
|
purb->flags |= URB_FLAG_STATE_PENDING;
|
|
|
|
InsertHeadList(&pendp->urb_list, &purb->urb_link);
|
|
}
|
|
|
|
pending_endp = alloc_pending_endp(&ehci->pending_endp_pool, 1);
|
|
if (!pending_endp)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
KeReleaseSpinLockFromDpcLevel(&ehci->pending_endp_list_lock);
|
|
return;
|
|
}
|
|
pending_endp->pendp = pendp;
|
|
InsertTailList(&ehci->pending_endp_list, &pending_endp->endp_link);
|
|
|
|
unlock_dev(pdev, TRUE);
|
|
KeReleaseSpinLockFromDpcLevel(&ehci->pending_endp_list_lock);
|
|
}
|
|
|
|
//ah...exhausted, let's find some in the pending_endp_list to rock
|
|
ehci_process_pending_endp(ehci);
|
|
return;
|
|
}
|
|
|
|
static BOOLEAN NTAPI
|
|
ehci_sync_cancel_urbs_dev(PVOID context)
|
|
{
|
|
//cancel all the urbs on one dev
|
|
PEHCI_DEV ehci;
|
|
PUSB_DEV pdev, dest_dev;
|
|
PSYNC_PARAM sync_param;
|
|
PLIST_ENTRY pthis, pnext;
|
|
LONG count;
|
|
|
|
sync_param = (PSYNC_PARAM) context;
|
|
dest_dev = (PUSB_DEV) sync_param->context;
|
|
ehci = sync_param->ehci;
|
|
|
|
if (ehci == NULL || dest_dev == NULL)
|
|
{
|
|
return (UCHAR) (sync_param->ret = FALSE);
|
|
}
|
|
count = 0;
|
|
ListFirst(&ehci->urb_list, pthis);
|
|
while (pthis)
|
|
{
|
|
pdev = dev_from_endp(((PURB) pthis)->pendp);
|
|
if (pdev == dest_dev)
|
|
{
|
|
((PURB) pthis)->flags |= URB_FLAG_FORCE_CANCEL;
|
|
}
|
|
ListNext(&ehci->urb_list, pthis, pnext);
|
|
pthis = pnext;
|
|
count++;
|
|
}
|
|
|
|
if (count)
|
|
{
|
|
// signal an int for further process
|
|
press_doorbell(ehci);
|
|
}
|
|
return (UCHAR) (sync_param->ret = TRUE);
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_remove_device(PEHCI_DEV ehci, PUSB_DEV dev)
|
|
{
|
|
PUHCI_PENDING_ENDP ppending_endp;
|
|
PLIST_ENTRY pthis, pnext;
|
|
PURB purb;
|
|
LIST_HEAD temp_list;
|
|
int i, j, k;
|
|
SYNC_PARAM sync_param;
|
|
|
|
USE_BASIC_IRQL;
|
|
|
|
if (ehci == NULL || dev == NULL)
|
|
return FALSE;
|
|
|
|
InitializeListHead(&temp_list);
|
|
|
|
//free pending endp that has purb queued from pending endp list
|
|
lock_pending_endp_list(&ehci->pending_endp_list_lock);
|
|
|
|
ListFirst(&ehci->pending_endp_list, pthis);
|
|
|
|
while (pthis)
|
|
{
|
|
ppending_endp = (PUHCI_PENDING_ENDP) pthis;
|
|
ListNext(&ehci->pending_endp_list, pthis, pnext);
|
|
if (dev_from_endp(ppending_endp->pendp) == dev)
|
|
{
|
|
RemoveEntryList(pthis);
|
|
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
|
|
}
|
|
pthis = pnext;
|
|
}
|
|
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
|
|
|
|
//cancel all the urbs in the purb-list
|
|
sync_param.ehci = ehci;
|
|
sync_param.context = (PVOID) dev;
|
|
|
|
KeSynchronizeExecution(ehci->pdev_ext->ehci_int, ehci_sync_cancel_urbs_dev, &sync_param);
|
|
|
|
//cancel all the purb in the endp's purb-list
|
|
k = 0;
|
|
lock_dev(dev, FALSE);
|
|
if (dev->usb_config)
|
|
{
|
|
//only for configed dev
|
|
for(i = 0; i < dev->usb_config->if_count; i++)
|
|
{
|
|
for(j = 0; j < dev->usb_config->interf[i].endp_count; j++)
|
|
{
|
|
ListFirst(&dev->usb_config->interf[i].endp[j].urb_list, pthis);
|
|
while (pthis)
|
|
{
|
|
ListNext(&dev->usb_config->interf[i].endp[j].urb_list, pthis, pnext);
|
|
|
|
RemoveEntryList(pthis);
|
|
InsertHeadList(&temp_list, pthis);
|
|
pthis = pnext;
|
|
k++;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
ListFirst(&dev->default_endp.urb_list, pthis);
|
|
|
|
while (pthis)
|
|
{
|
|
ListNext(&dev->default_endp.urb_list, pthis, pnext);
|
|
|
|
RemoveEntryList(pthis);
|
|
InsertHeadList(&temp_list, pthis);
|
|
pthis = pnext;
|
|
k++;
|
|
}
|
|
unlock_dev(dev, FALSE);
|
|
|
|
if (IsListEmpty(&temp_list) == FALSE)
|
|
{
|
|
for(i = 0; i < k; i++)
|
|
{
|
|
//complete those urbs with error
|
|
pthis = RemoveHeadList(&temp_list);
|
|
purb = (PURB) pthis;
|
|
purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
{
|
|
ehci_generic_urb_completion(purb, purb->context);
|
|
}
|
|
}
|
|
}
|
|
|
|
lock_dev(dev, FALSE) dev->ref_count -= k;
|
|
unlock_dev(dev, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOLEAN
|
|
ehci_insert_urb_schedule(PEHCI_DEV ehci, PURB purb)
|
|
// must have dev_lock( ehci_process_pending_endp ) and frame_list_lock acquired
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return FALSE;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
switch (pipe_content->trans_type)
|
|
{
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
ehci_insert_control_schedule(ehci, purb);
|
|
break;
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
ehci_insert_bulk_schedule(ehci, purb);
|
|
break;
|
|
case USB_ENDPOINT_XFER_INT:
|
|
ehci_insert_int_schedule(ehci, purb);
|
|
break;
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
ehci_insert_iso_schedule(ehci, purb);
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
purb->flags &= ~URB_FLAG_STATE_MASK;
|
|
purb->flags |= URB_FLAG_STATE_IN_PROCESS | URB_FLAG_IN_SCHEDULE;
|
|
InsertTailList(&ehci->urb_list, &purb->urb_link);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOLEAN
|
|
ehci_insert_tds_qh(PEHCI_DEV ehci, PEHCI_QH pqh, PEHCI_QTD td_chain)
|
|
{
|
|
if (pqh == NULL || td_chain == NULL)
|
|
return FALSE;
|
|
|
|
UNREFERENCED_PARAMETER(ehci);
|
|
|
|
ehci_copy_overlay((PEHCI_QH_CONTENT) pqh, (PEHCI_QTD_CONTENT) td_chain);
|
|
InsertTailList(&td_chain->elem_head_link->elem_link, &pqh->elem_head_link->elem_link);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOLEAN
|
|
ehci_insert_qh_urb(PURB purb, PEHCI_QH pqh)
|
|
{
|
|
PLIST_ENTRY pthis, pnext;
|
|
if (pqh == NULL || purb == NULL)
|
|
return FALSE;
|
|
|
|
InsertTailList(&pqh->elem_head_link->elem_link, &purb->trasac_list);
|
|
ListFirst(&purb->trasac_list, pthis) while (pthis)
|
|
{
|
|
// note: fstn may in this chain
|
|
struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link)->purb = purb;
|
|
ListNext(&purb->trasac_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#define calc_td_count( pURB, start_aDDR, td_coUNT ) \
|
|
{\
|
|
LONG i, j, k;\
|
|
td_coUNT = 0;\
|
|
k = ( ( pURB )->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size;\
|
|
if( k != 0 )\
|
|
{\
|
|
LONG packets_per_td, packets_per_page;\
|
|
packets_per_td = EHCI_QTD_MAX_TRANS_SIZE / max_packet_size;\
|
|
packets_per_page = PAGE_SIZE / max_packet_size;\
|
|
i = ( ( LONG )&( pURB )->data_buffer[ ( start_aDDR ) ] ) & ( PAGE_SIZE - 1 );\
|
|
if( i )\
|
|
{\
|
|
i = PAGE_SIZE - i;\
|
|
j = i & ( max_packet_size - 1 );\
|
|
k -= ( EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j ) / max_packet_size;\
|
|
if( k < 0 )\
|
|
td_coUNT = 1;\
|
|
else\
|
|
{\
|
|
if( j )\
|
|
i = packets_per_td - packets_per_page;\
|
|
else\
|
|
i = packets_per_td;\
|
|
td_coUNT = 1 + ( k + i - 1 ) / i; \
|
|
}\
|
|
}\
|
|
else\
|
|
{\
|
|
td_coUNT = ( k + packets_per_td - 1 ) / packets_per_td;\
|
|
}\
|
|
}\
|
|
}
|
|
|
|
static BOOLEAN
|
|
ehci_fill_td_buf_ptr(PURB purb, LONG start_addr, // start idx into purb->data_buffer
|
|
PLIST_ENTRY td_list, LONG td_count, ULONG toggle)
|
|
// fill the tds' bytes_to_transfer and hw_buf, return next toggle value: true 1, false 0
|
|
{
|
|
LONG i, j, k, data_load;
|
|
LONG packets_per_td, packets_per_page, bytes_to_transfer, max_packet_size;
|
|
PLIST_ENTRY pthis, pnext;
|
|
PEHCI_QTD_CONTENT ptdc;
|
|
PEHCI_QTD ptd;
|
|
PVOID ptr;
|
|
|
|
if (purb == NULL || td_list == NULL || td_count == 0)
|
|
return toggle;
|
|
|
|
max_packet_size = 1 << ((PURB_HS_PIPE_CONTENT) & purb->pipe)->max_packet_size;
|
|
packets_per_td = EHCI_QTD_MAX_TRANS_SIZE / max_packet_size;
|
|
packets_per_page = PAGE_SIZE / max_packet_size;
|
|
|
|
pthis = td_list;
|
|
bytes_to_transfer = purb->bytes_to_transfer;
|
|
|
|
i = ((LONG) & (purb)->data_buffer[(start_addr)]) & (PAGE_SIZE - 1);
|
|
if (i)
|
|
{
|
|
i = PAGE_SIZE - i;
|
|
j = i & (max_packet_size - 1);
|
|
}
|
|
else
|
|
{
|
|
i = j = 0;
|
|
}
|
|
|
|
while (bytes_to_transfer)
|
|
{
|
|
ptd = qtd_from_list_entry(pthis);
|
|
ptd->hw_buf[0] = MmGetPhysicalAddress(&purb->data_buffer[start_addr]).LowPart;
|
|
ptdc = (PEHCI_QTD_CONTENT) ptd;
|
|
|
|
if (i != 0)
|
|
{
|
|
data_load = (LONG) (EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j) < bytes_to_transfer
|
|
? (LONG) (EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j) : bytes_to_transfer;
|
|
|
|
ptdc->bytes_to_transfer = (USHORT) data_load;
|
|
ptd->bytes_to_transfer = (USHORT) data_load;
|
|
|
|
// subtract the header part
|
|
data_load -= (i < data_load ? i : data_load);
|
|
|
|
for(k = 1; data_load > 0; k++)
|
|
{
|
|
ptr = &purb->data_buffer[start_addr + i + (k - 1) * PAGE_SIZE];
|
|
ptr = (PVOID) (((ULONG) ptr) & ~(PAGE_SIZE - 1));
|
|
ptd->hw_buf[k] = MmGetPhysicalAddress(ptr).LowPart;
|
|
data_load -= PAGE_SIZE < data_load ? PAGE_SIZE : data_load;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// aligned on page boundary
|
|
data_load = EHCI_QTD_MAX_TRANS_SIZE < bytes_to_transfer
|
|
? EHCI_QTD_MAX_TRANS_SIZE : bytes_to_transfer;
|
|
|
|
ptdc->bytes_to_transfer = (USHORT) data_load;
|
|
ptd->bytes_to_transfer = (USHORT) data_load;
|
|
|
|
data_load -= (PAGE_SIZE < data_load ? PAGE_SIZE : data_load);
|
|
|
|
for(k = 1; data_load > 0; k++)
|
|
{
|
|
ptr = &purb->data_buffer[start_addr + k * PAGE_SIZE];
|
|
ptr = (PVOID) (((ULONG) ptr) & ~(PAGE_SIZE - 1));
|
|
ptd->hw_buf[k] = MmGetPhysicalAddress(ptr).LowPart;
|
|
data_load -= PAGE_SIZE < data_load ? PAGE_SIZE : data_load;
|
|
}
|
|
}
|
|
ptdc->data_toggle = toggle;
|
|
if (((ptdc->bytes_to_transfer + max_packet_size - 1) / max_packet_size) & 1)
|
|
{
|
|
//only odd num of transactions has effect
|
|
toggle ^= 1;
|
|
}
|
|
start_addr += ptdc->bytes_to_transfer;
|
|
bytes_to_transfer -= ptdc->bytes_to_transfer;
|
|
ListNext(td_list, pthis, pnext);
|
|
pthis = pnext;
|
|
i = j;
|
|
}
|
|
return toggle;
|
|
}
|
|
|
|
static NTSTATUS
|
|
ehci_internal_submit_bulk(PEHCI_DEV ehci, PURB purb)
|
|
//
|
|
// assume that the purb has its rest_bytes and bytes_to_transfer set
|
|
// and bytes_transfered is zeroed.
|
|
// dev_lock must be acquired outside
|
|
// purb comes from dev's endpoint purb-list. it is already removed from
|
|
// the endpoint purb-list.
|
|
//
|
|
{
|
|
|
|
LONG max_packet_size, td_count, offset, bytes_to_transfer;
|
|
PBYTE start_addr;
|
|
PEHCI_QTD ptd;
|
|
PEHCI_QH pqh;
|
|
LIST_ENTRY td_list, *pthis, *pnext;
|
|
BOOLEAN old_toggle, toggle, ret;
|
|
UCHAR pid;
|
|
LONG i, j;
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PEHCI_QTD_CONTENT ptdc;
|
|
PEHCI_QH_CONTENT pqhc;
|
|
PEHCI_ELEM_LINKS pelnk;
|
|
PEHCI_ELEM_LINKS plnk;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
max_packet_size = endp_max_packet_size(purb->pendp);
|
|
if (purb->bytes_to_transfer == 0)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
start_addr = &purb->data_buffer[purb->data_length - purb->rest_bytes];
|
|
calc_td_count(purb, purb->data_length - purb->rest_bytes, td_count);
|
|
|
|
elem_pool_lock(qtd_pool, TRUE);
|
|
pelnk = elem_pool_alloc_elems(qtd_pool, td_count);
|
|
elem_pool_unlock(qtd_pool, TRUE);
|
|
|
|
if (pelnk == NULL)
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
ptd = (PEHCI_QTD) ((ULONG) pelnk->phys_part & PHYS_PART_ADDR_MASK);
|
|
|
|
InitializeListHead(&td_list);
|
|
InsertTailList(&ptd->elem_head_link->elem_link, &td_list);
|
|
|
|
ListFirst(&td_list, pthis);
|
|
ListNext(&td_list, pthis, pnext);
|
|
|
|
offset = 0;
|
|
|
|
old_toggle = toggle = (purb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE) ? TRUE : FALSE;
|
|
bytes_to_transfer = purb->bytes_to_transfer;
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_internal_submit_bulk():dev toggle=%d\n", toggle));
|
|
|
|
for(i = 1; i < 16; i++)
|
|
{
|
|
if ((max_packet_size >> i) == 0)
|
|
break;
|
|
}
|
|
i--;
|
|
i &= 0xf;
|
|
|
|
purb->pipe = 0;
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
pipe_content->max_packet_size = i;
|
|
pipe_content->endp_addr = endp_num(purb->pendp);
|
|
pipe_content->dev_addr = dev_from_endp(purb->pendp)->dev_addr;
|
|
pipe_content->trans_dir = endp_dir(purb->pendp);
|
|
pipe_content->trans_type = USB_ENDPOINT_XFER_BULK;
|
|
pipe_content->data_toggle = toggle;
|
|
pipe_content->speed_high = (dev_from_endp(purb->pendp)->flags & USB_DEV_FLAG_HIGH_SPEED) ? 1 : 0;
|
|
pipe_content->speed_low = (dev_from_endp(purb->pendp)->flags & USB_DEV_FLAG_LOW_SPEED) ? 1 : 0;
|
|
|
|
pid = (((ULONG) purb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN) ? QTD_PID_IN : QTD_PID_OUT);
|
|
|
|
i = ((ULONG) start_addr) & (PAGE_SIZE - 1); // header part within first page
|
|
if (i)
|
|
{
|
|
i = PAGE_SIZE - i;
|
|
if (i < purb->bytes_to_transfer)
|
|
j = i & (max_packet_size - 1);
|
|
else
|
|
j = 0;
|
|
}
|
|
else
|
|
j = 0;
|
|
|
|
// fill the page pointer and toggle
|
|
|
|
toggle = ehci_fill_td_buf_ptr(purb, purb->data_length - purb->rest_bytes, pthis, td_count, toggle);
|
|
while (pthis)
|
|
{
|
|
ptd = qtd_from_list_entry(pthis);
|
|
ptdc = (PEHCI_QTD_CONTENT) ptd;
|
|
|
|
// ptdc->alt_terminal = 1;
|
|
// ptdc->alt_qtd = 0;
|
|
ptd->hw_alt_next = EHCI_PTR_TERM;
|
|
ptdc->pid = pid;
|
|
|
|
// ptd->elem_head_link->purb = purb; will be filled later
|
|
ptdc->err_count = 3;
|
|
ptdc->status = 0x80; // active, and do_start_split for split transfer
|
|
ptdc->cur_page = 0;
|
|
// ptdc->data_toggle = toggle;
|
|
|
|
if (pnext)
|
|
{
|
|
ptd->hw_next = qtd_from_list_entry(pnext)->phys_addr;
|
|
}
|
|
else
|
|
{
|
|
//Last one, enable ioc and short packet detect if necessary
|
|
ptd->hw_next = EHCI_PTR_TERM;
|
|
ptdc->ioc = TRUE;
|
|
if (bytes_to_transfer < max_packet_size && (pid == QTD_PID_IN))
|
|
{
|
|
//ptd->status |= TD_CTRL_SPD;
|
|
}
|
|
}
|
|
|
|
pthis = pnext;
|
|
|
|
if (pthis)
|
|
ListNext(&td_list, pthis, pnext);
|
|
}
|
|
|
|
ListFirst(&td_list, pthis);
|
|
RemoveEntryList(&td_list);
|
|
|
|
elem_pool_lock(qh_pool, TRUE);
|
|
|
|
plnk = elem_pool_alloc_elem(qh_pool);
|
|
if (plnk == NULL)
|
|
{
|
|
// free the qtds
|
|
elem_safe_free(pthis, TRUE);
|
|
if (qh_pool) elem_pool_unlock(qh_pool, TRUE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
pqh = (PEHCI_QH) ((ULONG) plnk->phys_part & PHYS_PART_ADDR_MASK);
|
|
elem_pool_unlock(qh_pool, TRUE);
|
|
|
|
if (pqh == NULL)
|
|
{
|
|
// free the qtds
|
|
elem_safe_free(pthis, TRUE);
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
|
|
}
|
|
|
|
purb->td_count = td_count;
|
|
pqhc = (PEHCI_QH_CONTENT) pqh;
|
|
pqh->hw_next = EHCI_PTR_TERM; // filled later
|
|
pqhc->dev_addr = pipe_content->dev_addr;
|
|
pqhc->inactive = 0;
|
|
pqhc->endp_addr = pipe_content->endp_addr;
|
|
pqhc->data_toggle = 0; //pipe_content->data_toggle;
|
|
pqhc->is_async_head = 0;
|
|
pqhc->max_packet_size = (1 << pipe_content->max_packet_size);
|
|
pqhc->is_ctrl_endp = 0;
|
|
pqhc->reload_counter = EHCI_NAK_RL_COUNT;
|
|
|
|
if (pipe_content->speed_high)
|
|
pqhc->endp_spd = USB_SPEED_HIGH;
|
|
else if (pipe_content->speed_low)
|
|
pqhc->endp_spd = USB_SPEED_LOW;
|
|
else
|
|
pqhc->endp_spd = USB_SPEED_FULL;
|
|
|
|
pqh->hw_info2 = 0;
|
|
pqhc->mult = 1;
|
|
pqh->hw_current = 0;
|
|
pqh->hw_qtd_next = 0; // filled later
|
|
pqh->hw_alt_next = EHCI_PTR_TERM;
|
|
pqh->hw_token = 0; //indicate to advance queue before execution
|
|
|
|
if (!pipe_content->speed_high)
|
|
{
|
|
pqhc->hub_addr = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->hub_addr;
|
|
pqhc->port_idx = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->port_idx;
|
|
}
|
|
|
|
ptd = qtd_from_list_entry(pthis);
|
|
ehci_insert_tds_qh(ehci, pqh, ptd);
|
|
ehci_insert_qh_urb(purb, pqh);
|
|
purb->pendp->flags =
|
|
(purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE) | (toggle ? USB_ENDP_FLAG_DATATOGGLE : 0);
|
|
usb_endp_busy_count_inc(purb->pendp);
|
|
ehci_insert_urb_to_schedule(ehci, purb, ret);
|
|
|
|
if (ret == FALSE)
|
|
{
|
|
// undo all we have done
|
|
ListFirst(&pqh->elem_head_link->elem_link, pthis);
|
|
|
|
RemoveEntryList(&purb->trasac_list);
|
|
RemoveEntryList(&pqh->elem_head_link->elem_link); //remove qh from td_chain
|
|
|
|
elem_safe_free(pthis, FALSE);
|
|
elem_safe_free(&pqh->elem_head_link->elem_link, TRUE);
|
|
|
|
InitializeListHead(&purb->trasac_list);
|
|
// usb_endp_busy_count_dec( purb->pendp ); // the decrement is done in the dpc callback
|
|
purb->pendp->flags =
|
|
(purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE) | (old_toggle ? USB_ENDP_FLAG_DATATOGGLE : 0);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS
|
|
ehci_internal_submit_ctrl(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
|
|
LIST_ENTRY td_list, *pthis, *pnext;
|
|
LONG i, td_count;
|
|
LONG toggle;
|
|
LONG max_packet_size, bytes_to_transfer, bytes_rest, start_idx;
|
|
|
|
PEHCI_QTD ptd;
|
|
PEHCI_QH pqh;
|
|
PEHCI_QH_CONTENT pqhc;
|
|
UCHAR dev_addr;
|
|
BOOLEAN ret;
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PEHCI_QTD_CONTENT ptdc;
|
|
PEHCI_ELEM_LINKS pelnk;
|
|
PUSB_DEV pdev;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
bytes_rest = purb->rest_bytes;
|
|
bytes_to_transfer = purb->bytes_to_transfer;
|
|
max_packet_size = endp_max_packet_size(purb->pendp);
|
|
start_idx = purb->data_length - purb->rest_bytes;
|
|
|
|
calc_td_count(purb, start_idx, td_count);
|
|
td_count += 2; // add setup td and handshake td
|
|
|
|
elem_pool_lock(qtd_pool, TRUE);
|
|
pelnk = elem_pool_alloc_elems(qtd_pool, td_count);
|
|
elem_pool_unlock(qtd_pool, TRUE);
|
|
|
|
if (pelnk == NULL)
|
|
{
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
InsertTailList(&pelnk->elem_link, &td_list);
|
|
ListFirst(&td_list, pthis);
|
|
ListNext(&td_list, pthis, pnext);
|
|
|
|
ptd = qtd_from_list_entry(pthis);
|
|
|
|
pdev = dev_from_endp(purb->pendp);
|
|
dev_addr = pdev->dev_addr;
|
|
|
|
if (dev_state(pdev) <= USB_DEV_STATE_RESET) //only valid for control transfer
|
|
dev_addr = 0;
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("ehci_internal_submit_ctrl(): dev_addr =0x%x\n", dev_addr));
|
|
|
|
// fill the setup packet
|
|
ptdc = (PEHCI_QTD_CONTENT) ptd;
|
|
ptd->hw_next = qtd_from_list_entry(pnext)->phys_addr;
|
|
ptd->hw_alt_next = EHCI_PTR_TERM;
|
|
ptdc->status = 0x80; // active
|
|
ptdc->pid = QTD_PID_SETUP;
|
|
ptdc->err_count = 3;
|
|
ptdc->cur_page = 0;
|
|
ptdc->ioc = 0;
|
|
ptdc->bytes_to_transfer = sizeof(USB_CTRL_SETUP_PACKET);
|
|
ptdc->data_toggle = 0;
|
|
ptd->hw_buf[0] = MmGetPhysicalAddress(purb->setup_packet).LowPart;
|
|
|
|
for(i = 1; i < 16; i++)
|
|
{
|
|
if ((max_packet_size >> i) == 0)
|
|
break;
|
|
}
|
|
i--;
|
|
i &= 0xf;
|
|
|
|
purb->pipe = 0;
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
pipe_content->max_packet_size = i;
|
|
pipe_content->endp_addr = endp_num(purb->pendp);
|
|
pipe_content->dev_addr = dev_addr;
|
|
pipe_content->speed_low = (pdev->flags & USB_DEV_FLAG_LOW_SPEED) ? 1 : 0;
|
|
pipe_content->speed_high = (pdev->flags & USB_DEV_FLAG_HIGH_SPEED) ? 1 : 0;
|
|
pipe_content->trans_type = USB_ENDPOINT_XFER_CONTROL;
|
|
|
|
pthis = pnext;
|
|
ListNext(&td_list, pthis, pnext);
|
|
|
|
// all the tds's toggle and data_buffer pointer is filled here
|
|
toggle = 1;
|
|
ehci_fill_td_buf_ptr(purb, start_idx, pthis, td_count - 2, toggle);
|
|
|
|
for(i = 0; ((i < td_count - 2) && pthis); i++)
|
|
{
|
|
//construct tds for DATA packets of data stage.
|
|
ptd = qtd_from_list_entry(pthis);
|
|
ptdc = (PEHCI_QTD_CONTENT) ptd;
|
|
ptd->hw_alt_next = EHCI_PTR_TERM;
|
|
ptdc->status = 0x80; // active and startXSplit
|
|
ptdc->pid = ((purb->setup_packet[0] & USB_DIR_IN) ? QTD_PID_IN : QTD_PID_OUT);
|
|
ptdc->err_count = 3;
|
|
ptdc->cur_page = 0;
|
|
ptdc->ioc = 0;
|
|
|
|
if (pnext)
|
|
ptd->hw_next = qtd_from_list_entry(pnext)->phys_addr;
|
|
else
|
|
ptd->hw_next = EHCI_PTR_TERM;
|
|
|
|
pthis = pnext;
|
|
if (pthis)
|
|
ListNext(&td_list, pthis, pnext);
|
|
}
|
|
|
|
if (pthis)
|
|
ptd->hw_next = qtd_from_list_entry(pthis)->phys_addr;
|
|
else
|
|
TRAP();
|
|
|
|
// ListFirstPrev( &td_list, pthis );
|
|
ptd = qtd_from_list_entry(pthis);
|
|
|
|
//the last is an IN transaction
|
|
ptdc = (PEHCI_QTD_CONTENT) ptd;
|
|
ptd->hw_alt_next = EHCI_PTR_TERM;
|
|
ptdc->status = 0x80;
|
|
ptdc->pid = ((td_count > 2)
|
|
? ((purb->setup_packet[0] & USB_DIR_IN) ? QTD_PID_OUT : QTD_PID_IN) : QTD_PID_IN);
|
|
|
|
ptdc->err_count = 3;
|
|
ptdc->cur_page = 0;
|
|
ptdc->ioc = 1;
|
|
ptdc->bytes_to_transfer = 0;
|
|
ptdc->data_toggle = 1;
|
|
ptd->hw_next = EHCI_PTR_TERM;
|
|
|
|
ListFirst(&td_list, pthis);
|
|
RemoveEntryList(&td_list);
|
|
|
|
ptd = qtd_from_list_entry(pthis);
|
|
elem_pool_lock(qh_pool, TRUE);
|
|
pelnk = elem_pool_alloc_elem(qh_pool);
|
|
elem_pool_unlock(qh_pool, TRUE);
|
|
|
|
if (pelnk == NULL)
|
|
{
|
|
elem_safe_free(pthis, FALSE);
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
|
|
}
|
|
pqh = (PEHCI_QH) ((ULONG) pelnk->phys_part & PHYS_PART_ADDR_MASK);
|
|
pqhc = (PEHCI_QH_CONTENT) pqh;
|
|
|
|
pqh->hw_alt_next = pqh->hw_next = EHCI_PTR_TERM;
|
|
|
|
pqhc->dev_addr = dev_addr;
|
|
pqhc->inactive = 0;
|
|
pqhc->endp_addr = endp_num(purb->pendp);
|
|
|
|
if (pipe_content->speed_high)
|
|
pqhc->endp_spd = USB_SPEED_HIGH;
|
|
else if (pipe_content->speed_low)
|
|
pqhc->endp_spd = USB_SPEED_LOW;
|
|
else
|
|
pqhc->endp_spd = USB_SPEED_FULL;
|
|
|
|
pqhc->data_toggle = 1; // use dt from qtd
|
|
pqhc->is_async_head = 0;
|
|
pqhc->max_packet_size = endp_max_packet_size(purb->pendp);
|
|
|
|
if (pipe_content->speed_high == 0)
|
|
pqhc->is_ctrl_endp = 1;
|
|
else
|
|
pqhc->is_ctrl_endp = 0;
|
|
|
|
pqhc->reload_counter = EHCI_NAK_RL_COUNT;
|
|
|
|
// DWORD 2
|
|
pqh->hw_info2 = 0;
|
|
pqhc->mult = 1;
|
|
|
|
if (!pipe_content->speed_high)
|
|
{
|
|
pqhc->hub_addr = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->hub_addr;
|
|
pqhc->port_idx = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->port_idx;
|
|
}
|
|
|
|
purb->td_count = td_count;
|
|
|
|
ehci_insert_tds_qh(ehci, pqh, ptd);
|
|
ehci_insert_qh_urb(purb, pqh);
|
|
|
|
usb_endp_busy_count_inc(purb->pendp);
|
|
ehci_insert_urb_to_schedule(ehci, purb, ret);
|
|
|
|
if (ret == FALSE)
|
|
{
|
|
RemoveEntryList(&purb->trasac_list);
|
|
RemoveEntryList(&pqh->elem_head_link->elem_link);
|
|
|
|
elem_safe_free(&pqh->elem_head_link->elem_link, TRUE);
|
|
elem_safe_free(pthis, FALSE);
|
|
|
|
InitializeListHead(&purb->trasac_list);
|
|
// usb_endp_busy_count_dec( purb->pendp );
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS
|
|
ehci_internal_submit_int(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
LONG i, max_packet_size;
|
|
PEHCI_QTD ptd;
|
|
BOOLEAN ret;
|
|
PUSB_DEV pdev;
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
UCHAR mult_trans, toggle, old_toggle;
|
|
PEHCI_ELEM_LINKS pelnk;
|
|
PEHCI_QTD_CONTENT ptdc;
|
|
PEHCI_QH pqh;
|
|
PEHCI_QH_CONTENT pqhc;
|
|
PEHCI_FSTN pfstn;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
old_toggle = toggle = (purb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE) ? TRUE : FALSE;
|
|
max_packet_size = endp_max_packet_size(purb->pendp);
|
|
pdev = dev_from_endp(purb->pendp);
|
|
|
|
if (max_packet_size == 0 || max_packet_size > 64)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if ((pdev->flags & USB_DEV_FLAG_HIGH_SPEED) == 0)
|
|
{
|
|
if (max_packet_size < purb->data_length)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
for(i = 1; i < 16; i++)
|
|
{
|
|
if ((((ULONG) purb->pendp->pusb_endp_desc->bInterval) >> i) == 0)
|
|
break;
|
|
}
|
|
i--;
|
|
mult_trans = 1;
|
|
}
|
|
else
|
|
{
|
|
mult_trans = endp_mult_count(purb->pendp);
|
|
if (max_packet_size * endp_mult_count(purb->pendp) < purb->data_length)
|
|
return STATUS_INVALID_PARAMETER;
|
|
i = purb->pendp->pusb_endp_desc->bInterval - 1;
|
|
}
|
|
|
|
purb->pipe = 0;
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
pipe_content->interval = i;
|
|
pipe_content->trans_type = USB_ENDPOINT_XFER_INT; // bit 0-1
|
|
pipe_content->speed_high = (pdev->flags & USB_DEV_FLAG_HIGH_SPEED) ? 1 : 0; // bit 5
|
|
pipe_content->speed_low = (pdev->flags & USB_DEV_FLAG_LOW_SPEED) ? 1 : 0; // bit 6
|
|
pipe_content->trans_dir = endp_dir(purb->pendp); // bit 7
|
|
pipe_content->dev_addr = pdev->dev_addr; // bit 8-14
|
|
pipe_content->endp_addr = endp_num(purb->pendp); // bit 15-18
|
|
pipe_content->data_toggle = 1; // bit 19
|
|
pipe_content->mult_count = mult_trans;
|
|
|
|
// pipe_content->start_uframe : 3; // bit 28-30 will be filled later
|
|
|
|
for(i = 1; i <= 16; i++)
|
|
{
|
|
if (((ULONG) max_packet_size) >> i)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
i--;
|
|
i &= 0xf;
|
|
|
|
pipe_content->max_packet_size = i; // bit 20-23 log2( max_packet_size )
|
|
|
|
if (ehci_claim_bandwidth(ehci, purb, TRUE) == FALSE)
|
|
{
|
|
// can not allocate bandwidth for it
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// one qtd is enough
|
|
elem_pool_lock(qtd_pool, TRUE);
|
|
pelnk = elem_pool_alloc_elem(qtd_pool);
|
|
elem_pool_unlock(qtd_pool, TRUE);
|
|
|
|
if (pelnk == NULL)
|
|
{
|
|
ehci_claim_bandwidth(ehci, purb, FALSE);
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
ptd = (PEHCI_QTD) ((ULONG) pelnk->phys_part & PHYS_PART_ADDR_MASK);
|
|
ptdc = (PEHCI_QTD_CONTENT) ptd;
|
|
ptd->hw_next = EHCI_PTR_TERM;
|
|
// DWORD 1
|
|
ptd->hw_alt_next = EHCI_PTR_TERM;
|
|
// DWORD 2
|
|
ptdc->status = 0x80;
|
|
ptdc->pid = pipe_content->trans_dir ? QTD_PID_IN : QTD_PID_OUT;
|
|
ptdc->err_count = 3;
|
|
ptdc->cur_page = 0;
|
|
ptdc->ioc = 1;
|
|
ptdc->bytes_to_transfer = purb->data_length;
|
|
toggle = (UCHAR) ehci_fill_td_buf_ptr(purb, 0, &pelnk->elem_link, 1, toggle);
|
|
|
|
elem_pool_lock(qh_pool, TRUE);
|
|
pelnk = elem_pool_alloc_elem(qh_pool);
|
|
elem_pool_unlock(qh_pool, TRUE);
|
|
if (pelnk == NULL)
|
|
{
|
|
elem_safe_free(&ptd->elem_head_link->elem_link, TRUE);
|
|
InitializeListHead(&purb->trasac_list);
|
|
ehci_claim_bandwidth(ehci, purb, FALSE);
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
pqh = (PEHCI_QH) ((ULONG) pelnk->phys_part & PHYS_PART_ADDR_MASK);
|
|
pqhc = (PEHCI_QH_CONTENT) pqh;
|
|
|
|
pqh->hw_next = EHCI_PTR_TERM;
|
|
pqhc->dev_addr = pdev->dev_addr;
|
|
pqhc->inactive = 0;
|
|
pqhc->endp_addr = endp_num(purb->pendp);
|
|
|
|
if (pipe_content->speed_high)
|
|
pqhc->endp_spd = USB_SPEED_HIGH;
|
|
else if (pipe_content->speed_low)
|
|
pqhc->endp_spd = USB_SPEED_LOW;
|
|
else
|
|
pqhc->endp_spd = USB_SPEED_FULL;
|
|
|
|
pqhc->data_toggle = 0;
|
|
pqhc->is_async_head = 0;
|
|
pqhc->max_packet_size = endp_max_packet_size(purb->pendp);
|
|
pqhc->is_ctrl_endp = 0;
|
|
pqhc->reload_counter = 0;
|
|
|
|
// DWORD 2
|
|
pqh->hw_info2 = 0;
|
|
pqhc->mult = mult_trans;
|
|
|
|
if (pipe_content->speed_high)
|
|
{
|
|
if (pipe_content->interval == 0) // one poll per uframe
|
|
pqhc->s_mask = 0xff;
|
|
else if (pipe_content->interval == 1) // one poll every 2 uframe
|
|
pqhc->s_mask = pipe_content->start_uframe == 0 ? 0x55 : 0xbb;
|
|
else if (pipe_content->interval == 2)
|
|
{
|
|
pqhc->s_mask = 0x11;
|
|
pqhc->s_mask <<= pipe_content->start_uframe;
|
|
}
|
|
else
|
|
{
|
|
pqhc->s_mask = 1 << (pipe_content->start_uframe);
|
|
}
|
|
pqhc->c_mask = 0;
|
|
}
|
|
else // full/low speed
|
|
{
|
|
pqhc->s_mask = 1 << pipe_content->start_uframe;
|
|
if (pipe_content->start_uframe < 4)
|
|
{
|
|
pqhc->c_mask = 0x07 << (pipe_content->start_uframe + 2);
|
|
}
|
|
else if (pipe_content->start_uframe == 4)
|
|
{
|
|
pqhc->c_mask = 0xc1;
|
|
}
|
|
else if (pipe_content->start_uframe >= 5)
|
|
{
|
|
// we need fstn
|
|
pqhc->c_mask = 0x03;
|
|
if (pipe_content->start_uframe == 5)
|
|
{
|
|
pqhc->c_mask |= 0x80;
|
|
}
|
|
}
|
|
if (pipe_content->start_uframe >= 4)
|
|
{
|
|
// chain an fstn
|
|
elem_pool_lock(fstn_pool, TRUE);
|
|
pelnk = elem_pool_alloc_elem(fstn_pool);
|
|
elem_pool_unlock(fstn_pool, TRUE);
|
|
if (pelnk == NULL)
|
|
{
|
|
elem_safe_free(&pqh->elem_head_link->elem_link, TRUE);
|
|
elem_safe_free(&ptd->elem_head_link->elem_link, TRUE);
|
|
InitializeListHead(&purb->trasac_list);
|
|
ehci_claim_bandwidth(ehci, purb, FALSE);
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
pfstn = (PEHCI_FSTN) ((ULONG) pelnk->phys_part & PHYS_PART_ADDR_MASK);
|
|
pfstn->hw_prev = ptd->phys_addr;
|
|
pfstn->elem_head_link->purb = purb;
|
|
InsertTailList(&ptd->elem_head_link->elem_link, &pfstn->elem_head_link->elem_link);
|
|
}
|
|
pqhc->hub_addr = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->hub_addr;
|
|
pqhc->port_idx = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->port_idx;
|
|
}
|
|
|
|
// DWORD 3
|
|
purb->td_count = 1;
|
|
|
|
InitializeListHead(&purb->trasac_list);
|
|
ehci_insert_tds_qh(ehci, pqh, ptd);
|
|
ehci_insert_qh_urb(purb, pqh);
|
|
|
|
purb->pendp->flags = (purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE) | (toggle << 31);
|
|
usb_endp_busy_count_inc(purb->pendp);
|
|
|
|
ehci_insert_urb_to_schedule(ehci, purb, ret);
|
|
|
|
if (ret == FALSE)
|
|
{
|
|
RemoveEntryList(&purb->trasac_list);
|
|
RemoveEntryList(&pqh->elem_head_link->elem_link);
|
|
|
|
elem_safe_free(&pqh->elem_head_link->elem_link, TRUE);
|
|
// an fstn may follow the td
|
|
elem_safe_free(&ptd->elem_head_link->elem_link, FALSE);
|
|
|
|
InitializeListHead(&purb->trasac_list);
|
|
ehci_claim_bandwidth(ehci, purb, FALSE);
|
|
|
|
purb->pendp->flags = (purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE) | ((toggle ^ 1) << 31);
|
|
// usb_endp_busy_count_dec( purb->pendp );
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
ehci_internal_submit_iso(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
LONG i, j, td_count, temp;
|
|
PEHCI_ITD pitd;
|
|
PEHCI_SITD psitd;
|
|
PEHCI_SITD_CONTENT psitdc;
|
|
PEHCI_ITD_CONTENT pitdc;
|
|
LIST_ENTRY td_list, *pthis, *pnext, *pprev;
|
|
BOOLEAN ret;
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PUSB_DEV pdev;
|
|
PEHCI_ELEM_LINKS pelnk;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (purb->iso_frame_count == 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
pdev = dev_from_endp(purb->pendp);
|
|
purb->pipe = 0;
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
pipe_content->trans_type = USB_ENDPOINT_XFER_ISOC; // bit 0-1
|
|
pipe_content->speed_high = (pdev->flags & USB_DEV_FLAG_HIGH_SPEED) ? 1 : 0; // bit 5
|
|
pipe_content->speed_low = 0; // bit 6
|
|
pipe_content->trans_dir = endp_dir(purb->pendp); // bit 7
|
|
pipe_content->dev_addr = pdev->dev_addr; // bit 8-14
|
|
pipe_content->endp_addr = endp_num(purb->pendp); // bit 15-18
|
|
pipe_content->data_toggle = 0; // bit 19
|
|
|
|
ret = FALSE;
|
|
purb->params[0] = j = endp_max_packet_size(purb->pendp);
|
|
|
|
if (pipe_content->speed_high == 0)
|
|
{
|
|
// check to see if the frame data is too long to transfer
|
|
if (purb->iso_frame_count >= (LONG) ehci->frame_count)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
if (purb->iso_packet_desc[i].length > j)
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// excess the frame count limit
|
|
if (purb->iso_frame_count >= (LONG) (ehci->frame_count << 3))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
if (purb->iso_packet_desc[i].length > j * endp_mult_count(purb->pendp)) // 3 is max mult-transaction count
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pipe_content->mult_count = endp_mult_count(purb->pendp);
|
|
}
|
|
|
|
pipe_content->max_packet_size = 0; // bit 20-23 log( max_packet_size ), not correct, should not be used
|
|
|
|
if (pipe_content->speed_high == 0)
|
|
{
|
|
for(i = 1; i < 16; i++)
|
|
{
|
|
if ((((ULONG) purb->pendp->pusb_endp_desc->bInterval) >> i) == 0)
|
|
break;
|
|
}
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
i = purb->pendp->pusb_endp_desc->bInterval - 1;
|
|
}
|
|
|
|
pipe_content->interval = i; // bit 24-27 the same definition as in USB2.0 spec, for high or full/low speed
|
|
|
|
if (ehci_claim_bandwidth(ehci, purb, TRUE) == FALSE)
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
if (pipe_content->speed_high == 0)
|
|
{
|
|
td_count = purb->iso_frame_count;
|
|
|
|
// test to see if the last td needs one more sitd for pure complete-split
|
|
if (pipe_content->trans_dir == 0)
|
|
{
|
|
j = (purb->iso_packet_desc[purb->iso_frame_count - 1].length + 187) / 188;
|
|
if (purb->iso_packet_desc[purb->iso_frame_count - 1].params.start_uframe + 1 + j >= 8)
|
|
{
|
|
td_count++;
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
elem_pool_lock(itd_pool, TRUE);
|
|
pelnk = elem_pool_alloc_elems(itd_pool, td_count);
|
|
elem_pool_unlock(itd_pool, TRUE);
|
|
|
|
}
|
|
else
|
|
{
|
|
i = REAL_INTERVAL;
|
|
if (pipe_content->interval >= 3)
|
|
{
|
|
td_count = purb->iso_frame_count;
|
|
j = 0;
|
|
}
|
|
else
|
|
{
|
|
j = purb->iso_start_frame & 0x07;
|
|
if (j == 0)
|
|
{
|
|
td_count = (purb->iso_frame_count + 8 / i - 1) * i / 8;
|
|
}
|
|
else
|
|
{
|
|
j = 1 + (7 - j) / i; // the leading packets from the 8-trans boundary
|
|
td_count = (j >= (LONG) purb->iso_frame_count ?
|
|
1 : 1 + (purb->iso_frame_count - j + 8 / i - 1) * i / 8);
|
|
}
|
|
}
|
|
|
|
elem_pool_lock(sitd_pool, TRUE);
|
|
pelnk = elem_pool_alloc_elems(sitd_pool, td_count);
|
|
elem_pool_unlock(sitd_pool, TRUE);
|
|
}
|
|
|
|
if (pelnk == NULL)
|
|
{
|
|
ehci_claim_bandwidth(ehci, purb, FALSE);
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
InsertTailList(&pelnk->elem_link, &td_list);
|
|
ListFirst(&td_list, pthis);
|
|
pprev = pthis;
|
|
purb->td_count = td_count;
|
|
|
|
//set up offset for high speed and interval == 1
|
|
if (pipe_content->speed_high && pipe_content->interval == 0)
|
|
{
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
if (i == 0)
|
|
purb->iso_packet_desc[i].offset = 0;
|
|
else
|
|
purb->iso_packet_desc[i].offset = purb->iso_packet_desc[i - 1].offset +
|
|
purb->iso_packet_desc[i].length;
|
|
}
|
|
}
|
|
|
|
i = 0, temp = 0;
|
|
|
|
while (pthis)
|
|
{
|
|
init_elem_phys_part(struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link));
|
|
if (pipe_content->speed_high)
|
|
{
|
|
LONG start_uframe, k;
|
|
LONG l, pk_idx, offset, start_uf, td_length;
|
|
PULONG pbuf;
|
|
ULONG phys_addr[8];
|
|
|
|
pitd = itd_from_list_entry(pthis);
|
|
pitdc = (PEHCI_ITD_CONTENT) pitd;
|
|
start_uframe = purb->iso_start_frame & 0x07;
|
|
|
|
// will be filled later
|
|
pitd->hw_next = EHCI_PTR_TERM;
|
|
|
|
// DWORD 9;
|
|
pitdc->dev_addr = pdev->dev_addr;
|
|
pitdc->endp_num = endp_num(purb->pendp);
|
|
|
|
pitdc->max_packet_size = endp_max_packet_size(purb->pendp);
|
|
pitdc->io_dir = pipe_content->trans_dir;
|
|
pitdc->mult = endp_mult_count(purb->pendp);
|
|
|
|
pbuf = pitd->hw_bufp;
|
|
RtlZeroMemory(phys_addr, sizeof(phys_addr));
|
|
|
|
if (pipe_content->interval < 3)
|
|
{
|
|
// this indicates one itd schedules more than one uframes
|
|
// for multiple transactions described by iso_packet_desc
|
|
if (i == 0)
|
|
k = td_count == 1 ? purb->iso_frame_count : j; // the first itd
|
|
else
|
|
k = (LONG) (purb->iso_frame_count - i) <= 8 / REAL_INTERVAL
|
|
? (purb->iso_frame_count - i) : 8 / REAL_INTERVAL;
|
|
|
|
// j is the header transactions out of the interval
|
|
// aligned transactions per td
|
|
if (j > 0 && i == 0) // handle the first itd
|
|
start_uf = start_uframe;
|
|
else
|
|
start_uf = start_uframe % REAL_INTERVAL;
|
|
}
|
|
else
|
|
{
|
|
k = 1, start_uf = start_uframe & 0x07;
|
|
}
|
|
|
|
|
|
// calculate the data to transfer with this td
|
|
td_length = 0;
|
|
for(l = start_uf, pk_idx = i; pk_idx < i + k; pk_idx++, l += REAL_INTERVAL)
|
|
{
|
|
td_length += purb->iso_packet_desc[pk_idx].length;
|
|
phys_addr[l] =
|
|
MmGetPhysicalAddress(&purb->data_buffer[purb->iso_packet_desc[pk_idx].offset]).LowPart;
|
|
}
|
|
|
|
// fill the page pointer, and offset
|
|
if (pipe_content->interval != 0)
|
|
{
|
|
for(l = start_uf, pk_idx = i; pk_idx < i + k; pk_idx++, l += REAL_INTERVAL)
|
|
{
|
|
pitdc->status_slot[l].offset = phys_addr[l] & (PAGE_SIZE - 1);
|
|
pbuf[l >> pipe_content->interval] |= phys_addr[l] & (~(PAGE_SIZE - 1));
|
|
pitdc->status_slot[l].page_sel = l >> pipe_content->interval;
|
|
pitdc->status_slot[l].status = 0x08;
|
|
pitdc->status_slot[l].trans_length = purb->iso_packet_desc[pk_idx].length;
|
|
if (PAGE_SIZE - pitdc->status_slot[l].offset <
|
|
(ULONG) purb->iso_packet_desc[pk_idx].length)
|
|
{
|
|
// fill the next page buf, we can not simply add
|
|
// PAGE_SIZE to the phys_addr[ l ].
|
|
pbuf[(l >> pipe_content->interval) + 1] |=
|
|
MmGetPhysicalAddress((PBYTE)
|
|
(((ULONG) & purb->
|
|
data_buffer[purb->iso_packet_desc[pk_idx].
|
|
offset]) & (~(PAGE_SIZE - 1))) +
|
|
PAGE_SIZE).LowPart;
|
|
}
|
|
}
|
|
}
|
|
else // interval == 0
|
|
{
|
|
LONG m, n = 0, n2 = 0;
|
|
// fill the page buffer first
|
|
// calculate the page buffer needed
|
|
offset = phys_addr[0] & (PAGE_SIZE - 1);
|
|
if (offset != 0)
|
|
{
|
|
offset = PAGE_SIZE - offset;
|
|
l = 1 + (td_length - offset + PAGE_SIZE - 1) / PAGE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
l = (td_length + PAGE_SIZE - 1) / PAGE_SIZE;
|
|
}
|
|
|
|
if (l > 7)
|
|
TRAP();
|
|
|
|
// fill the hw_bufp array and PG field, pk_idx is index into hw_bufp
|
|
for(pk_idx = 0; pk_idx < l; pk_idx++)
|
|
{
|
|
if (pk_idx == 0)
|
|
{
|
|
offset = phys_addr[start_uf] & (~(PAGE_SIZE - 1));
|
|
pbuf[pk_idx] |= offset;
|
|
n = pk_idx;
|
|
pitdc->status_slot[0].page_sel = n;
|
|
n2 = start_uf;
|
|
}
|
|
else
|
|
{
|
|
// scan to find if the buf pointer already filled in the td
|
|
// since interval = 1, we do not need k * REAL_INTERVAL
|
|
// k is transaction count for current td,
|
|
// n is hw_bufp( pbuf ) index
|
|
// n2 is the last phys_addr index we stopped
|
|
for(m = n2; m < start_uf + k; m++)
|
|
{
|
|
// we can not determine the phys_addr[ x ] is piror
|
|
// to offset if it is less than offset.
|
|
// because phys_addr is discrete.
|
|
// if( ( phys_addr[ m ] & ( ~( PAGE_SIZE - 1 ) ) ) < offset )
|
|
// continue;
|
|
|
|
if ((phys_addr[m] & (~(PAGE_SIZE - 1))) == (ULONG) offset)
|
|
{
|
|
pitdc->status_slot[m].page_sel = n;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (m == start_uf + k)
|
|
TRAP();
|
|
|
|
offset = phys_addr[m] & (~(PAGE_SIZE - 1));
|
|
pbuf[pk_idx] |= offset;
|
|
n = pk_idx;
|
|
n2 = m;
|
|
pitdc->status_slot[m].page_sel = n;
|
|
}
|
|
}
|
|
// fill offset and others
|
|
for(l = start_uf, pk_idx = i; l < start_uf + k; l++, pk_idx++)
|
|
{
|
|
pitdc->status_slot[l].offset = (phys_addr[l] & (PAGE_SIZE - 1));
|
|
pitdc->status_slot[l].status = 0x08;
|
|
pitdc->status_slot[l].trans_length = purb->iso_packet_desc[pk_idx].length;
|
|
}
|
|
// exhausted
|
|
}
|
|
i += k;
|
|
}
|
|
else // full/low speed
|
|
{
|
|
psitd = sitd_from_list_entry(pthis);
|
|
psitdc = (PEHCI_SITD_CONTENT) psitd;
|
|
psitd->hw_next = EHCI_PTR_TERM;
|
|
|
|
// DWORD 1;
|
|
psitdc->dev_addr = pdev->dev_addr;
|
|
psitdc->endp_num = endp_num(purb->pendp);
|
|
psitdc->hub_addr = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->hub_addr;
|
|
psitdc->port_idx = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->port_idx;
|
|
psitdc->io_dir = endp_dir(purb->pendp);
|
|
|
|
psitdc->status &= 0x80; // in DWORD 3
|
|
|
|
// DWORD 2;
|
|
j = (purb->iso_packet_desc[i].length + 187) / 188;
|
|
|
|
if (psitdc->io_dir == 0)
|
|
{
|
|
for(; j > 0; j--)
|
|
{
|
|
psitdc->s_mask |= (1 << (j - 1));
|
|
}
|
|
psitdc->s_mask <<= purb->iso_packet_desc[i].params.start_uframe & 0x07;
|
|
psitdc->c_mask = 0;
|
|
}
|
|
else
|
|
{
|
|
LONG k;
|
|
|
|
psitdc->s_mask = 1 << purb->iso_packet_desc[i].params.start_uframe & 0x07;
|
|
// iso split case 2b: ehci spec 1.0
|
|
if (j == 6)
|
|
j = 5;
|
|
|
|
j = j - 1 + 2; // actual complete-split count
|
|
|
|
psitdc->c_mask |= temp >> 8; // the previous sitd's complete split
|
|
if (temp >> 8) // link back for sitd split completion
|
|
{
|
|
psitd->hw_backpointer = sitd_from_list_entry(pprev)->phys_addr;
|
|
psitdc->status &= 0x82;
|
|
}
|
|
else
|
|
{
|
|
psitd->hw_backpointer = EHCI_PTR_TERM;
|
|
}
|
|
|
|
for(k = temp = 0; k < j; k++)
|
|
{
|
|
temp |= 1 << k;
|
|
}
|
|
|
|
temp <<= ((purb->iso_packet_desc[i].params.start_uframe & 0x07) + 2);
|
|
|
|
// only uframe zero and one have complete split for prev sitd
|
|
if ((temp >> 8) > 3)
|
|
TRAP();
|
|
|
|
psitdc->c_mask |= temp & 0xff;
|
|
}
|
|
|
|
// DWORD 3:
|
|
psitdc->c_prog_mask = 0;
|
|
psitdc->bytes_to_transfer = purb->iso_packet_desc[i].length;
|
|
psitdc->page_sel = 0;
|
|
psitdc->ioc = 0;
|
|
|
|
// DWORD 4;
|
|
j = (ULONG) ((PBYTE) purb->data_buffer + purb->iso_packet_desc[i].offset);
|
|
psitd->hw_tx_results2 = MmGetPhysicalAddress((PVOID) j).LowPart;
|
|
|
|
// DWORD 5;
|
|
if (PAGE_SIZE - (j & (PAGE_SIZE - 1)) < (ULONG) purb->iso_packet_desc[i].length)
|
|
{
|
|
// need to fill another slot
|
|
psitdc->page1 =
|
|
MmGetPhysicalAddress((PVOID) ((j & ~(PAGE_SIZE - 1)) + PAGE_SIZE)).LowPart >> 12;
|
|
}
|
|
|
|
if (purb->iso_packet_desc[i].length > 188)
|
|
psitdc->trans_pos = 0x00;
|
|
else if (purb->iso_packet_desc[i].length <= 188)
|
|
psitdc->trans_pos = 0x01;
|
|
|
|
if (psitdc->io_dir == 0)
|
|
psitdc->trans_count = (purb->iso_packet_desc[i].length + 187) / 188;
|
|
|
|
}
|
|
ListNext(&td_list, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
|
|
}
|
|
|
|
if (pipe_content->speed_high == 0)
|
|
{
|
|
// has an extra sitd to fill at the tail
|
|
if (ret)
|
|
{
|
|
ListFirstPrev(&td_list, pthis);
|
|
init_elem_phys_part(struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link));
|
|
|
|
psitd = sitd_from_list_entry(pthis);
|
|
psitdc = (PEHCI_SITD_CONTENT) psitd;
|
|
psitd->hw_next = EHCI_PTR_TERM;
|
|
|
|
// DWORD 1;
|
|
psitdc->dev_addr = pdev->dev_addr;
|
|
psitdc->endp_num = endp_num(purb->pendp);
|
|
psitdc->hub_addr = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->hub_addr;
|
|
psitdc->port_idx = ((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->port_idx;
|
|
psitdc->io_dir = endp_dir(purb->pendp);
|
|
|
|
psitdc->status &= 0x80; // in DWORD 3
|
|
|
|
// DWORD 2;
|
|
psitdc->s_mask = 0x04; // uframe 2, random selection
|
|
|
|
psitdc->c_mask = 0x70; // complete split at uframe 4, 5, 6
|
|
ListFirstPrev(pthis, pprev);
|
|
psitd->hw_backpointer = sitd_from_list_entry(pprev)->phys_addr;
|
|
psitdc->status &= 0x82;
|
|
|
|
// DWORD 3:
|
|
psitdc->c_prog_mask = 0;
|
|
psitdc->bytes_to_transfer = 1; // purb->iso_packet_desc[ purb->iso_frame_count - 1 ].length;
|
|
psitdc->page_sel = 0;
|
|
|
|
j = (ULONG) ((PBYTE) purb->data_buffer + purb->iso_packet_desc[purb->iso_frame_count - 1].offset);
|
|
// the last byte is overridden.
|
|
j += purb->iso_packet_desc[purb->iso_frame_count - 1].length - 1;
|
|
psitd->hw_tx_results2 = MmGetPhysicalAddress((PVOID) j).LowPart;
|
|
}
|
|
|
|
// set the interrupt
|
|
ListFirstPrev(&td_list, pthis);
|
|
psitdc = (PEHCI_SITD_CONTENT) sitd_from_list_entry(pthis);
|
|
psitdc->ioc = 1;
|
|
}
|
|
else
|
|
{
|
|
// set the ioc
|
|
ListFirstPrev(&td_list, pthis);
|
|
pitdc = (PEHCI_ITD_CONTENT) itd_from_list_entry(pthis);
|
|
for(i = 7; i >= 0; i--)
|
|
{
|
|
if (pitdc->status_slot[i].status == 0x08)
|
|
{
|
|
pitdc->status_slot[i].ioc = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (i < 0)
|
|
TRAP();
|
|
}
|
|
|
|
ListFirst(&td_list, pthis);
|
|
// ListFirst( &purb->trasac_list, pthis )
|
|
RemoveEntryList(&td_list);
|
|
InsertTailList(pthis, &purb->trasac_list);
|
|
|
|
while (pthis)
|
|
{
|
|
// fill the purb ptr
|
|
struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link)->purb = purb;
|
|
ListNext(&purb->trasac_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
|
|
//indirectly guarded by pending_endp_list_lock
|
|
usb_endp_busy_count_inc(purb->pendp);
|
|
ehci_insert_urb_to_schedule(ehci, purb, ret);
|
|
|
|
if (ret == FALSE)
|
|
{
|
|
// usb_endp_busy_count_dec( purb->pendp );
|
|
|
|
ListFirst(&purb->trasac_list, pthis);
|
|
RemoveEntryList(&purb->trasac_list);
|
|
|
|
elem_safe_free(pthis, FALSE);
|
|
ehci_claim_bandwidth(ehci, purb, FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
//this function used as the KeSynchronizeExecution param to delegate control to ehci_insert_urb_schedule
|
|
ehci_sync_insert_urb_schedule(PVOID context)
|
|
{
|
|
PSYNC_PARAM sync_param;
|
|
PEHCI_DEV ehci;
|
|
PURB purb;
|
|
|
|
sync_param = (PSYNC_PARAM) context;
|
|
if (sync_param == NULL)
|
|
return FALSE;
|
|
|
|
ehci = sync_param->ehci;
|
|
purb = (PURB) sync_param->context;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return (UCHAR) (sync_param->ret = FALSE);
|
|
|
|
return (UCHAR) (sync_param->ret = ehci_insert_urb_schedule(ehci, purb));
|
|
}
|
|
|
|
static BOOLEAN NTAPI
|
|
ehci_sync_cancel_urb(PVOID context)
|
|
{
|
|
//cancel a single purb
|
|
PEHCI_DEV ehci;
|
|
PSYNC_PARAM sync_param;
|
|
PURB purb2, dest_urb;
|
|
PLIST_ENTRY pthis, pnext;
|
|
BOOLEAN found = FALSE;
|
|
|
|
if (context == NULL)
|
|
return FALSE;
|
|
|
|
sync_param = (PSYNC_PARAM) context;
|
|
ehci = sync_param->ehci;
|
|
dest_urb = (PURB) sync_param->context;
|
|
|
|
if (ehci == NULL || dest_urb == NULL)
|
|
return (UCHAR) (sync_param->ret = FALSE);
|
|
|
|
ListFirst(&ehci->urb_list, pthis);
|
|
while (pthis)
|
|
{
|
|
purb2 = (PURB) pthis;
|
|
if (purb2 == dest_urb)
|
|
{
|
|
found = TRUE;
|
|
purb2->flags |= URB_FLAG_FORCE_CANCEL;
|
|
break;
|
|
}
|
|
ListNext(&ehci->urb_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
press_doorbell(ehci);
|
|
}
|
|
return (UCHAR) (sync_param->ret = found);
|
|
}
|
|
|
|
NTSTATUS
|
|
ehci_cancel_urb(PEHCI_DEV ehci, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
|
|
//note any fields of the purb can not be referenced unless it is found in some queue
|
|
{
|
|
PLIST_ENTRY pthis, pnext;
|
|
BOOLEAN found;
|
|
PURB purb2;
|
|
|
|
SYNC_PARAM sync_param;
|
|
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (ehci == NULL || purb == NULL || pdev == NULL || pendp == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
lock_dev(pdev, FALSE);
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
//delegate to remove device for this job
|
|
return STATUS_DEVICE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
if (dev_from_endp(pendp) != pdev)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (endp_state(pendp) == USB_ENDP_FLAG_STALL)
|
|
{
|
|
//it will be canceled in ehci_process_pending_endp
|
|
unlock_dev(pdev, FALSE);
|
|
return USB_STATUS_ENDPOINT_HALTED;
|
|
}
|
|
|
|
found = FALSE;
|
|
ListFirst(&pendp->urb_list, pthis);
|
|
while (pthis)
|
|
{
|
|
purb2 = (PURB) pthis;
|
|
if (purb2 == purb)
|
|
{
|
|
found = TRUE;
|
|
RemoveEntryList(pthis);
|
|
InitializeListHead(pthis);
|
|
break;
|
|
}
|
|
ListNext(&pendp->urb_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
unlock_dev(pdev, FALSE);
|
|
|
|
if (found)
|
|
{
|
|
purb->status = STATUS_CANCELLED;
|
|
|
|
ehci_generic_urb_completion(purb, purb->context);
|
|
|
|
lock_dev(pdev, FALSE);
|
|
pdev->ref_count--;
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// search the purb in the purb-list and try to cancel
|
|
sync_param.ehci = ehci;
|
|
sync_param.context = purb;
|
|
|
|
KeSynchronizeExecution(ehci->pdev_ext->ehci_int, ehci_sync_cancel_urb, &sync_param);
|
|
|
|
found = sync_param.ret;
|
|
|
|
if (found)
|
|
return USB_STATUS_CANCELING;
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
VOID
|
|
ehci_generic_urb_completion(PURB purb, PVOID context)
|
|
{
|
|
PUSB_DEV pdev;
|
|
BOOLEAN is_ctrl = FALSE;
|
|
USE_NON_PENDING_IRQL;
|
|
|
|
old_irql = KeGetCurrentIrql();
|
|
if (old_irql > DISPATCH_LEVEL)
|
|
TRAP();
|
|
|
|
if (old_irql < DISPATCH_LEVEL)
|
|
KeRaiseIrql(DISPATCH_LEVEL, &old_irql);
|
|
|
|
pdev = purb->pdev;
|
|
if (purb == NULL)
|
|
goto LBL_LOWER_IRQL;
|
|
|
|
if (pdev == NULL)
|
|
goto LBL_LOWER_IRQL;
|
|
|
|
lock_dev(pdev, TRUE);
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
// no need to do following statistics
|
|
unlock_dev(pdev, TRUE);
|
|
goto LBL_CLIENT_PROCESS;
|
|
}
|
|
if (usb_error(purb->status))
|
|
{
|
|
pdev->error_count++;
|
|
}
|
|
|
|
if (purb->pendp == &pdev->default_endp)
|
|
{
|
|
if (usb_halted(purb->status))
|
|
{
|
|
pdev->time_out_count++;
|
|
if (pdev->time_out_count > 3)
|
|
{
|
|
dev_set_state(pdev, USB_DEV_STATE_ZOMB);
|
|
ehci_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_generic_urb_completion(): contiguous error 3 times, dev 0x%x is deactivated\n",
|
|
pdev));
|
|
}
|
|
}
|
|
else
|
|
pdev->time_out_count = 0;
|
|
|
|
}
|
|
|
|
if (endp_type(purb->pendp) == USB_ENDPOINT_XFER_CONTROL)
|
|
is_ctrl = TRUE;
|
|
|
|
unlock_dev(pdev, TRUE);
|
|
|
|
LBL_CLIENT_PROCESS:
|
|
if (!is_ctrl)
|
|
{
|
|
if (purb->completion)
|
|
purb->completion(purb, context);
|
|
}
|
|
else
|
|
{
|
|
if (purb->ctrl_req_context.ctrl_stack_count == 0)
|
|
{
|
|
if (purb->completion)
|
|
purb->completion(purb, context);
|
|
}
|
|
else
|
|
{
|
|
// pstack = &purb->ctrl_req_stack[ purb->ctrl_req_context.ctrl_cur_stack ];
|
|
// if( pstack->urb_completion )
|
|
// pstack->urb_completion( purb, pstack->context );
|
|
usb_call_ctrl_completion(purb);
|
|
}
|
|
}
|
|
|
|
LBL_LOWER_IRQL:
|
|
if (old_irql < DISPATCH_LEVEL)
|
|
KeLowerIrql(old_irql);
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
ehci_rh_submit_urb(PUSB_DEV pdev, PURB purb)
|
|
{
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PTIMER_SVC ptimer;
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
PEHCI_DEV ehci;
|
|
NTSTATUS status;
|
|
PHUB2_EXTENSION hub_ext;
|
|
PUSB_PORT_STATUS ps, psret;
|
|
LONG i;
|
|
UCHAR port_count;
|
|
|
|
USE_NON_PENDING_IRQL;
|
|
if (pdev == NULL || purb == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
dev_mgr = dev_mgr_from_dev(pdev);
|
|
|
|
KeAcquireSpinLock(&dev_mgr->timer_svc_list_lock, &old_irql);
|
|
lock_dev(pdev, FALSE);
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql);
|
|
return STATUS_DEVICE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
ehci = ehci_from_hcd(pdev->hcd);
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
|
|
hub_ext = ((PHUB2_EXTENSION) pdev->dev_ext);
|
|
port_count = (UCHAR) ((PEHCI_HCS_CONTENT) & ehci->ehci_caps.hcs_params)->port_count;
|
|
|
|
switch (endp_type(purb->pendp))
|
|
{
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
{
|
|
if (psetup->bmRequestType == 0xa3 && psetup->bRequest == USB_REQ_GET_STATUS)
|
|
{
|
|
//get-port-status
|
|
if (psetup->wIndex == 0 || psetup->wIndex > port_count || psetup->wLength < 4)
|
|
{
|
|
purb->status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
i = EHCI_PORTSC + 4 * (psetup->wIndex - 1); // USBPORTSC1;
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + i));
|
|
ps = &hub_ext->rh_port_status[psetup->wIndex];
|
|
|
|
psret = (PUSB_PORT_STATUS) purb->data_buffer;
|
|
ps->wPortStatus = 0;
|
|
|
|
if (status & PORT_CCS)
|
|
{
|
|
ps->wPortStatus |= USB_PORT_STAT_CONNECTION;
|
|
}
|
|
if (status & PORT_PE)
|
|
{
|
|
ps->wPortStatus |= USB_PORT_STAT_ENABLE;
|
|
ps->wPortStatus |= USB_PORT_STAT_HIGH_SPEED; // ehci spec
|
|
}
|
|
if (status & PORT_PR)
|
|
{
|
|
ps->wPortStatus |= USB_PORT_STAT_RESET;
|
|
}
|
|
if (status & PORT_SUSP)
|
|
{
|
|
ps->wPortStatus |= USB_PORT_STAT_SUSPEND;
|
|
}
|
|
if (PORT_USB11(status))
|
|
{
|
|
ps->wPortStatus |= USB_PORT_STAT_LOW_SPEED;
|
|
}
|
|
|
|
//always power on
|
|
ps->wPortStatus |= USB_PORT_STAT_POWER;
|
|
|
|
//now set change field
|
|
if ((status & PORT_CSC) && !(ps->wPortStatus & USB_PORT_STAT_LOW_SPEED))
|
|
{
|
|
ps->wPortChange |= USB_PORT_STAT_C_CONNECTION;
|
|
}
|
|
if ((status & PORT_PEC) && !(ps->wPortStatus & USB_PORT_STAT_LOW_SPEED))
|
|
{
|
|
ps->wPortChange |= USB_PORT_STAT_C_ENABLE;
|
|
}
|
|
|
|
//don't touch other fields, might be filled by
|
|
//other function
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_rh_submit_urb(): get port status, wPortStatus=0x%x, wPortChange=0x%x, address=0x%x\n",
|
|
ps->wPortStatus, ps->wPortChange, ps));
|
|
|
|
psret->wPortChange = ps->wPortChange;
|
|
psret->wPortStatus = ps->wPortStatus;
|
|
|
|
purb->status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
}
|
|
else if (psetup->bmRequestType == 0x23 && psetup->bRequest == USB_REQ_CLEAR_FEATURE)
|
|
{
|
|
//clear-port-feature
|
|
if (psetup->wIndex == 0 || psetup->wIndex > port_count)
|
|
{
|
|
purb->status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
i = EHCI_PORTSC + 4 * (psetup->wIndex - 1); // USBPORTSC1;
|
|
ps = &hub_ext->rh_port_status[psetup->wIndex];
|
|
|
|
purb->status = STATUS_SUCCESS;
|
|
switch (psetup->wValue)
|
|
{
|
|
case USB_PORT_FEAT_C_CONNECTION:
|
|
{
|
|
SET_RH2_PORTSTAT(i, USBPORTSC_CSC);
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + i));
|
|
usb_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_rh_submit_urb(): clear csc, port%d=0x%x\n", psetup->wIndex));
|
|
ps->wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
|
|
break;
|
|
}
|
|
case USB_PORT_FEAT_C_ENABLE:
|
|
{
|
|
SET_RH2_PORTSTAT(i, USBPORTSC_PEC);
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + i));
|
|
usb_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_rh_submit_urb(): clear pec, port%d=0x%x\n", psetup->wIndex));
|
|
ps->wPortChange &= ~USB_PORT_STAT_C_ENABLE;
|
|
break;
|
|
}
|
|
case USB_PORT_FEAT_C_RESET:
|
|
{
|
|
ps->wPortChange &= ~USB_PORT_STAT_C_RESET;
|
|
//the reset signal is down in rh_timer_svc_reset_port_completion
|
|
// enable or not is set by host controller
|
|
// status = EHCI_READ_PORT_ULONG( ( PUSHORT ) ( ehci->port_base + i ) );
|
|
usb_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_rh_submit_urb(): clear pr, enable pe, port%d=0x%x\n",
|
|
psetup->wIndex));
|
|
break;
|
|
}
|
|
case USB_PORT_FEAT_ENABLE:
|
|
{
|
|
ps->wPortStatus &= ~USB_PORT_STAT_ENABLE;
|
|
CLR_RH2_PORTSTAT(i, USBPORTSC_PE);
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + i));
|
|
usb_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_rh_submit_urb(): clear pe, port%d=0x%x\n", psetup->wIndex));
|
|
break;
|
|
}
|
|
default:
|
|
purb->status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
}
|
|
else if (psetup->bmRequestType == 0xd3 && psetup->bRequest == HUB_REQ_GET_STATE)
|
|
{
|
|
// get bus state
|
|
if (psetup->wIndex == 0 || psetup->wIndex > port_count || psetup->wLength == 0)
|
|
{
|
|
purb->status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
i = EHCI_PORTSC + 4 * (psetup->wIndex - 1); // USBPORTSC1;
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + i));
|
|
purb->data_buffer[0] = (status & USBPORTSC_LS);
|
|
|
|
// reverse the order
|
|
purb->data_buffer[0] ^= 0x3;
|
|
purb->status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
else if (psetup->bmRequestType == 0x23 && psetup->bRequest == USB_REQ_SET_FEATURE)
|
|
{
|
|
//reset port
|
|
if (psetup->wValue != USB_PORT_FEAT_RESET)
|
|
{
|
|
purb->status = STATUS_INVALID_PARAMETER;
|
|
ehci_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_rh_submit_urb(): set feature with wValue=0x%x\n", psetup->wValue));
|
|
break;
|
|
}
|
|
|
|
i = EHCI_PORTSC + 4 * (psetup->wIndex - 1); // USBPORTSC1;
|
|
|
|
ptimer = alloc_timer_svc(&dev_mgr->timer_svc_pool, 1);
|
|
if (!ptimer)
|
|
{
|
|
purb->status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
|
|
ptimer->threshold = 0; // within [ 50ms, 60ms ], one tick is 10 ms
|
|
ptimer->context = (ULONG) purb;
|
|
ptimer->pdev = pdev;
|
|
ptimer->func = rh_timer_svc_reset_port_completion;
|
|
|
|
//start the timer
|
|
pdev->ref_count += 2; //one for timer and one for purb
|
|
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + i));
|
|
usb_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_rh_submit_urb(): reset port, port%d=0x%x\n", psetup->wIndex, status));
|
|
InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link);
|
|
purb->status = STATUS_PENDING;
|
|
}
|
|
else
|
|
{
|
|
purb->status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
}
|
|
case USB_ENDPOINT_XFER_INT:
|
|
{
|
|
ptimer = alloc_timer_svc(&dev_mgr->timer_svc_pool, 1);
|
|
if (!ptimer)
|
|
{
|
|
purb->status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
ptimer->threshold = RH_INTERVAL;
|
|
ptimer->context = (ULONG) purb;
|
|
ptimer->pdev = pdev;
|
|
ptimer->func = rh_timer_svc_int_completion;
|
|
|
|
//start the timer
|
|
InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link);
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_rh_submit_urb(): current rh's ref_count=0x%x\n", pdev->ref_count));
|
|
pdev->ref_count += 2; //one for timer and one for purb
|
|
|
|
purb->status = STATUS_PENDING;
|
|
break;
|
|
}
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
default:
|
|
{
|
|
purb->status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
unlock_dev(pdev, FALSE);
|
|
KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql);
|
|
return purb->status;
|
|
}
|
|
|
|
//must have rh dev_lock acquired
|
|
BOOLEAN
|
|
ehci_rh_reset_port(PHCD hcd, UCHAR port_idx)
|
|
{
|
|
ULONG i;
|
|
PEHCI_DEV ehci;
|
|
ULONG status;
|
|
UCHAR port_count;
|
|
|
|
if (hcd == NULL)
|
|
return FALSE;
|
|
|
|
ehci = ehci_from_hcd(hcd);
|
|
port_count = (UCHAR) ((PEHCI_HCS_CONTENT) & ehci->ehci_caps.hcs_params)->port_count;
|
|
|
|
if (port_idx < 1 || port_idx > port_count)
|
|
return FALSE;
|
|
|
|
i = (ULONG) (EHCI_PORTSC + 4 * (port_idx - 1));
|
|
|
|
// assert the reset signal,(implicitly disable the port)
|
|
SET_RH2_PORTSTAT(i, PORT_PR);
|
|
|
|
usb_wait_ms_dpc(50);
|
|
// clear the reset signal, delay port enable till clearing port feature
|
|
CLR_RH2_PORTSTAT(i, PORT_PR);
|
|
|
|
// wait the port stable
|
|
usb_wait_ms_dpc(2);
|
|
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + i));
|
|
if (!(status & PORT_PE))
|
|
{
|
|
// release the ownership from ehci to companion hc
|
|
status |= PORT_OWNER;
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (ehci->port_base + i), status);
|
|
// the host controller will set PORTSC automatically
|
|
return FALSE;
|
|
}
|
|
usb_wait_us_dpc(10);
|
|
// SET_RH_PORTSTAT( i, PORT_PE );
|
|
|
|
//recovery time 10ms
|
|
usb_wait_ms_dpc(10);
|
|
|
|
// clear PORT_PEC and PORT_PCC
|
|
SET_RH2_PORTSTAT(i, 0x0a);
|
|
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + i));
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("ehci_rh_reset_port(): status after written=0x%x\n", status));
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
ehci_dispatch_irp(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp)
|
|
{
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PEHCI_DEV ehci;
|
|
|
|
pdev_ext = DeviceObject->DeviceExtension;
|
|
ehci = pdev_ext->ehci;
|
|
|
|
dev_mgr = ehci->hcd_interf.hcd_get_dev_mgr(&ehci->hcd_interf);
|
|
return dev_mgr_dispatch(dev_mgr, irp);
|
|
}
|
|
|
|
//the following are for hcd interface methods
|
|
VOID
|
|
ehci_set_dev_mgr(PHCD hcd, PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
hcd->dev_mgr = dev_mgr;
|
|
}
|
|
|
|
PUSB_DEV_MANAGER
|
|
ehci_get_dev_mgr(PHCD hcd)
|
|
{
|
|
return hcd->dev_mgr;
|
|
}
|
|
|
|
ULONG
|
|
ehci_get_type(PHCD hcd)
|
|
{
|
|
return HCD_TYPE_EHCI; // ( hcd->flags & HCD_TYPE_MASK );
|
|
}
|
|
|
|
VOID
|
|
ehci_set_id(PHCD hcd, UCHAR id)
|
|
{
|
|
hcd->flags &= ~HCD_ID_MASK;
|
|
hcd->flags |= (HCD_ID_MASK & id);
|
|
}
|
|
|
|
UCHAR
|
|
ehci_get_id(PHCD hcd)
|
|
{
|
|
return (UCHAR) (hcd->flags & HCD_ID_MASK);
|
|
}
|
|
|
|
|
|
UCHAR
|
|
ehci_alloc_addr(PHCD hcd)
|
|
{
|
|
LONG i;
|
|
if (hcd == NULL)
|
|
return 0;
|
|
|
|
for(i = 1; i < MAX_DEVS; i++)
|
|
{
|
|
if (hcd->dev_addr_map[i >> 3] & (1 << (i & 7)))
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= MAX_DEVS)
|
|
return 0xff;
|
|
|
|
hcd->dev_addr_map[i >> 3] |= (1 << (i & 7));
|
|
hcd->conn_count++;
|
|
return (BYTE) i;
|
|
}
|
|
|
|
VOID
|
|
ehci_free_addr(PHCD hcd, UCHAR addr)
|
|
{
|
|
if (addr & 0x80)
|
|
return;
|
|
|
|
if (hcd == NULL)
|
|
return;
|
|
|
|
hcd->dev_addr_map[addr >> 3] &= ~(1 << (addr & 7));
|
|
return;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ehci_submit_urb2(PHCD hcd, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
|
|
{
|
|
return ehci_submit_urb(ehci_from_hcd(hcd), pdev, pendp, purb);
|
|
}
|
|
|
|
PUSB_DEV
|
|
ehci_get_root_hub(PHCD hcd)
|
|
{
|
|
return ehci_from_hcd(hcd)->root_hub;
|
|
}
|
|
|
|
VOID
|
|
ehci_set_root_hub(PHCD hcd, PUSB_DEV root_hub)
|
|
{
|
|
if (hcd == NULL || root_hub == NULL)
|
|
return;
|
|
ehci_from_hcd(hcd)->root_hub = root_hub;
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_remove_device2(PHCD hcd, PUSB_DEV pdev)
|
|
{
|
|
if (hcd == NULL || pdev == NULL)
|
|
return FALSE;
|
|
|
|
return ehci_remove_device(ehci_from_hcd(hcd), pdev);
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_hcd_release(PHCD hcd)
|
|
{
|
|
PEHCI_DEV ehci;
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
|
|
if (hcd == NULL)
|
|
return FALSE;
|
|
|
|
ehci = ehci_from_hcd(hcd);
|
|
pdev_ext = ehci->pdev_ext;
|
|
return ehci_release(pdev_ext->pdev_obj, hcd->dev_mgr);
|
|
}
|
|
|
|
NTSTATUS
|
|
ehci_cancel_urb2(PHCD hcd, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
|
|
{
|
|
PEHCI_DEV ehci;
|
|
if (hcd == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
ehci = ehci_from_hcd(hcd);
|
|
return ehci_cancel_urb(ehci, pdev, pendp, purb);
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_rh_get_dev_change(PHCD hcd, PBYTE buf) //must have the rh dev_lock acquired
|
|
{
|
|
PEHCI_DEV ehci;
|
|
LONG port_count, i;
|
|
ULONG status;
|
|
|
|
if (hcd == NULL)
|
|
return FALSE;
|
|
|
|
ehci = ehci_from_hcd(hcd);
|
|
port_count = HCS_N_PORTS(ehci->ehci_caps.hcs_params);
|
|
for(i = 0; i < port_count; i++)
|
|
{
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + EHCI_PORTSC + (i << 2)));
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_rh_get_dev_change(): erh port%d status=0x%x\n", i, status));
|
|
|
|
if (status & (PORT_PEC | PORT_CSC | PORT_OCC))
|
|
{
|
|
buf[(i + 1) >> 3] |= (1 << ((i + 1) & 7));
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
ehci_hcd_dispatch(PHCD hcd, LONG disp_code, PVOID param)
|
|
{
|
|
PEHCI_DEV ehci;
|
|
|
|
if (hcd == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
ehci = ehci_from_hcd(hcd);
|
|
switch (disp_code)
|
|
{
|
|
case HCD_DISP_READ_PORT_COUNT:
|
|
{
|
|
if (param == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
*((PUCHAR) param) = (UCHAR) HCS_N_PORTS(ehci->ehci_caps.hcs_params);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
case HCD_DISP_READ_RH_DEV_CHANGE:
|
|
{
|
|
if (ehci_rh_get_dev_change(hcd, param) == FALSE)
|
|
return STATUS_INVALID_PARAMETER;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// EHCI routines follows
|
|
//
|
|
VOID ehci_init_int8_qh(PEHCI_QH_CONTENT qh);
|
|
|
|
BOOLEAN NTAPI
|
|
ehci_cal_cpu_freq(PVOID context)
|
|
{
|
|
usb_cal_cpu_freq();
|
|
return TRUE;
|
|
}
|
|
|
|
PDEVICE_OBJECT
|
|
ehci_probe(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
LONG bus, i, j, ret = 0;
|
|
PCI_SLOT_NUMBER slot_num;
|
|
PPCI_COMMON_CONFIG pci_config;
|
|
PDEVICE_OBJECT pdev;
|
|
BYTE buffer[sizeof(PCI_COMMON_CONFIG)];
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
LONG count = 0;
|
|
|
|
slot_num.u.AsULONG = 0;
|
|
pci_config = (PPCI_COMMON_CONFIG) buffer;
|
|
pdev = NULL;
|
|
|
|
//scan the PCI buses to find ehci controller
|
|
for (bus = 0; bus <= PCI_MAX_BRIDGE_NUMBER; bus++) //Yes, it should be <=
|
|
{
|
|
for(i = 0; i <= PCI_MAX_DEVICES; i++)
|
|
{
|
|
slot_num.u.bits.DeviceNumber = i;
|
|
for(j = 0; j <= PCI_MAX_FUNCTION; j++)
|
|
{
|
|
slot_num.u.bits.FunctionNumber = j;
|
|
|
|
ret = HalGetBusData(PCIConfiguration,
|
|
bus, slot_num.u.AsULONG, pci_config, PCI_COMMON_HDR_LENGTH);
|
|
|
|
if (ret == 0) /*no this bus */
|
|
break;
|
|
|
|
if (ret == 2) /*no device on the slot */
|
|
break;
|
|
|
|
if (pci_config->BaseClass == 0x0c && pci_config->SubClass == 0x03
|
|
&& pci_config->ProgIf == 0x20)
|
|
{
|
|
//well, we find our usb host controller( EHCI ), create device
|
|
pdev = ehci_alloc(drvr_obj, reg_path, ((bus << 8) | (i << 3) | j), dev_mgr);
|
|
if (pdev)
|
|
#ifdef _MULTI_EHCI
|
|
count++;
|
|
#else
|
|
goto LBL_LOOPOUT;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (ret == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifndef _MULTI_EHCI
|
|
LBL_LOOPOUT:
|
|
#endif
|
|
DbgPrint("Found %d EHCI controllers\n", count);
|
|
|
|
if (pdev)
|
|
{
|
|
pdev_ext = pdev->DeviceExtension;
|
|
if (pdev_ext)
|
|
{
|
|
// acquire higher irql to eliminate pre-empty
|
|
KeSynchronizeExecution(pdev_ext->ehci_int, ehci_cal_cpu_freq, NULL);
|
|
}
|
|
}
|
|
return pdev;
|
|
}
|
|
|
|
PDEVICE_OBJECT
|
|
ehci_alloc(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, ULONG bus_addr, PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
|
|
LONG frd_num, prd_num;
|
|
PDEVICE_OBJECT pdev;
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
ULONG vector, addr_space;
|
|
LONG bus, i;
|
|
KIRQL irql;
|
|
KAFFINITY affinity;
|
|
|
|
DEVICE_DESCRIPTION dev_desc;
|
|
CM_PARTIAL_RESOURCE_DESCRIPTOR *pprd;
|
|
PCI_SLOT_NUMBER slot_num;
|
|
NTSTATUS status;
|
|
|
|
|
|
pdev = ehci_create_device(drvr_obj, dev_mgr);
|
|
|
|
if (pdev == NULL)
|
|
return NULL;
|
|
|
|
pdev_ext = pdev->DeviceExtension;
|
|
|
|
pdev_ext->pci_addr = bus_addr;
|
|
bus = (bus_addr >> 8);
|
|
|
|
slot_num.u.AsULONG = 0;
|
|
slot_num.u.bits.DeviceNumber = ((bus_addr & 0xff) >> 3);
|
|
slot_num.u.bits.FunctionNumber = (bus_addr & 0x07);
|
|
|
|
//now create adapter object
|
|
RtlZeroMemory(&dev_desc, sizeof(dev_desc));
|
|
|
|
dev_desc.Version = DEVICE_DESCRIPTION_VERSION;
|
|
dev_desc.Master = TRUE;
|
|
dev_desc.ScatterGather = TRUE;
|
|
dev_desc.Dma32BitAddresses = TRUE;
|
|
dev_desc.BusNumber = bus;
|
|
dev_desc.InterfaceType = PCIBus;
|
|
dev_desc.MaximumLength = EHCI_MAX_SIZE_TRANSFER;
|
|
|
|
pdev_ext->map_regs = 2; // we do not use it seriously
|
|
|
|
pdev_ext->padapter = HalGetAdapter(&dev_desc, &pdev_ext->map_regs);
|
|
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_alloc(): padapter=0x%x\n", pdev_ext->padapter));
|
|
if (pdev_ext->padapter == NULL)
|
|
{
|
|
//fatal error
|
|
ehci_delete_device(pdev, dev_mgr);
|
|
return NULL;
|
|
}
|
|
|
|
DbgPrint("ehci_alloc(): reg_path=0x%x, \n \
|
|
ehci_alloc(): PCIBus=0x%x, bus=0x%x, bus_addr=0x%x \n \
|
|
ehci_alloc(): slot_num=0x%x, &res_list=0x%x \n \
|
|
ehci_alloc(): adapter=0x%x \n", (DWORD) reg_path, (DWORD) PCIBus, (DWORD) bus, (DWORD) bus_addr, (DWORD) slot_num.u.AsULONG, (DWORD) & pdev_ext->res_list, pdev_ext->padapter);
|
|
|
|
//let's allocate resources for this device
|
|
DbgPrint("ehci_alloc(): about to assign slot res\n");
|
|
if ((status = HalAssignSlotResources(reg_path, NULL, //no class name yet
|
|
drvr_obj, NULL, //no support of another ehci controller
|
|
PCIBus,
|
|
bus, slot_num.u.AsULONG, &pdev_ext->res_list)) != STATUS_SUCCESS)
|
|
{
|
|
DbgPrint("ehci_alloc(): error assign slot res, 0x%x\n", status);
|
|
release_adapter(pdev_ext->padapter);
|
|
pdev_ext->padapter = NULL;
|
|
ehci_delete_device(pdev, dev_mgr);
|
|
return NULL;
|
|
}
|
|
|
|
//parse the resource list
|
|
for(frd_num = 0; frd_num < (LONG) pdev_ext->res_list->Count; frd_num++)
|
|
{
|
|
for(prd_num = 0; prd_num < (LONG) pdev_ext->res_list->List[frd_num].PartialResourceList.Count;
|
|
prd_num++)
|
|
{
|
|
pprd = &pdev_ext->res_list->List[frd_num].PartialResourceList.PartialDescriptors[prd_num];
|
|
if (pprd->Type == CmResourceTypePort)
|
|
{
|
|
RtlCopyMemory(&pdev_ext->res_port, &pprd->u.Port, sizeof(pprd->u.Port));
|
|
|
|
}
|
|
else if (pprd->Type == CmResourceTypeInterrupt)
|
|
{
|
|
RtlCopyMemory(&pdev_ext->res_interrupt, &pprd->u.Interrupt, sizeof(pprd->u.Interrupt));
|
|
}
|
|
else if (pprd->Type == CmResourceTypeMemory)
|
|
{
|
|
RtlCopyMemory(&pdev_ext->res_memory, &pprd->u.Memory, sizeof(pprd->u.Memory));
|
|
}
|
|
}
|
|
}
|
|
|
|
//for port, translate them to system address
|
|
addr_space = 0;
|
|
if (HalTranslateBusAddress(PCIBus, bus, pdev_ext->res_port.Start, &addr_space, //io space
|
|
&pdev_ext->ehci->ehci_reg_base) != (BOOLEAN) TRUE)
|
|
{
|
|
DbgPrint("ehci_alloc(): error, can not translate bus address\n");
|
|
release_adapter(pdev_ext->padapter);
|
|
pdev_ext->padapter = NULL;
|
|
ehci_delete_device(pdev, dev_mgr);
|
|
return NULL;
|
|
}
|
|
|
|
DbgPrint("ehci_alloc(): address space=0x%x\n, reg_base=0x%x\n",
|
|
addr_space, pdev_ext->ehci->ehci_reg_base.u.LowPart);
|
|
|
|
if (addr_space == 0)
|
|
{
|
|
//port has been mapped to memory space
|
|
pdev_ext->ehci->port_mapped = TRUE;
|
|
pdev_ext->ehci->port_base = (PBYTE) MmMapIoSpace(pdev_ext->ehci->ehci_reg_base,
|
|
pdev_ext->res_port.Length, FALSE);
|
|
|
|
//fatal error can not map the registers
|
|
if (pdev_ext->ehci->port_base == NULL)
|
|
{
|
|
release_adapter(pdev_ext->padapter);
|
|
pdev_ext->padapter = NULL;
|
|
ehci_delete_device(pdev, dev_mgr);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//io space
|
|
pdev_ext->ehci->port_mapped = FALSE;
|
|
pdev_ext->ehci->port_base = (PBYTE) pdev_ext->ehci->ehci_reg_base.LowPart;
|
|
}
|
|
|
|
//before we connect the interrupt, we have to init ehci
|
|
pdev_ext->ehci->pdev_ext = pdev_ext;
|
|
|
|
//init ehci_caps
|
|
// i = ( ( PEHCI_HCS_CONTENT )( &pdev_ext->ehci->ehci_caps.hcs_params ) )->length;
|
|
|
|
ehci_get_capabilities(pdev_ext->ehci, pdev_ext->ehci->port_base);
|
|
i = pdev_ext->ehci->ehci_caps.length;
|
|
pdev_ext->ehci->port_base += i;
|
|
|
|
if (ehci_init_schedule(pdev_ext->ehci, pdev_ext->padapter) == FALSE)
|
|
{
|
|
release_adapter(pdev_ext->padapter);
|
|
pdev_ext->padapter = NULL;
|
|
ehci_delete_device(pdev, dev_mgr);
|
|
return NULL;
|
|
}
|
|
|
|
InitializeListHead(&pdev_ext->ehci->urb_list);
|
|
KeInitializeSpinLock(&pdev_ext->ehci->pending_endp_list_lock);
|
|
InitializeListHead(&pdev_ext->ehci->pending_endp_list);
|
|
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_alloc(): pending_endp_list=0x%x\n",
|
|
&pdev_ext->ehci->pending_endp_list));
|
|
|
|
init_pending_endp_pool(&pdev_ext->ehci->pending_endp_pool);
|
|
|
|
KeInitializeTimer(&pdev_ext->ehci->reset_timer);
|
|
|
|
vector = HalGetInterruptVector(PCIBus,
|
|
bus,
|
|
pdev_ext->res_interrupt.level,
|
|
pdev_ext->res_interrupt.vector, &irql, &affinity);
|
|
|
|
KeInitializeDpc(&pdev_ext->ehci_dpc, ehci_dpc_callback, (PVOID) pdev_ext->ehci);
|
|
|
|
//connect the interrupt
|
|
DbgPrint("ehci_alloc(): the int=0x%x\n", vector);
|
|
if (IoConnectInterrupt(&pdev_ext->ehci_int, ehci_isr, pdev_ext->ehci, NULL, //&pdev_ext->ehci->frame_list_lock,
|
|
vector, irql, irql, LevelSensitive, TRUE, //share the vector
|
|
affinity, FALSE) //No float save
|
|
!= STATUS_SUCCESS)
|
|
{
|
|
ehci_release(pdev, dev_mgr);
|
|
return NULL;
|
|
}
|
|
|
|
return pdev;
|
|
}
|
|
|
|
PDEVICE_OBJECT
|
|
ehci_create_device(PDRIVER_OBJECT drvr_obj, PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT pdev;
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
|
|
UNICODE_STRING dev_name;
|
|
UNICODE_STRING symb_name;
|
|
|
|
STRING string, another_string;
|
|
CHAR str_dev_name[64], str_symb_name[64];
|
|
UCHAR hcd_id;
|
|
|
|
if (drvr_obj == NULL)
|
|
return NULL;
|
|
|
|
//note: hcd count wont increment till the hcd is registered in dev_mgr
|
|
sprintf(str_dev_name, "%s%d", EHCI_DEVICE_NAME, dev_mgr->hcd_count);
|
|
sprintf(str_symb_name, "%s%d", EHCI_DOS_DEVICE_NAME, dev_mgr->hcd_count);
|
|
|
|
RtlInitString(&string, str_dev_name);
|
|
RtlAnsiStringToUnicodeString(&dev_name, &string, TRUE);
|
|
|
|
pdev = NULL;
|
|
status = IoCreateDevice(drvr_obj,
|
|
sizeof(EHCI_DEVICE_EXTENSION) + sizeof(EHCI_DEV),
|
|
&dev_name, FILE_EHCI_DEV_TYPE, 0, FALSE, &pdev);
|
|
|
|
if (status != STATUS_SUCCESS || pdev == NULL)
|
|
{
|
|
RtlFreeUnicodeString(&dev_name);
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_create_device(): error create device 0x%x\n", status));
|
|
return NULL;
|
|
}
|
|
|
|
pdev_ext = pdev->DeviceExtension;
|
|
RtlZeroMemory(pdev_ext, sizeof(EHCI_DEVICE_EXTENSION) + sizeof(EHCI_DEV));
|
|
|
|
pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_HCD;
|
|
pdev_ext->dev_ext_hdr.dispatch = ehci_dispatch_irp;
|
|
pdev_ext->dev_ext_hdr.start_io = NULL; //we do not support startio
|
|
pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr;
|
|
|
|
pdev_ext->pdev_obj = pdev;
|
|
pdev_ext->pdrvr_obj = drvr_obj;
|
|
|
|
pdev_ext->ehci = (PEHCI_DEV) & (pdev_ext[1]);
|
|
|
|
RtlInitString(&another_string, str_symb_name);
|
|
RtlAnsiStringToUnicodeString(&symb_name, &another_string, TRUE);
|
|
//RtlInitUnicodeString( &symb_name, DOS_DEVICE_NAME );
|
|
|
|
IoCreateSymbolicLink(&symb_name, &dev_name);
|
|
|
|
ehci_dbg_print(DBGLVL_MAXIMUM,
|
|
("ehci_create_device(): dev=0x%x\n, pdev_ext= 0x%x, ehci=0x%x, dev_mgr=0x%x\n", pdev,
|
|
pdev_ext, pdev_ext->ehci, dev_mgr));
|
|
|
|
RtlFreeUnicodeString(&dev_name);
|
|
RtlFreeUnicodeString(&symb_name);
|
|
|
|
//register with dev_mgr though it is not initilized
|
|
ehci_init_hcd_interface(pdev_ext->ehci);
|
|
hcd_id = dev_mgr_register_hcd(dev_mgr, &pdev_ext->ehci->hcd_interf);
|
|
|
|
pdev_ext->ehci->hcd_interf.hcd_set_id(&pdev_ext->ehci->hcd_interf, hcd_id);
|
|
pdev_ext->ehci->hcd_interf.hcd_set_dev_mgr(&pdev_ext->ehci->hcd_interf, dev_mgr);
|
|
|
|
return pdev;
|
|
|
|
}
|
|
|
|
VOID
|
|
ehci_init_hcd_interface(PEHCI_DEV ehci)
|
|
{
|
|
ehci->hcd_interf.hcd_set_dev_mgr = ehci_set_dev_mgr;
|
|
ehci->hcd_interf.hcd_get_dev_mgr = ehci_get_dev_mgr;
|
|
ehci->hcd_interf.hcd_get_type = ehci_get_type;
|
|
ehci->hcd_interf.hcd_set_id = ehci_set_id;
|
|
ehci->hcd_interf.hcd_get_id = ehci_get_id;
|
|
ehci->hcd_interf.hcd_alloc_addr = ehci_alloc_addr;
|
|
ehci->hcd_interf.hcd_free_addr = ehci_free_addr;
|
|
ehci->hcd_interf.hcd_submit_urb = ehci_submit_urb2;
|
|
ehci->hcd_interf.hcd_generic_urb_completion = ehci_generic_urb_completion;
|
|
ehci->hcd_interf.hcd_get_root_hub = ehci_get_root_hub;
|
|
ehci->hcd_interf.hcd_set_root_hub = ehci_set_root_hub;
|
|
ehci->hcd_interf.hcd_remove_device = ehci_remove_device2;
|
|
ehci->hcd_interf.hcd_rh_reset_port = ehci_rh_reset_port;
|
|
ehci->hcd_interf.hcd_release = ehci_hcd_release;
|
|
ehci->hcd_interf.hcd_cancel_urb = ehci_cancel_urb2;
|
|
ehci->hcd_interf.hcd_start = ehci_start;
|
|
ehci->hcd_interf.hcd_dispatch = ehci_hcd_dispatch;
|
|
|
|
ehci->hcd_interf.flags = HCD_TYPE_EHCI; //hcd types | hcd id
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_init_schedule(PEHCI_DEV ehci, PADAPTER_OBJECT padapter)
|
|
{
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
BOOLEAN ret = TRUE;
|
|
LONG i;
|
|
PEHCI_QH_CONTENT pqh_content;
|
|
PEHCI_QH pqh;
|
|
PEHCI_ELEM_LINKS pelnk;
|
|
|
|
if (ehci == NULL)
|
|
return FALSE;
|
|
|
|
pdev_ext = ehci->pdev_ext;
|
|
if (pdev_ext == NULL)
|
|
return FALSE;
|
|
|
|
// padapter = pdev_ext->padapter;
|
|
if (ehci->frame_count == 0)
|
|
ehci->frame_count = EHCI_DEFAULT_FRAMES;
|
|
|
|
// allocate pools
|
|
for(i = 0; i < 5; i++)
|
|
{
|
|
ret = elem_pool_init_pool(&ehci->elem_pools[i], i, padapter);
|
|
if (ret == FALSE)
|
|
break;
|
|
}
|
|
|
|
if (ret == FALSE)
|
|
{
|
|
i--;
|
|
for(; i >= 0; i--)
|
|
elem_pool_destroy_pool(&ehci->elem_pools[i]);
|
|
return FALSE;
|
|
}
|
|
|
|
// allocate periodic frame list
|
|
ehci->frame_list =
|
|
HalAllocateCommonBuffer(padapter,
|
|
sizeof(ULONG) * ehci->frame_count, &ehci->frame_list_phys_addr, FALSE);
|
|
if (ehci->frame_list == NULL)
|
|
goto ERROR_OUT;
|
|
|
|
RtlZeroMemory(ehci->frame_list, sizeof(ULONG) * ehci->frame_count);
|
|
ehci->frame_list_cpu = usb_alloc_mem(NonPagedPool, sizeof(LIST_HEAD) * ehci->frame_count);
|
|
|
|
if (ehci->frame_list_cpu == NULL)
|
|
goto ERROR_OUT;
|
|
|
|
for(i = 0; i < (LONG) ehci->frame_count; i++)
|
|
{
|
|
InitializeListHead(&ehci->frame_list_cpu[i].td_link);
|
|
}
|
|
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
InitializeListHead(&ehci->periodic_list_cpu[i]);
|
|
}
|
|
|
|
InitializeListHead(&ehci->async_list_cpu);
|
|
|
|
// init frame band budget array
|
|
ehci->frame_bw = usb_alloc_mem(NonPagedPool, sizeof(USHORT) * ehci->frame_count * 8);
|
|
if (ehci->frame_bw == NULL)
|
|
goto ERROR_OUT;
|
|
|
|
for(i = 0; i < (LONG) ehci->frame_count * 8; i++)
|
|
{
|
|
ehci->frame_bw[i] = EHCI_MAX_SYNC_BUS_TIME;
|
|
}
|
|
ehci->min_bw = EHCI_MAX_SYNC_BUS_TIME;
|
|
|
|
// chain the 1ms interrupt qh to the schedule
|
|
if ((pelnk = elem_pool_alloc_elem(&ehci->elem_pools[EHCI_QH_POOL_IDX])) == NULL)
|
|
goto ERROR_OUT;
|
|
|
|
pqh_content = (PEHCI_QH_CONTENT) ((ULONG) pelnk->phys_part & PHYS_PART_ADDR_MASK);
|
|
ehci_init_int8_qh(pqh_content);
|
|
|
|
// chain qh to the shadow list
|
|
InsertTailList(&ehci->periodic_list_cpu[EHCI_SCHED_INT8_INDEX], &pelnk->sched_link);
|
|
|
|
// chain it to the periodic schedule, we use it as a docking point
|
|
// for req of 8- uframes request
|
|
pqh = (PEHCI_QH) pqh_content;
|
|
|
|
for(i = 0; i < (LONG) ehci->frame_count; i++)
|
|
{
|
|
ehci->frame_list[i] = pqh->phys_addr;
|
|
}
|
|
|
|
// allocate fstn
|
|
/*if( ( pelnk = elem_pool_alloc_elem( &ehci->elem_pools[ EHCI_FSTN_POOL_IDX ] ) ) == NULL )
|
|
goto ERROR_OUT;
|
|
|
|
pfstn = ( PEHCI_FSTN )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
|
|
pfstn->hw_next = EHCI_PTR_TERM;
|
|
pfstn->hw_prev = EHCI_PTR_TERM | ( INIT_LIST_FLAG_QH << 1 );
|
|
InsertTailList( &ehci->periodic_list_cpu[ EHCI_SCHED_FSTN_INDEX ], &pelnk->sched_link );
|
|
pqh->hw_next = pfstn->phys_addr; */
|
|
|
|
// allocate for async list head
|
|
if ((pelnk = elem_pool_alloc_elem(&ehci->elem_pools[EHCI_QH_POOL_IDX])) == NULL)
|
|
goto ERROR_OUT;
|
|
|
|
// init the async list head
|
|
pqh = (PEHCI_QH) ((ULONG) pelnk->phys_part & PHYS_PART_ADDR_MASK);
|
|
pqh_content = (PEHCI_QH_CONTENT) pqh;
|
|
ehci_init_int8_qh(pqh_content);
|
|
pqh->hw_next = pqh->phys_addr;
|
|
pqh_content->s_mask = 0;
|
|
pqh_content->is_async_head = 1;
|
|
pqh_content->endp_addr = 0;
|
|
ehci->skel_async_qh = pqh;
|
|
InsertTailList(&ehci->async_list_cpu, &pqh->elem_head_link->sched_link);
|
|
return TRUE;
|
|
|
|
ERROR_OUT:
|
|
ehci_destroy_schedule(ehci);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_destroy_schedule(PEHCI_DEV ehci)
|
|
{
|
|
LONG i;
|
|
if (ehci == NULL)
|
|
return FALSE;
|
|
|
|
if (ehci->frame_bw)
|
|
usb_free_mem(ehci->frame_bw);
|
|
ehci->frame_bw = NULL;
|
|
|
|
if (ehci->frame_list_cpu)
|
|
usb_free_mem(ehci->frame_list_cpu);
|
|
ehci->frame_list_cpu = NULL;
|
|
|
|
if (ehci->frame_list)
|
|
HalFreeCommonBuffer(ehci->pdev_ext->padapter,
|
|
sizeof(ULONG) * ehci->frame_count,
|
|
ehci->frame_list_phys_addr, ehci->frame_list, FALSE);
|
|
|
|
ehci->frame_list = NULL;
|
|
ehci->frame_list_phys_addr.LowPart = 0;
|
|
ehci->frame_list_phys_addr.HighPart = 0;
|
|
|
|
for(i = 0; i < 5; i++)
|
|
elem_pool_destroy_pool(&ehci->elem_pools[i]);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
ehci_init_int8_qh(PEHCI_QH_CONTENT qh)
|
|
{
|
|
if (qh == NULL)
|
|
return;
|
|
// DWORD 0
|
|
qh->terminal = EHCI_PTR_TERM;
|
|
qh->ptr_type = 0;
|
|
qh->reserved = 0;
|
|
qh->next_link = 0;
|
|
|
|
// DWORD 1
|
|
qh->dev_addr = 126; // a fake addr
|
|
qh->inactive = 0;
|
|
qh->endp_addr = 1; // a fake endp
|
|
qh->endp_spd = USB_SPEED_HIGH;
|
|
qh->data_toggle = 0;
|
|
qh->is_async_head = 0;
|
|
qh->max_packet_size = 64;
|
|
qh->is_ctrl_endp = 0;
|
|
qh->reload_counter = 0;
|
|
|
|
// DWORD 2
|
|
qh->s_mask = 0x80; // we are interrupt qh
|
|
qh->c_mask = 0;
|
|
qh->hub_addr = 0;
|
|
qh->port_idx = 0;
|
|
qh->mult = 1;
|
|
|
|
// DWORD 3
|
|
qh->cur_qtd_ptr = 0; // a terminal
|
|
|
|
// overlay
|
|
// !active and !halted
|
|
RtlZeroMemory(&qh->cur_qtd, get_elem_phys_part_size(INIT_LIST_FLAG_QTD));
|
|
qh->cur_qtd.alt_terminal = 1; // don't use this
|
|
qh->cur_qtd.terminal = 1;
|
|
qh->cur_qtd.status = QTD_STS_HALT;
|
|
}
|
|
|
|
VOID
|
|
ehci_get_capabilities(PEHCI_DEV ehci, PBYTE base)
|
|
// fetch capabilities register from ehci
|
|
{
|
|
PEHCI_CAPS pcap;
|
|
PEHCI_HCS_CONTENT phcs;
|
|
LONG i;
|
|
|
|
if (ehci == NULL)
|
|
return;
|
|
|
|
pcap = &ehci->ehci_caps;
|
|
pcap->length = EHCI_READ_PORT_UCHAR((PUCHAR) (base + 0));
|
|
pcap->reserved = EHCI_READ_PORT_UCHAR((PUCHAR) (base + 1));
|
|
pcap->hci_version = EHCI_READ_PORT_USHORT((PUSHORT) (base + 2));
|
|
pcap->hcs_params = EHCI_READ_PORT_ULONG((PULONG) (base + 4));
|
|
pcap->hcc_params = EHCI_READ_PORT_ULONG((PULONG) (base + 8));
|
|
|
|
phcs = (PEHCI_HCS_CONTENT) & pcap->hcs_params;
|
|
if (phcs->port_rout_rules)
|
|
{
|
|
for(i = 0; i < 8; i++)
|
|
pcap->portroute[i] = EHCI_READ_PORT_UCHAR((PUCHAR) (base + 12 + i));
|
|
}
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_delete_device(PDEVICE_OBJECT pdev, PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
STRING string;
|
|
UNICODE_STRING symb_name;
|
|
CHAR str_symb_name[64];
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
|
|
if (pdev == NULL)
|
|
return FALSE;
|
|
|
|
pdev_ext = pdev->DeviceExtension;
|
|
|
|
sprintf(str_symb_name,
|
|
"%s%d", EHCI_DOS_DEVICE_NAME, pdev_ext->ehci->hcd_interf.hcd_get_id(&pdev_ext->ehci->hcd_interf));
|
|
|
|
RtlInitString(&string, str_symb_name);
|
|
RtlAnsiStringToUnicodeString(&symb_name, &string, TRUE);
|
|
IoDeleteSymbolicLink(&symb_name);
|
|
RtlFreeUnicodeString(&symb_name);
|
|
|
|
dev_mgr_deregister_hcd(dev_mgr, pdev_ext->ehci->hcd_interf.hcd_get_id(&pdev_ext->ehci->hcd_interf));
|
|
|
|
if (pdev_ext->res_list)
|
|
ExFreePool(pdev_ext->res_list); // not allocated by usb_alloc_mem
|
|
|
|
IoDeleteDevice(pdev);
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_delete_device(): device deleted\n"));
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
ehci_stop(PEHCI_DEV ehci)
|
|
{
|
|
PBYTE base;
|
|
PEHCI_USBCMD_CONTENT usbcmd;
|
|
LONG tmp;
|
|
|
|
base = ehci->port_base;
|
|
// turn off all the interrupt
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBINTR), 0);
|
|
tmp = EHCI_READ_PORT_ULONG((PULONG) (base + EHCI_USBCMD));
|
|
usbcmd = (PEHCI_USBCMD_CONTENT) & tmp;
|
|
usbcmd->run_stop = 0;
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp);
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_release(PDEVICE_OBJECT pdev, PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
PEHCI_DEV ehci;
|
|
|
|
if (pdev == NULL)
|
|
return FALSE;
|
|
|
|
pdev_ext = pdev->DeviceExtension;
|
|
|
|
if (pdev_ext == NULL)
|
|
return FALSE;
|
|
|
|
ehci = pdev_ext->ehci;
|
|
if (ehci == NULL)
|
|
return FALSE;
|
|
|
|
ehci_stop(ehci);
|
|
|
|
if (pdev_ext->ehci_int)
|
|
{
|
|
IoDisconnectInterrupt(pdev_ext->ehci_int);
|
|
pdev_ext->ehci_int = NULL;
|
|
}
|
|
else
|
|
TRAP();
|
|
destroy_pending_endp_pool(&pdev_ext->ehci->pending_endp_pool);
|
|
|
|
ehci_destroy_schedule(ehci);
|
|
|
|
release_adapter(pdev_ext->padapter);
|
|
pdev_ext->padapter = NULL;
|
|
|
|
ehci_delete_device(pdev, dev_mgr);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_start(PHCD hcd)
|
|
{
|
|
ULONG tmp;
|
|
PBYTE base;
|
|
PEHCI_USBCMD_CONTENT usbcmd;
|
|
PEHCI_DEV ehci;
|
|
|
|
if (hcd == NULL)
|
|
return FALSE;
|
|
|
|
ehci = struct_ptr(hcd, EHCI_DEV, hcd_interf);
|
|
base = ehci->port_base;
|
|
|
|
// stop the controller
|
|
tmp = EHCI_READ_PORT_ULONG((PULONG) (base + EHCI_USBCMD));
|
|
usbcmd = (PEHCI_USBCMD_CONTENT) & tmp;
|
|
usbcmd->run_stop = 0;
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp);
|
|
|
|
// wait the controller stop( ehci spec, 16 microframe )
|
|
usb_wait_ms_dpc(2);
|
|
|
|
// reset the controller
|
|
usbcmd = (PEHCI_USBCMD_CONTENT) & tmp;
|
|
usbcmd->hcreset = TRUE;
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp);
|
|
|
|
for(;;)
|
|
{
|
|
// interval.QuadPart = -100 * 10000; // 10 ms
|
|
// KeDelayExecutionThread( KernelMode, FALSE, &interval );
|
|
KeStallExecutionProcessor(10);
|
|
tmp = EHCI_READ_PORT_ULONG((PULONG) (base + EHCI_USBCMD));
|
|
if (!usbcmd->hcreset)
|
|
break;
|
|
}
|
|
|
|
// prepare the registers
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_CTRLDSSEGMENT), 0);
|
|
|
|
// turn on all the int
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBINTR),
|
|
EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR
|
|
// EHCI_USBINTR_FLROVR | \ // it is noisy
|
|
// EHCI_USBINTR_PC | // we detect it by polling
|
|
);
|
|
// write the list base reg
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_PERIODICLISTBASE), ehci->frame_list_phys_addr.LowPart);
|
|
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_ASYNCLISTBASE), ehci->skel_async_qh->phys_addr & ~(0x1f));
|
|
|
|
usbcmd->int_threshold = 1;
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp);
|
|
|
|
// let's rock
|
|
usbcmd->run_stop = 1;
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp);
|
|
|
|
// set the configuration flag
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_CONFIGFLAG), 1);
|
|
|
|
// enable the list traversaling
|
|
usbcmd->async_enable = 1;
|
|
usbcmd->periodic_enable = 1;
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
ehci_run_stop(PEHCI_DEV ehci, BOOLEAN start)
|
|
{
|
|
PEHCI_USBCMD_CONTENT usbcmd;
|
|
PEHCI_USBSTS_CONTENT usbsts;
|
|
ULONG tmp;
|
|
PBYTE base;
|
|
|
|
if (ehci == NULL)
|
|
return;
|
|
|
|
base = ehci->port_base;
|
|
usbcmd = (PEHCI_USBCMD_CONTENT) & tmp;
|
|
usbsts = (PEHCI_USBSTS_CONTENT) & tmp;
|
|
|
|
tmp = EHCI_READ_PORT_ULONG((PULONG) (base + EHCI_USBSTS));
|
|
if (start && usbsts->hc_halted == 0)
|
|
{
|
|
TRAP();
|
|
usb_dbg_print(DBGLVL_MEDIUM, ("ehci_run_stop(): WARNING: hc running, can not start again\n"));
|
|
return;
|
|
}
|
|
tmp = EHCI_READ_PORT_ULONG((PULONG) (base + EHCI_USBCMD));
|
|
usbcmd->run_stop = start ? 1 : 0;
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (base + EHCI_USBCMD), tmp);
|
|
if (start)
|
|
usb_wait_ms_dpc(2); //ehci spec 16 microframes
|
|
}
|
|
|
|
VOID
|
|
ehci_find_min_bandwidth(PEHCI_DEV ehci)
|
|
{
|
|
LONG i;
|
|
if (ehci == NULL)
|
|
return;
|
|
|
|
for(i = 0; i < (LONG) (ehci->frame_count << 3); i++)
|
|
{
|
|
ehci->min_bw = ehci->min_bw < ehci->frame_bw[i] ? ehci->min_bw : ehci->frame_bw[i];
|
|
}
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_claim_bw_for_int(PEHCI_DEV ehci, PURB purb, BOOLEAN release)
|
|
// should have pending_endp_list_lock acquired, and purb->pipe prepared
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
LONG i, j;
|
|
ULONG interval, bus_time, ss_time, cs_time;
|
|
BOOLEAN bw_avail;
|
|
ULONG max_packet_size;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return FALSE;
|
|
|
|
if (purb->pipe == 0)
|
|
return FALSE;
|
|
|
|
bw_avail = FALSE;
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
interval = REAL_INTERVAL;
|
|
if (pipe_content->speed_high == 0)
|
|
{
|
|
// translate to high speed uframe count
|
|
interval <<= 3;
|
|
}
|
|
|
|
if (interval > (ehci->frame_count << 3))
|
|
interval = (ehci->frame_count << 3);
|
|
|
|
max_packet_size = (1 << pipe_content->max_packet_size);
|
|
if (pipe_content->speed_high)
|
|
{
|
|
// this is a high speed endp
|
|
|
|
bus_time = usb_calc_bus_time(USB_SPEED_HIGH,
|
|
(pipe_content->trans_dir) << 7,
|
|
FALSE, min(purb->data_length, (LONG) max_packet_size));
|
|
|
|
// multiple transactions per uframe
|
|
if (purb->data_length > (LONG) max_packet_size)
|
|
{
|
|
bus_time *= (purb->data_length) / max_packet_size;
|
|
bus_time += usb_calc_bus_time(USB_SPEED_HIGH,
|
|
(pipe_content->trans_dir) << 7,
|
|
FALSE, purb->data_length % max_packet_size);
|
|
}
|
|
bus_time = (bus_time + 1) >> 1;
|
|
|
|
if (release)
|
|
{
|
|
for(i = pipe_content->start_uframe + (purb->int_start_frame << 3);
|
|
i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
ehci->frame_bw[i] += (USHORT) bus_time;
|
|
}
|
|
goto LBL_OUT;
|
|
}
|
|
if (bus_time < ehci->min_bw)
|
|
{
|
|
// start the poll from uframe zero of each frame
|
|
bw_avail = TRUE;
|
|
pipe_content->start_uframe = 0;
|
|
for(i = 0; i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
ehci->frame_bw[i] -= (USHORT) bus_time;
|
|
if (ehci->frame_bw[i] < ehci->min_bw)
|
|
ehci->min_bw = ehci->frame_bw[i];
|
|
}
|
|
}
|
|
else // bother to find a pattern
|
|
{
|
|
for(j = 0; j < (LONG) interval; j++)
|
|
{
|
|
for(i = j; i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
if (ehci->frame_bw[i] < bus_time)
|
|
break;
|
|
}
|
|
if (i > (LONG) (ehci->frame_count << 3))
|
|
{
|
|
bw_avail = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (bw_avail)
|
|
{
|
|
for(i = j; i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
ehci->frame_bw[i] -= (USHORT) bus_time;
|
|
if (ehci->frame_bw[i] < ehci->min_bw)
|
|
ehci->min_bw = ehci->frame_bw[i];
|
|
}
|
|
pipe_content->start_uframe = j & 7;
|
|
purb->int_start_frame = j >> 3;
|
|
}
|
|
}
|
|
}
|
|
else // full/low speed pipe
|
|
{
|
|
// split condition is considered
|
|
if (pipe_content->trans_dir)
|
|
{
|
|
// an input interrupt, with handshake
|
|
// 55 is 144 - 90 + 1, turnaround time is one byte not the worst case 90 bytes,
|
|
// refer to ehci-1.0 table 4-5 p64
|
|
ss_time = 231 * 25 / 12;
|
|
// cs_time = ( 55 * 8 + ( LONG )( ( ( 19 + 7 * 8 * purb->data_length ) / 6 ) ) ) * 25 / 12;
|
|
cs_time = (55 * 8 + (LONG) (((7 * 8 * purb->data_length) / 6))) * 25 / 12;
|
|
}
|
|
else
|
|
{
|
|
// according to ehci-1.0 table 4-5 p64
|
|
ss_time = (49 * 8 + (LONG) (((7 * 8 * purb->data_length) / 6))) * 25 / 12;
|
|
// 287 = 237 + 48( handshake ) + 8( turn around time )
|
|
cs_time = 287 * 25 / 12;
|
|
}
|
|
ss_time >>= 1, cs_time >>= 1;
|
|
if (release)
|
|
{
|
|
for(i = pipe_content->start_uframe + (purb->int_start_frame << 3);
|
|
i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
ehci->frame_bw[i] += (USHORT) ss_time;
|
|
ehci->frame_bw[i + 2] += (USHORT) cs_time;
|
|
ehci->frame_bw[i + 3] += (USHORT) cs_time;
|
|
if ((i & 0x07) != 0x06)
|
|
ehci->frame_bw[i + 4] += (USHORT) cs_time;
|
|
}
|
|
goto LBL_OUT;
|
|
}
|
|
if (ss_time < ehci->min_bw && cs_time < ehci->min_bw)
|
|
{
|
|
pipe_content->start_uframe = 0;
|
|
bw_avail = TRUE;
|
|
for(i = 0; i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
ehci->frame_bw[i] -= (USHORT) ss_time;
|
|
ehci->min_bw = min(ehci->frame_bw[i], ehci->min_bw);
|
|
ehci->frame_bw[i + 2] -= (USHORT) cs_time;
|
|
ehci->min_bw = min(ehci->frame_bw[i + 2], ehci->min_bw);
|
|
ehci->frame_bw[i + 3] -= (USHORT) cs_time;
|
|
ehci->min_bw = min(ehci->frame_bw[i + 3], ehci->min_bw);
|
|
if ((i & 0x07) != 0x06)
|
|
{
|
|
ehci->frame_bw[i + 4] -= (USHORT) cs_time;
|
|
ehci->min_bw = min(ehci->frame_bw[i + 4], ehci->min_bw);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(j = 0; j < (LONG) interval; j++)
|
|
{
|
|
if ((j & 0x7) == 7) // start-split not allowed at this uframe
|
|
continue;
|
|
|
|
for(i = j; i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
if (ehci->frame_bw[i] < ss_time)
|
|
break;
|
|
if (ehci->frame_bw[i + 2] < cs_time)
|
|
break;
|
|
if (ehci->frame_bw[i + 3] < cs_time)
|
|
break;
|
|
if ((i & 0x7) != 6)
|
|
if (ehci->frame_bw[i + 4] < cs_time)
|
|
break;
|
|
}
|
|
if (i > (LONG) (ehci->frame_count << 3))
|
|
{
|
|
bw_avail = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pipe_content->start_uframe = j & 7;
|
|
purb->int_start_frame = j >> 3;
|
|
|
|
for(i = j; i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
ehci->frame_bw[i] -= (USHORT) ss_time;
|
|
ehci->min_bw = min(ehci->frame_bw[i], ehci->min_bw);
|
|
ehci->frame_bw[i + 2] -= (USHORT) cs_time;
|
|
ehci->min_bw = min(ehci->frame_bw[i + 2], ehci->min_bw);
|
|
ehci->frame_bw[i + 3] -= (USHORT) cs_time;
|
|
ehci->min_bw = min(ehci->frame_bw[i + 3], ehci->min_bw);
|
|
if ((i & 0x7) != 6)
|
|
{
|
|
ehci->frame_bw[i + 4] -= (USHORT) cs_time;
|
|
ehci->min_bw = min(ehci->frame_bw[i + 4], ehci->min_bw);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LBL_OUT:
|
|
if (!release)
|
|
return bw_avail;
|
|
else
|
|
{
|
|
ehci_find_min_bandwidth(ehci);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
LONG
|
|
ehci_get_cache_policy(PEHCI_DEV ehci)
|
|
{
|
|
PEHCI_HCC_CONTENT hcc_content;
|
|
|
|
hcc_content = (PEHCI_HCC_CONTENT) & ehci->ehci_caps;
|
|
if (hcc_content->iso_sched_threshold >= 8)
|
|
return 16;
|
|
if (hcc_content->iso_sched_threshold == 0)
|
|
return 2;
|
|
return hcc_content->iso_sched_threshold + 2;
|
|
}
|
|
|
|
// 25/12 is bus-time per bit ( 1000 / 480 )
|
|
#define BEST_BUDGET_TIME_UFRAME ( ( 188 * 7 / 6 ) * 25 / 12 )
|
|
|
|
// in: 231 is sum of split token + host ipg + token, 8 is bus turn-around time, 67 is full speed data token in DATA packet
|
|
// out: 49 byte is sum of split token+ host ipg + token + host ipg + data packet
|
|
#define iso_max_data_load( dir ) ( dir == USB_DIR_IN ? \
|
|
( ( 188 * 8 - 231 - 8 - 67 + ( 8 - 1 ) ) / 8 ) : ( 188 - 49 ) )
|
|
|
|
#define iso_uframes_data_load( dir, data_load, uf_cnt )
|
|
|
|
BOOLEAN
|
|
ehci_claim_bw_for_iso(PEHCI_DEV ehci, PURB purb, BOOLEAN release)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
LONG i, j, k;
|
|
ULONG interval, bus_time, ss_time, cs_time, remainder;
|
|
BOOLEAN bw_avail;
|
|
ULONG cur_uframe, start_uframe = 0, max_time, max_packet_size;
|
|
PBYTE base;
|
|
if (ehci == NULL || purb == NULL)
|
|
return FALSE;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
base = ehci->port_base;
|
|
cur_uframe = EHCI_READ_PORT_ULONG((PULONG) (base + EHCI_FRINDEX)) + 1;
|
|
bw_avail = FALSE;
|
|
|
|
max_packet_size = purb->params[0]; //( 1 << pipe_content->max_packet_size );
|
|
|
|
if (pipe_content->speed_high)
|
|
{
|
|
interval = REAL_INTERVAL;
|
|
if (purb->iso_frame_count == 0 || purb->iso_frame_count + 2 * 8 > (LONG) (ehci->frame_count << 3))
|
|
return FALSE;
|
|
|
|
for(i = 0, max_time = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
bus_time = usb_calc_bus_time(USB_SPEED_HIGH,
|
|
(pipe_content->trans_dir) << 7,
|
|
TRUE, min(purb->iso_packet_desc[i].length, (LONG) max_packet_size));
|
|
// NOTE: we did not use endp_mult_count here, because the comparison is enough
|
|
// to calculate the bandwidth
|
|
if (purb->iso_packet_desc[i].length > (LONG) max_packet_size)
|
|
{
|
|
// multiple transactions per uframe
|
|
bus_time *= purb->iso_packet_desc[i].length / max_packet_size;
|
|
bus_time += usb_calc_bus_time(USB_SPEED_HIGH,
|
|
(pipe_content->trans_dir) << 7,
|
|
TRUE, purb->iso_packet_desc[i].length % max_packet_size);
|
|
}
|
|
bus_time = (bus_time + 1) >> 1;
|
|
max_time = max(bus_time, max_time);
|
|
purb->iso_packet_desc[i].bus_time = bus_time;
|
|
}
|
|
|
|
if (release)
|
|
{
|
|
// it is a release operation
|
|
for(i = purb->iso_start_frame, k = 0; k < (LONG) purb->iso_frame_count;
|
|
k++, i = (i + interval) % (ehci->frame_count << 3))
|
|
{
|
|
ehci->frame_bw[i] += (USHORT) purb->iso_packet_desc[k].bus_time;
|
|
}
|
|
ehci_find_min_bandwidth(ehci);
|
|
return TRUE;
|
|
}
|
|
|
|
if (max_time < ehci->min_bw)
|
|
{
|
|
start_uframe = cur_uframe + ehci_get_cache_policy(ehci); // avoid cache
|
|
for(i = start_uframe, j = 0; j < (LONG) purb->iso_frame_count;
|
|
i = (i + interval) % (ehci->frame_count << 3), j++)
|
|
{
|
|
ehci->frame_bw[i] -= (USHORT) purb->iso_packet_desc[j].bus_time;
|
|
ehci->min_bw = min(ehci->frame_bw[j], ehci->min_bw);
|
|
}
|
|
purb->iso_start_frame = start_uframe;
|
|
return TRUE;
|
|
}
|
|
else // max_time >= ehci->min_bw
|
|
{
|
|
for(j = 0; j < (LONG) interval; j++)
|
|
{
|
|
start_uframe = cur_uframe + ehci_get_cache_policy(ehci) + j;
|
|
for(i = start_uframe, k = 0; k < (LONG) purb->iso_frame_count;
|
|
k++, i = (i + interval) % (ehci->frame_count << 3))
|
|
{
|
|
if (ehci->frame_bw[i] < (USHORT) purb->iso_packet_desc[k].bus_time)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (k < (LONG) purb->iso_frame_count)
|
|
continue;
|
|
bw_avail = TRUE;
|
|
break;
|
|
}
|
|
if (bw_avail)
|
|
{
|
|
// allocate the bandwidth
|
|
for(i = start_uframe, k = 0; k < (LONG) purb->iso_frame_count;
|
|
k++, i = (i + interval) % (ehci->frame_count << 3))
|
|
{
|
|
ehci->frame_bw[i] -= (USHORT) purb->iso_packet_desc[k].bus_time;
|
|
ehci->min_bw = min(ehci->min_bw, ehci->frame_bw[i]);
|
|
}
|
|
purb->iso_start_frame = start_uframe;
|
|
}
|
|
}
|
|
}
|
|
else // not high speed endpoint
|
|
{
|
|
// split transfer
|
|
if (purb->iso_frame_count == 0 || purb->iso_frame_count + 2 > (LONG) ehci->frame_count)
|
|
return FALSE;
|
|
|
|
if (max_packet_size > 1023)
|
|
return FALSE;
|
|
|
|
remainder = 0;
|
|
|
|
//
|
|
// calculate for each frame
|
|
// in: 231 is sum of split token + host ipg + token, 8 is bus turn-around time, 67 is full speed data token in DATA packet
|
|
// out: 49 byte is sum of split token+ host ipg + token + host ipg + data packet
|
|
// bit-stuffing is for high speed bus transfer
|
|
//
|
|
|
|
if (pipe_content->trans_dir)
|
|
{
|
|
// an input transfer, no handshake
|
|
ss_time = 231 * 25 / 12;
|
|
// cs_time = ( 231 + 8 + 67 + ( LONG )( ( ( 19 + 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12;
|
|
cs_time = (231 + 8 + 67 + (LONG) (((7 * 8 * 188) / 6))) * 25 / 12;
|
|
}
|
|
else
|
|
{
|
|
// an output transfer according to ehci-1.0 table 4-5 p64
|
|
// ss_time = ( 49 * 8 + ( LONG )( ( ( 19 + 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12;
|
|
ss_time = (49 * 8 + (LONG) (((7 * 8 * 188) / 6))) * 25 / 12;
|
|
cs_time = 0;
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
// remainder = ( 49 * 8 + ( LONG )( ( ( 19 + 7 * 8 * ( purb->iso_packet_desc[ i ].length % 188 ) ) / 6 ) ) ) * 25 / 12;
|
|
remainder =
|
|
(49 * 8 + (LONG) (((7 * 8 * (purb->iso_packet_desc[i].length % 188)) / 6))) * 25 / 12;
|
|
remainder >>= 1;
|
|
purb->iso_packet_desc[i].params.bus_time = (USHORT) remainder;
|
|
}
|
|
}
|
|
|
|
ss_time >>= 1;
|
|
cs_time >>= 1;
|
|
cur_uframe = (cur_uframe + 7) & (~0x07);
|
|
|
|
j = ehci->frame_count << 3;
|
|
if (release)
|
|
{
|
|
if (pipe_content->trans_dir)
|
|
{
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
ehci->frame_bw[purb->iso_packet_desc[i].params.start_uframe] += (USHORT) ss_time;
|
|
for(k = 0; k < (purb->iso_packet_desc[i].length + 187) / 188; k++)
|
|
{
|
|
ehci->frame_bw[(cur_uframe + 0x12 + (i << 3) + k) % j] += (USHORT) cs_time;
|
|
}
|
|
|
|
// two extra complete-split
|
|
ehci->frame_bw[(cur_uframe + 0x12 + (i << 3) + k) % j] += (USHORT) cs_time;
|
|
ehci->frame_bw[(cur_uframe + 0x13 + (i << 3) + k) % j] += (USHORT) cs_time;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
for(k = 0; k < (purb->iso_packet_desc[i].length + 187) / 188; k++)
|
|
{
|
|
ehci->frame_bw[(cur_uframe + 0x10 + (i << 3) + k) % j] += (USHORT) ss_time;
|
|
}
|
|
}
|
|
}
|
|
ehci_find_min_bandwidth(ehci);
|
|
}
|
|
|
|
// search for available bw
|
|
if (ss_time < ehci->min_bw && cs_time < ehci->min_bw)
|
|
{
|
|
if (pipe_content->trans_dir)
|
|
{
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
ehci->frame_bw[(cur_uframe + 0x10 + (i << 3)) % j] -= (USHORT) ss_time;
|
|
ehci->min_bw = min(ehci->frame_bw[(cur_uframe + 0x10 + (i << 3)) % j], ehci->min_bw);
|
|
|
|
for(k = 0; k < (purb->iso_packet_desc[i].length + 187) / 188; k++)
|
|
{
|
|
ehci->frame_bw[(cur_uframe + 0x12 + (i << 3) + k) % j] -= (USHORT) cs_time;
|
|
ehci->min_bw =
|
|
min(ehci->frame_bw[(cur_uframe + 0x12 + (i << 3) + k) % j], ehci->min_bw);
|
|
}
|
|
|
|
// two extra complete-split
|
|
ehci->frame_bw[(cur_uframe + 0x12 + (i << 3) + k) % j] -= (USHORT) cs_time;
|
|
ehci->min_bw = min(ehci->frame_bw[cur_uframe + 0x12 + (i << 3) + k], ehci->min_bw);
|
|
ehci->frame_bw[(cur_uframe + 0x13 + (i << 3) + k) % j] -= (USHORT) cs_time;
|
|
ehci->min_bw = min(ehci->frame_bw[cur_uframe + 0x13 + (i << 3) + k], ehci->min_bw);
|
|
}
|
|
}
|
|
else // iso output
|
|
{
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
for(k = 0; k < (purb->iso_packet_desc[i].length + 187) / 188; k++)
|
|
{
|
|
ehci->frame_bw[(cur_uframe + 0x10 + (i << 3) + k) % j] -= (USHORT) ss_time;
|
|
ehci->min_bw =
|
|
min(ehci->frame_bw[(cur_uframe + 0x11 + (i << 3) + k) % j], ehci->min_bw);
|
|
}
|
|
}
|
|
}
|
|
purb->iso_start_frame = 0;
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
if (i == 0)
|
|
purb->iso_packet_desc[i].params.start_uframe = (USHORT) (cur_uframe + 0x10);
|
|
else
|
|
purb->iso_packet_desc[i].params.start_uframe =
|
|
purb->iso_packet_desc[i - 1].params.start_uframe + 0x8;
|
|
}
|
|
bw_avail = TRUE;
|
|
}
|
|
else // take the pain to find one
|
|
{
|
|
BOOLEAN large;
|
|
long temp, base;
|
|
|
|
for(j = (cur_uframe >> 3) + 2; j != (LONG) (cur_uframe >> 3); j = (j + 1) % ehci->frame_count)
|
|
{
|
|
temp = 0;
|
|
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
large = purb->iso_packet_desc[i].length > 579;
|
|
base = (purb->iso_packet_desc[i].length + 187) / 188;
|
|
|
|
if (base > 6)
|
|
return FALSE;
|
|
|
|
if (pipe_content->trans_dir)
|
|
{
|
|
// input split iso, for those large than 579, schedule it at the uframe boundary
|
|
for(temp = 0; temp < (large == FALSE) ? (8 - base - 1) : 1; temp++)
|
|
{
|
|
k = (((j + i) << 3) + temp) % (ehci->frame_count << 3);
|
|
if (ehci->frame_bw[k] > ss_time)
|
|
continue;
|
|
|
|
k = base;
|
|
while (k != 0)
|
|
{
|
|
if (ehci->
|
|
frame_bw[(((j + i) << 3) + 1 + temp + k) % (ehci->frame_count << 3)] <
|
|
cs_time)
|
|
break;
|
|
k--;
|
|
}
|
|
if (k > 0) // not available
|
|
continue;
|
|
|
|
// the first following extra cs
|
|
k = (((j + i) << 3) + 2 + temp + base) % (ehci->frame_count << 3);
|
|
if (ehci->frame_bw[k] < cs_time)
|
|
continue;
|
|
|
|
if (base < 6)
|
|
{
|
|
// very large one does not have this second extra cs
|
|
if (ehci->frame_bw[(k + 1) % (ehci->frame_count << 3)] < cs_time)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (temp == 8 - 1 - base) // no bandwidth for ss
|
|
break;
|
|
}
|
|
else // output
|
|
{
|
|
// note: 8 - 1 - base has different meaning from the above
|
|
// it is to avoid the ss on H-Frame 7, but the above one is
|
|
// the latency of the classic bus.
|
|
for(temp = 0; temp < 8 - 1 - base; temp++)
|
|
{
|
|
if (ehci->frame_bw[((j + i) << 3) % (ehci->frame_count << 3) + temp] > ss_time)
|
|
continue;
|
|
|
|
for(k = temp; k < temp + base; k++)
|
|
{
|
|
if (ehci->frame_bw[((j + i) << 3) % (ehci->frame_count << 3) + k] < ss_time)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (temp == 8 - 1 - base)
|
|
break;
|
|
}
|
|
|
|
purb->iso_packet_desc[i].params.start_uframe =
|
|
(USHORT) ((((j + i) << 3) + temp) % (ehci->frame_count << 3));
|
|
// next frame
|
|
}
|
|
if (i < (LONG) purb->iso_frame_count)
|
|
{
|
|
// skip to the next section
|
|
j = (j + i) % (ehci->frame_count << 3);
|
|
continue;
|
|
}
|
|
bw_avail = TRUE;
|
|
break;
|
|
}
|
|
// FIXME: Should we claim bw for the last complete split sitd? this is not done
|
|
// yet.
|
|
if (bw_avail)
|
|
{
|
|
if (pipe_content->trans_dir)
|
|
{
|
|
// input iso
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
j = purb->iso_packet_desc[i].length;
|
|
temp = (purb->iso_packet_desc[i].params.start_uframe) % (ehci->frame_count << 3);
|
|
ehci->frame_bw[temp] -= (USHORT) ss_time;
|
|
for(k = 0; k < (j + 187) / 188; j++)
|
|
{
|
|
ehci->frame_bw[temp + 2 + k] -= (USHORT) cs_time;
|
|
}
|
|
ehci->frame_bw[temp + 2 + k] -= (USHORT) cs_time;
|
|
if ((j + 187) / 188 < 6) //ehci restriction
|
|
{
|
|
ehci->frame_bw[temp + 3 + k] -= (USHORT) cs_time;
|
|
}
|
|
}
|
|
}
|
|
else //output iso
|
|
{
|
|
for(i = 0; i < (LONG) purb->iso_frame_count; i++)
|
|
{
|
|
j = purb->iso_packet_desc[i].length;
|
|
temp = (purb->iso_packet_desc[i].params.start_uframe) % (ehci->frame_count << 3);
|
|
for(k = 0; k < j / 188; j++)
|
|
{
|
|
ehci->frame_bw[temp + k] -= (USHORT) ss_time;
|
|
}
|
|
if (j % 188)
|
|
ehci->frame_bw[temp + k] -= purb->iso_packet_desc[i].params.bus_time;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bw_avail;
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_claim_bandwidth(PEHCI_DEV ehci, PURB purb, BOOLEAN claim_bw) //true to claim band-width, false to free band-width
|
|
// should have pending_endp_list_lock acquired, and purb->pipe prepared
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
BOOLEAN ret;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return FALSE;
|
|
|
|
ret = FALSE;
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
if (pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC)
|
|
{
|
|
ret = ehci_claim_bw_for_iso(ehci, purb, claim_bw ? FALSE : TRUE);
|
|
}
|
|
else if (pipe_content->trans_type == USB_ENDPOINT_XFER_INT)
|
|
{
|
|
ret = ehci_claim_bw_for_int(ehci, purb, claim_bw ? FALSE : TRUE);
|
|
}
|
|
else
|
|
TRAP();
|
|
return ret;
|
|
}
|
|
|
|
BOOLEAN
|
|
ehci_can_remove(PURB purb, BOOLEAN door_bell_rings, ULONG cur_frame)
|
|
// test if the purb can be removed from the schedule, by current frame index and
|
|
// purb states
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
ULONG interval;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
interval = REAL_INTERVAL;
|
|
|
|
switch (purb->flags & URB_FLAG_STATE_MASK)
|
|
{
|
|
case URB_FLAG_STATE_PENDING:
|
|
{
|
|
// not impossible
|
|
TRAP();
|
|
break;
|
|
}
|
|
case URB_FLAG_STATE_IN_PROCESS:
|
|
{
|
|
break;
|
|
}
|
|
case URB_FLAG_STATE_DOORBELL:
|
|
{
|
|
if ((pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL) && door_bell_rings == TRUE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if ((pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TRAP();
|
|
break;
|
|
}
|
|
}
|
|
case URB_FLAG_STATE_WAIT_FRAME:
|
|
{
|
|
// need more processing
|
|
if ((purb->flags & URB_FLAG_FORCE_CANCEL) == 0)
|
|
{
|
|
TRAP();
|
|
break;
|
|
}
|
|
if (pipe_content->trans_type == USB_ENDPOINT_XFER_INT)
|
|
{
|
|
return door_bell_rings;
|
|
}
|
|
else // isochronous can not be canceled
|
|
{
|
|
TRAP();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS ehci_remove_urb_from_schedule(PEHCI_DEV ehci, PURB purb);
|
|
|
|
static VOID
|
|
ehci_deactivate_urb(PURB purb)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PLIST_ENTRY pthis, pnext;
|
|
PEHCI_QH_CONTENT pqh_content;
|
|
PEHCI_QTD_CONTENT pqtd_content;
|
|
|
|
if (purb == NULL)
|
|
return;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
switch (pipe_content->trans_type)
|
|
{
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
case USB_ENDPOINT_XFER_INT:
|
|
{
|
|
ListFirst(&purb->trasac_list, pthis);
|
|
pqh_content = (PEHCI_QH_CONTENT) qh_from_list_entry(pthis);
|
|
ListNext(&purb->trasac_list, pthis, pnext);
|
|
do
|
|
{
|
|
pqtd_content = (PEHCI_QTD_CONTENT) qtd_from_list_entry(pthis);
|
|
if (pqtd_content->status & QTD_STS_ACTIVE)
|
|
{
|
|
pqtd_content->status &= ~QTD_STS_ACTIVE;
|
|
}
|
|
ListNext(&purb->trasac_list, pthis, pnext);
|
|
pthis = pnext;
|
|
|
|
}
|
|
while (pthis);
|
|
break;
|
|
}
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
{
|
|
// fall through
|
|
}
|
|
default:
|
|
TRAP();
|
|
}
|
|
return;
|
|
}
|
|
|
|
static VOID
|
|
ehci_insert_bulk_schedule(PEHCI_DEV ehci, PURB purb)
|
|
// list head is only a handle, the qh and qtd are following it.
|
|
{
|
|
PLIST_ENTRY list_head;
|
|
PEHCI_QH pqh, pqhprev, pqhnext;
|
|
PLIST_ENTRY pthis, pprev, pnext;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return;
|
|
|
|
list_head = &purb->trasac_list;
|
|
ListFirst(list_head, pthis);
|
|
if (pthis == NULL)
|
|
return;
|
|
|
|
if (elem_type_list_entry(pthis) != INIT_LIST_FLAG_QH)
|
|
return;
|
|
|
|
pqh = qh_from_list_entry(pthis);
|
|
// the last qh
|
|
ListFirstPrev(&ehci->async_list_cpu, pprev);
|
|
pqhprev = qh_from_schedule(pprev);
|
|
|
|
// the first qh
|
|
ListFirst(&ehci->async_list_cpu, pnext);
|
|
pqhnext = qh_from_schedule(pnext);
|
|
|
|
if (pprev == &ehci->async_list_cpu)
|
|
{
|
|
// always a qh in async list
|
|
TRAP();
|
|
return;
|
|
}
|
|
pqh->hw_next = pqhnext->phys_addr;
|
|
InsertTailList(&ehci->async_list_cpu, &pqh->elem_head_link->sched_link);
|
|
pqhprev->hw_next = pqh->phys_addr;
|
|
return;
|
|
}
|
|
|
|
static VOID
|
|
ehci_remove_bulk_from_schedule(PEHCI_DEV ehci, PURB purb)
|
|
// executed in isr, and have frame_list_lock acquired, so
|
|
// never try to acquire any spin-lock
|
|
// remove the bulk purb from schedule, and mark it not in
|
|
// the schedule
|
|
{
|
|
PLIST_ENTRY list_head;
|
|
PEHCI_QH pqh, pqhprev, pqhnext;
|
|
PEHCI_QH_CONTENT pqhc;
|
|
PLIST_ENTRY pthis, pprev, pnext;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return;
|
|
|
|
list_head = &purb->trasac_list;
|
|
ListFirst(list_head, pthis);
|
|
if (pthis == NULL)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
pqh = qh_from_list_entry(pthis);
|
|
pqhc = (PEHCI_QH_CONTENT) pqh;
|
|
|
|
if (pqhc->is_async_head)
|
|
TRAP();
|
|
|
|
ListFirst(&pqh->elem_head_link->sched_link, pnext);
|
|
ListFirstPrev(&pqh->elem_head_link->sched_link, pprev);
|
|
|
|
if (pprev == &ehci->async_list_cpu)
|
|
{
|
|
// we will at least have a qh with H-bit 1 in the async-list
|
|
TRAP();
|
|
}
|
|
else if (pnext == &ehci->async_list_cpu)
|
|
{
|
|
// remove the last one
|
|
pqhprev = qh_from_schedule(pprev);
|
|
ListFirst(&ehci->async_list_cpu, pnext);
|
|
pqhnext = qh_from_schedule(pnext);
|
|
pqhprev->hw_next = pqhnext->phys_addr;
|
|
}
|
|
else
|
|
{
|
|
pqhprev = qh_from_schedule(pprev);
|
|
pqhnext = qh_from_schedule(pnext);
|
|
pqhprev->hw_next = pqhnext->phys_addr;
|
|
}
|
|
RemoveEntryList(&pqh->elem_head_link->sched_link);
|
|
return;
|
|
}
|
|
|
|
|
|
static VOID
|
|
ehci_insert_fstn_schedule(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
|
|
PURB_HS_PIPE_CONTENT pipe_content, pc;
|
|
PLIST_ENTRY pthis, list_head, pnext = NULL, pprev;
|
|
PEHCI_QH pqhnext;
|
|
PEHCI_FSTN pfstn;
|
|
PURB purb1;
|
|
|
|
ULONG interval, start_frame, start_uframe;
|
|
LONG i;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
interval = (1 << (pipe_content->interval + 3));
|
|
list_head = &purb->trasac_list;
|
|
start_frame = purb->int_start_frame;
|
|
start_uframe = (start_frame << 3) + 1; //( start_frame << 3 ) + pipe_content->start_uframe;
|
|
|
|
if ((start_frame << 3) >= interval)
|
|
TRAP();
|
|
|
|
ListFirstPrev(list_head, pprev);
|
|
|
|
if (elem_type_list_entry(pprev) != INIT_LIST_FLAG_FSTN)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
|
|
pfstn = fstn_from_list_entry(pprev);
|
|
|
|
if (interval == 8)
|
|
{
|
|
ListFirst(&ehci->periodic_list_cpu[EHCI_SCHED_INT8_INDEX], pthis);
|
|
|
|
// skip the first one
|
|
ListNext(&ehci->periodic_list_cpu[EHCI_SCHED_INT8_INDEX], pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
|
|
while (pthis)
|
|
{
|
|
purb1 = qh_from_schedule(pthis)->elem_head_link->purb;
|
|
pc = (PURB_HS_PIPE_CONTENT) & purb1->pipe;
|
|
if (pc->speed_high)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
if ((1 << (pc->interval + 3)) > (LONG) interval)
|
|
{
|
|
TRAP();
|
|
continue;
|
|
}
|
|
else if ((1 << (pc->interval + 3) < (LONG) interval))
|
|
{
|
|
break;
|
|
}
|
|
else if (elem_type(pthis, FALSE) == INIT_LIST_FLAG_FSTN)
|
|
{
|
|
ListNext(&ehci->periodic_list_cpu[EHCI_SCHED_INT8_INDEX], pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
}
|
|
else if (pc->start_uframe <= 1)
|
|
{
|
|
ListNext(&ehci->periodic_list_cpu[EHCI_SCHED_INT8_INDEX], pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
}
|
|
break;
|
|
}
|
|
if (pprev == NULL)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
if (pthis == NULL)
|
|
{
|
|
//the last one
|
|
InsertTailList(&ehci->periodic_list_cpu[EHCI_SCHED_INT8_INDEX],
|
|
&pfstn->elem_head_link->sched_link);
|
|
}
|
|
else
|
|
{
|
|
if (elem_type(pprev, FALSE) == INIT_LIST_FLAG_FSTN)
|
|
{
|
|
InsertHeadList(&fstn_from_schedule(pprev)->elem_head_link->sched_link,
|
|
&pfstn->elem_head_link->sched_link);
|
|
}
|
|
else
|
|
{
|
|
InsertHeadList(&qh_from_schedule(pprev)->elem_head_link->sched_link,
|
|
&pfstn->elem_head_link->sched_link);
|
|
}
|
|
}
|
|
pfstn->hw_next = qh_from_schedule(pprev)->hw_next;
|
|
qh_from_schedule(pprev)->hw_next = pfstn->phys_addr;
|
|
}
|
|
else
|
|
{
|
|
start_frame++;
|
|
for(i = start_frame; i < (LONG) start_frame + 1; i += (interval >> 3))
|
|
{
|
|
list_head = &ehci->frame_list_cpu[i].td_link;
|
|
ListFirst(list_head, pthis);
|
|
|
|
pprev = list_head;
|
|
while (pthis)
|
|
{
|
|
// skip itds and sitds
|
|
if (elem_type(pthis, FALSE) == INIT_LIST_FLAG_ITD ||
|
|
elem_type(pthis, FALSE) == INIT_LIST_FLAG_SITD)
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
while (pthis)
|
|
{
|
|
// find the insertion point
|
|
ULONG u;
|
|
|
|
pqhnext = qh_from_schedule(pthis);
|
|
if (elem_type(pthis, FALSE) == INIT_LIST_FLAG_FSTN)
|
|
purb1 = fstn_from_schedule(pthis)->elem_head_link->purb;
|
|
else
|
|
purb1 = pqhnext->elem_head_link->purb;
|
|
|
|
if (purb1 == NULL)
|
|
TRAP();
|
|
|
|
pc = (PURB_HS_PIPE_CONTENT) & purb1->pipe;
|
|
u = 1 << (pc->speed_high ? (1 << pc->interval) : (1 << (pc->interval + 3)));
|
|
|
|
if (u > interval)
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
continue;
|
|
}
|
|
else if (u == interval)
|
|
{
|
|
if (start_uframe >=
|
|
(elem_type(pthis, FALSE) == INIT_LIST_FLAG_FSTN ?
|
|
1 : pc->start_uframe) + (purb1->int_start_frame << 3))
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
continue;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else if (u < interval)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pprev == list_head)
|
|
{
|
|
// insert to the list head
|
|
pnext = pfstn->elem_head_link->sched_link.Flink = list_head->Flink;
|
|
list_head->Flink = &pfstn->elem_head_link->sched_link;
|
|
pfstn->hw_next = ehci->frame_list[i]; // point to following node
|
|
ehci->frame_list[i] = pfstn->phys_addr;
|
|
}
|
|
else
|
|
{
|
|
pnext = pfstn->elem_head_link->sched_link.Flink = pprev->Flink;
|
|
pprev->Flink = &pfstn->elem_head_link->sched_link;
|
|
|
|
// fstn can be handled correctly
|
|
pfstn->hw_next = qh_from_schedule(pprev)->hw_next;
|
|
qh_from_schedule(pprev)->hw_next = pfstn->phys_addr;
|
|
}
|
|
}
|
|
// the pointer to next node of this fstn is alway same across the frame list.
|
|
for(i = start_frame + (interval >> 3); i < (LONG) ehci->frame_count; i += (interval >> 3))
|
|
{
|
|
pprev = list_head = &ehci->frame_list_cpu[i].td_link;
|
|
ListFirst(list_head, pthis);
|
|
|
|
while (pthis)
|
|
{
|
|
if (pthis == pnext)
|
|
{
|
|
break;
|
|
}
|
|
pprev = pthis;
|
|
ListNext(list_head, pthis, pthis);
|
|
}
|
|
|
|
pprev->Flink = &pfstn->elem_head_link->sched_link;
|
|
if (pprev == list_head)
|
|
ehci->frame_list[i] = pfstn->phys_addr;
|
|
else
|
|
qh_from_schedule(pprev)->hw_next = pfstn->phys_addr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
ehci_remove_fstn_from_schedule(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PLIST_ENTRY pthis, list_head, pnext, pprev;
|
|
PEHCI_FSTN pfstn;
|
|
|
|
ULONG interval, start_frame, start_uframe;
|
|
LONG i;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
interval = (1 << (pipe_content->interval + 3));
|
|
list_head = &purb->trasac_list;
|
|
start_frame = purb->int_start_frame;
|
|
start_uframe = 1;
|
|
|
|
if ((start_frame << 3) >= interval)
|
|
TRAP();
|
|
start_frame++;
|
|
|
|
ListFirstPrev(list_head, pprev);
|
|
if (elem_type_list_entry(pprev) != INIT_LIST_FLAG_FSTN)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
|
|
pfstn = fstn_from_list_entry(pprev);
|
|
if (interval < 8)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
if (interval == 8)
|
|
{
|
|
ListFirstPrev(&pfstn->elem_head_link->sched_link, pprev);
|
|
qh_from_schedule(pprev)->hw_next = pfstn->hw_next;
|
|
RemoveEntryList(&pfstn->elem_head_link->sched_link);
|
|
}
|
|
else
|
|
{
|
|
for(i = start_frame; i < (LONG) ehci->frame_count; i++)
|
|
{
|
|
ListFirst(&ehci->frame_list_cpu[i].td_link, pthis);
|
|
if (pthis == NULL)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
pprev = &ehci->frame_list_cpu[i].td_link;
|
|
while (pthis && pthis != &pfstn->elem_head_link->sched_link)
|
|
{
|
|
pprev = pthis;
|
|
ListNext(&ehci->frame_list_cpu[i].td_link, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
if (pthis == NULL)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
qh_from_schedule(pprev)->hw_next = pfstn->hw_next;
|
|
pprev->Flink = pfstn->elem_head_link->sched_link.Flink;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static VOID
|
|
ehci_insert_int_schedule(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content, pc;
|
|
PLIST_ENTRY pthis, list_head, pnext = NULL, pprev;
|
|
PEHCI_ELEM_LINKS elem_link;
|
|
PEHCI_QH pqh, pqhprev, pqhnext;
|
|
PURB purb1;
|
|
|
|
ULONG interval, u, start_frame, start_uframe;
|
|
LONG i;
|
|
UCHAR need_fstn;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
interval = REAL_INTERVAL;
|
|
start_uframe = (purb->int_start_frame << 3) + pipe_content->start_uframe;
|
|
start_frame = purb->int_start_frame;
|
|
need_fstn = FALSE;
|
|
list_head = &purb->trasac_list;
|
|
|
|
ListFirst(list_head, pthis);
|
|
if (pthis == NULL)
|
|
return;
|
|
|
|
pqh = qh_from_list_entry(pthis);
|
|
|
|
if (!pipe_content->speed_high)
|
|
{
|
|
interval = (interval << 3);
|
|
ListFirstPrev(list_head, pprev);
|
|
if (elem_type_list_entry(pprev) == INIT_LIST_FLAG_FSTN)
|
|
need_fstn = TRUE;
|
|
}
|
|
|
|
if (interval < 16)
|
|
{
|
|
pqhprev = pqhnext = NULL;
|
|
if (interval == 1)
|
|
{
|
|
list_head = &ehci->periodic_list_cpu[EHCI_SCHED_FSTN_INDEX];
|
|
ListFirst(list_head, pthis);
|
|
InsertTailList(list_head, &pqh->elem_head_link->sched_link);
|
|
ListFirstPrev(&pqh->elem_head_link->sched_link, pprev);
|
|
pqh->hw_next = EHCI_PTR_TERM;
|
|
if (pprev == pthis)
|
|
{
|
|
fstn_from_schedule(pthis)->hw_next = pqh->phys_addr;
|
|
}
|
|
else
|
|
{
|
|
qh_from_schedule(pthis)->hw_next = pqh->phys_addr;
|
|
}
|
|
}
|
|
else // interval == 2 or 4 or 8
|
|
{
|
|
list_head = &ehci->periodic_list_cpu[EHCI_SCHED_INT8_INDEX];
|
|
ListFirst(list_head, pthis);
|
|
pprev = NULL;
|
|
while (pthis)
|
|
{
|
|
elem_link = struct_ptr(pthis, EHCI_ELEM_LINKS, sched_link);
|
|
purb1 = elem_link->purb;
|
|
pc = (PURB_HS_PIPE_CONTENT) purb1->pipe;
|
|
u = (pc->speed_high ? (1 << pc->interval) : (1 << (pc->interval + 3)));
|
|
|
|
if (interval < u)
|
|
{
|
|
ListFirstPrev(pthis, pprev);
|
|
break;
|
|
}
|
|
else if (interval > u)
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
continue;
|
|
}
|
|
// FIXME: is this right to fix fstn's start_uf 1???
|
|
else if (start_uframe <=
|
|
(elem_type(pthis, FALSE) == INIT_LIST_FLAG_FSTN ?
|
|
1 : pc->start_uframe) + (purb1->int_start_frame << 3))
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
continue;
|
|
}
|
|
else // interval is equal, and start_uframe is greater
|
|
{
|
|
ListFirstPrev(pthis, pprev);
|
|
break;
|
|
}
|
|
}
|
|
if (pprev == NULL)
|
|
{
|
|
// at least one dummy qh is there
|
|
TRAP();
|
|
}
|
|
else if (pnext == NULL)
|
|
{
|
|
// the last one in this chain, fstn can be handled correctly
|
|
InsertTailList(list_head, &pqh->elem_head_link->sched_link);
|
|
pqhprev = qh_from_schedule(pprev);
|
|
pqh->hw_next = pqhprev->hw_next;
|
|
pqhprev->hw_next = pqh->phys_addr;
|
|
}
|
|
else
|
|
{
|
|
pqhprev = qh_from_schedule(pprev);
|
|
if (elem_type(pprev, FALSE) == INIT_LIST_FLAG_QH)
|
|
{
|
|
InsertHeadList(&pqhprev->elem_head_link->sched_link, &pqh->elem_head_link->sched_link);
|
|
}
|
|
else if (elem_type(pprev, FALSE) == INIT_LIST_FLAG_FSTN)
|
|
{
|
|
InsertHeadList(&fstn_from_schedule(pprev)->elem_head_link->sched_link,
|
|
&pqh->elem_head_link->sched_link);
|
|
}
|
|
pqh->hw_next = pqhprev->hw_next;
|
|
pqhprev->hw_next = pqh->phys_addr;
|
|
}
|
|
}
|
|
}
|
|
else // interval >= 16
|
|
{
|
|
if ((start_frame << 3) >= interval)
|
|
TRAP();
|
|
|
|
for(i = start_frame; i < (LONG) start_frame + 1; i += (interval >> 3))
|
|
{
|
|
list_head = &ehci->frame_list_cpu[i].td_link;
|
|
ListFirst(list_head, pthis);
|
|
|
|
pprev = list_head;
|
|
while (pthis)
|
|
{
|
|
// skip itds and sitds
|
|
if (elem_type(pthis, FALSE) == INIT_LIST_FLAG_ITD ||
|
|
elem_type(pthis, FALSE) == INIT_LIST_FLAG_SITD)
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
while (pthis)
|
|
{
|
|
// find the insertion point
|
|
|
|
pqhnext = qh_from_schedule(pthis);
|
|
if (elem_type(pthis, FALSE) == INIT_LIST_FLAG_FSTN)
|
|
purb1 = fstn_from_schedule(pthis)->elem_head_link->purb;
|
|
else
|
|
purb1 = pqhnext->elem_head_link->purb;
|
|
|
|
if (purb1 == NULL)
|
|
TRAP();
|
|
|
|
pc = (PURB_HS_PIPE_CONTENT) & purb1->pipe;
|
|
u = 1 << (pc->speed_high ? (1 << pc->interval) : (1 << (pc->interval + 3)));
|
|
|
|
if (u > interval)
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
continue;
|
|
}
|
|
else if (u == interval)
|
|
{
|
|
if (start_uframe >=
|
|
(elem_type(pthis, FALSE) == INIT_LIST_FLAG_FSTN ?
|
|
1 : pc->start_uframe) + (purb1->int_start_frame << 3))
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pprev = pthis;
|
|
pthis = pnext;
|
|
continue;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else if (u < interval)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (pprev == list_head)
|
|
{
|
|
// insert to the list head
|
|
pnext = pqh->elem_head_link->sched_link.Flink = list_head->Flink;
|
|
list_head->Flink = &pqh->elem_head_link->sched_link;
|
|
pqh->hw_next = ehci->frame_list[i]; // point to following node
|
|
ehci->frame_list[i] = pqh->phys_addr;
|
|
}
|
|
else
|
|
{
|
|
pnext = pqh->elem_head_link->sched_link.Flink = pprev->Flink;
|
|
pprev->Flink = &pqh->elem_head_link->sched_link;
|
|
|
|
// fstn can be handled correctly
|
|
pqh->hw_next = qh_from_schedule(pprev)->hw_next;
|
|
qh_from_schedule(pprev)->hw_next = pqh->phys_addr;
|
|
}
|
|
}
|
|
for(i = start_frame + (interval >> 3); i < (LONG) ehci->frame_count; i += (interval >> 3))
|
|
{
|
|
pprev = list_head = &ehci->frame_list_cpu[i].td_link;
|
|
ListFirst(list_head, pthis);
|
|
|
|
while (pthis)
|
|
{
|
|
if (pthis == pnext)
|
|
{
|
|
break;
|
|
}
|
|
pprev = pthis;
|
|
ListNext(list_head, pthis, pthis);
|
|
}
|
|
|
|
pprev->Flink = &pqh->elem_head_link->sched_link;
|
|
if (pprev == list_head)
|
|
ehci->frame_list[i] = pqh->phys_addr;
|
|
else
|
|
qh_from_schedule(pprev)->hw_next = pqh->phys_addr;
|
|
}
|
|
}
|
|
|
|
if (need_fstn)
|
|
ehci_insert_fstn_schedule(ehci, purb);
|
|
|
|
return;
|
|
}
|
|
|
|
static VOID
|
|
ehci_remove_int_from_schedule(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PLIST_ENTRY pthis, list_head, pnext, pprev, pcur;
|
|
PEHCI_QH pqh, pqhprev;
|
|
|
|
ULONG interval, start_frame, start_uframe;
|
|
LONG i;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
interval = REAL_INTERVAL;
|
|
start_uframe = (purb->int_start_frame << 3) + pipe_content->start_uframe;
|
|
start_frame = purb->int_start_frame;
|
|
|
|
ListFirst(&purb->trasac_list, pthis);
|
|
if (pthis == NULL)
|
|
return;
|
|
|
|
pqh = qh_from_list_entry(pthis);
|
|
list_head = &purb->trasac_list;
|
|
|
|
if (IsListEmpty(list_head))
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
|
|
if (!pipe_content->speed_high)
|
|
{
|
|
interval = (interval << 3);
|
|
}
|
|
|
|
if (interval >= 256 * 8)
|
|
return;
|
|
|
|
if (interval < 16)
|
|
{
|
|
ListFirstPrev(&pqh->elem_head_link->sched_link, pprev);
|
|
RemoveEntryList(&pqh->elem_head_link->sched_link);
|
|
if (interval > 1)
|
|
{
|
|
pqhprev = qh_from_schedule(pprev);
|
|
pqhprev->hw_next = pqh->hw_next;
|
|
}
|
|
else
|
|
{
|
|
ListFirst(&ehci->frame_list_cpu[EHCI_SCHED_FSTN_INDEX].td_link, list_head);
|
|
if (elem_type(pprev, FALSE) == INIT_LIST_FLAG_FSTN)
|
|
{
|
|
fstn_from_schedule(list_head)->hw_next = pqh->hw_next;
|
|
}
|
|
else
|
|
{
|
|
qh_from_schedule(pprev)->hw_next = pqh->hw_next;
|
|
}
|
|
}
|
|
}
|
|
else if (interval >= 16)
|
|
{
|
|
ListFirst(list_head, pthis);
|
|
pthis = &pqh->elem_head_link->sched_link;
|
|
|
|
for(i = start_uframe; i < (LONG) (ehci->frame_count << 3); i += interval)
|
|
{
|
|
ListFirst(&ehci->frame_list_cpu[i].td_link, pcur);
|
|
pprev = NULL;
|
|
while (pthis != pcur && pcur)
|
|
{
|
|
ListNext(&ehci->frame_list_cpu[i].td_link, pcur, pnext);
|
|
pprev = pcur;
|
|
pcur = pnext;
|
|
}
|
|
|
|
if (pcur == NULL)
|
|
{
|
|
TRAP();
|
|
continue;
|
|
}
|
|
else if (pprev == NULL)
|
|
{
|
|
// the first one in the frame list
|
|
ehci->frame_list_cpu[i].td_link.Flink = pthis->Flink;
|
|
ehci->frame_list[i] = qh_from_schedule(pthis)->hw_next;
|
|
}
|
|
else
|
|
{
|
|
if (elem_type(pprev, FALSE) == INIT_LIST_FLAG_QH)
|
|
{
|
|
qh_from_schedule(pprev)->elem_head_link->sched_link.Flink =
|
|
pqh->elem_head_link->sched_link.Flink;
|
|
qh_from_schedule(pprev)->hw_next = pqh->hw_next;
|
|
}
|
|
else if (elem_type(pprev, FALSE) == INIT_LIST_FLAG_ITD)
|
|
{
|
|
itd_from_schedule(pprev)->elem_head_link->sched_link.Flink =
|
|
pqh->elem_head_link->sched_link.Flink;
|
|
itd_from_schedule(pprev)->hw_next = pqh->hw_next;
|
|
}
|
|
else if (elem_type(pprev, FALSE) == INIT_LIST_FLAG_SITD)
|
|
{
|
|
sitd_from_schedule(pprev)->elem_head_link->sched_link.Flink =
|
|
pqh->elem_head_link->sched_link.Flink;
|
|
sitd_from_schedule(pprev)->hw_next = pqh->hw_next;
|
|
}
|
|
else if (elem_type(pprev, FALSE) == INIT_LIST_FLAG_FSTN)
|
|
{
|
|
fstn_from_schedule(pprev)->elem_head_link->sched_link.Flink =
|
|
pqh->elem_head_link->sched_link.Flink;
|
|
fstn_from_schedule(pprev)->hw_next = pqh->hw_next;
|
|
}
|
|
else
|
|
TRAP();
|
|
}
|
|
}
|
|
}
|
|
|
|
ListFirstPrev(&purb->trasac_list, pprev);
|
|
if (elem_type_list_entry(pprev) == INIT_LIST_FLAG_FSTN)
|
|
ehci_remove_fstn_from_schedule(ehci, purb);
|
|
return;
|
|
}
|
|
|
|
static VOID
|
|
ehci_insert_iso_schedule(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PLIST_ENTRY pthis, list_head, pnext;
|
|
|
|
ULONG interval, start_frame;
|
|
LONG i;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
|
|
interval = 8;
|
|
if (pipe_content->speed_high)
|
|
interval = REAL_INTERVAL;
|
|
|
|
start_frame = purb->iso_start_frame;
|
|
|
|
ListFirst(&purb->trasac_list, pthis);
|
|
if (pthis == NULL)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
|
|
list_head = &purb->trasac_list;
|
|
if (IsListEmpty(list_head))
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
|
|
i = start_frame;
|
|
while (pthis)
|
|
{
|
|
if (pipe_content->speed_high)
|
|
{
|
|
itd_from_list_entry(pthis)->elem_head_link->sched_link.Flink =
|
|
ehci->frame_list_cpu[i].td_link.Flink;
|
|
itd_from_list_entry(pthis)->hw_next = ehci->frame_list[i];
|
|
|
|
ehci->frame_list[i] = itd_from_list_entry(pthis)->phys_addr;
|
|
ehci->frame_list_cpu[i].td_link.Flink = pthis;
|
|
}
|
|
else
|
|
{
|
|
sitd_from_list_entry(pthis)->elem_head_link->sched_link.Flink =
|
|
ehci->frame_list_cpu[i].td_link.Flink;
|
|
sitd_from_list_entry(pthis)->hw_next = ehci->frame_list[i];
|
|
|
|
ehci->frame_list[i] = sitd_from_list_entry(pthis)->phys_addr;
|
|
ehci->frame_list_cpu[i].td_link.Flink = pthis;
|
|
}
|
|
|
|
ListNext(list_head, pthis, pnext);
|
|
pthis = pnext;
|
|
|
|
if (interval <= 8)
|
|
i++;
|
|
else
|
|
i += (interval >> 3);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static VOID
|
|
ehci_remove_iso_from_schedule(PEHCI_DEV ehci, PURB purb)
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PLIST_ENTRY pthis, list_head, pnext, pprev, pcur;
|
|
|
|
ULONG interval, start_frame;
|
|
LONG i;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
|
|
interval = 8;
|
|
if (pipe_content->speed_high)
|
|
interval = REAL_INTERVAL;
|
|
|
|
start_frame = purb->iso_start_frame;
|
|
|
|
ListFirst(&purb->trasac_list, pthis);
|
|
if (pthis == NULL)
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
|
|
list_head = &purb->trasac_list;
|
|
if (IsListEmpty(list_head))
|
|
{
|
|
TRAP();
|
|
return;
|
|
}
|
|
|
|
i = start_frame;
|
|
while (pthis)
|
|
{
|
|
// for the possible existance of sitd back pointer, we can not use for(...)
|
|
ListFirst(&ehci->frame_list_cpu[i].td_link, pcur);
|
|
pprev = &ehci->frame_list_cpu[i].td_link;
|
|
while (pcur)
|
|
{
|
|
if (pcur != pthis)
|
|
{
|
|
ListNext(&ehci->frame_list_cpu[i].td_link, pcur, pnext);
|
|
pprev = pcur;
|
|
pcur = pnext;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (pcur == NULL)
|
|
{
|
|
TRAP();
|
|
}
|
|
pprev->Flink = pcur->Flink;
|
|
if (pprev != &ehci->frame_list_cpu[i].td_link)
|
|
qh_from_schedule(pprev)->hw_next = qh_from_schedule(pcur)->hw_next;
|
|
else
|
|
ehci->frame_list[i] = qh_from_schedule(pcur)->hw_next;
|
|
|
|
ListNext(list_head, pthis, pnext);
|
|
pthis = pnext;
|
|
|
|
if (interval <= 8)
|
|
i++;
|
|
else
|
|
i += (interval >> 3);
|
|
}
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
ehci_isr_removing_urb(PEHCI_DEV ehci, PURB purb, BOOLEAN doorbell_rings, ULONG cur_frame)
|
|
{
|
|
UCHAR type;
|
|
PLIST_ENTRY pthis;
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PEHCI_ITD_CONTENT pitd_content;
|
|
PEHCI_SITD_CONTENT psitd_content;
|
|
PEHCI_QH_CONTENT pqh_content;
|
|
PEHCI_QTD_CONTENT pqtd_content;
|
|
LONG i;
|
|
|
|
if (purb == NULL || ehci == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if ((purb->flags & URB_FLAG_STATE_MASK) == URB_FLAG_STATE_FINISHED)
|
|
return STATUS_SUCCESS;
|
|
|
|
type = 0;
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
|
|
switch (purb->flags & URB_FLAG_STATE_MASK)
|
|
{
|
|
case URB_FLAG_STATE_IN_PROCESS:
|
|
{
|
|
// determine the removal type: complete, error or cancel
|
|
ListFirst(&purb->trasac_list, pthis);
|
|
if (purb->flags & URB_FLAG_FORCE_CANCEL)
|
|
{
|
|
type = 3;
|
|
}
|
|
else
|
|
{
|
|
if (pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_INT ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL)
|
|
{
|
|
pqh_content =
|
|
(PEHCI_QH_CONTENT) ((ULONG) struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link)->
|
|
phys_part & PHYS_PART_ADDR_MASK);
|
|
if (EHCI_QH_ERROR(pqh_content))
|
|
{
|
|
purb->status = pqh_content->cur_qtd.status;
|
|
type = 2;
|
|
}
|
|
else
|
|
{
|
|
pqtd_content = &pqh_content->cur_qtd;
|
|
if (pqtd_content->terminal && ((pqtd_content->status & QTD_STS_ACTIVE) == 0))
|
|
{
|
|
type = 1;
|
|
}
|
|
// else, not finished
|
|
}
|
|
}
|
|
else if (pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC)
|
|
{
|
|
// FIXME: do we need to check if current frame falls out of the
|
|
// frame range of iso transfer
|
|
// inspect the last td to determine if finished
|
|
ListFirstPrev(&purb->trasac_list, pthis);
|
|
if (pthis)
|
|
{
|
|
if (pipe_content->speed_high)
|
|
{
|
|
pitd_content =
|
|
(PEHCI_ITD_CONTENT) ((ULONG) struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link)->
|
|
phys_part & PHYS_PART_ADDR_MASK);
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
if (pitd_content->status_slot[i].trans_length &&
|
|
pitd_content->status_slot[i].status & 0x08)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == 8)
|
|
{
|
|
// the itds are all inactive
|
|
type = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psitd_content =
|
|
(PEHCI_SITD_CONTENT) ((ULONG) struct_ptr(pthis, EHCI_ELEM_LINKS, elem_link)->
|
|
phys_part & PHYS_PART_ADDR_MASK);
|
|
if ((psitd_content->status & 0x80) == 0)
|
|
{
|
|
type = 1;
|
|
}
|
|
}
|
|
}
|
|
else // empty transaction list in purb
|
|
TRAP();
|
|
}
|
|
else // unknown transfer type
|
|
TRAP();
|
|
|
|
} // end of not force cancel
|
|
|
|
if (type == 0)
|
|
return STATUS_SUCCESS;
|
|
|
|
switch (type)
|
|
{
|
|
case 1:
|
|
{
|
|
if (pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_BULK)
|
|
{
|
|
ehci_remove_bulk_from_schedule(ehci, purb);
|
|
purb->flags &= ~URB_FLAG_STATE_MASK;
|
|
purb->flags |= URB_FLAG_STATE_DOORBELL;
|
|
purb->status = 0;
|
|
press_doorbell(ehci);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC)
|
|
{
|
|
ehci_remove_iso_from_schedule(ehci, purb);
|
|
}
|
|
else if (pipe_content->trans_type == USB_ENDPOINT_XFER_INT)
|
|
{
|
|
ehci_remove_int_from_schedule(ehci, purb);
|
|
}
|
|
else // unknown transfer type
|
|
TRAP();
|
|
|
|
purb->flags &= ~URB_FLAG_STATE_MASK;
|
|
purb->flags |= URB_FLAG_STATE_FINISHED;
|
|
|
|
// notify dpc the purb can be completed;
|
|
purb->flags &= ~URB_FLAG_IN_SCHEDULE;
|
|
purb->status = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
case 2:
|
|
{
|
|
if (pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_BULK)
|
|
{
|
|
ehci_deactivate_urb(purb);
|
|
ehci_remove_bulk_from_schedule(ehci, purb);
|
|
purb->flags &= ~URB_FLAG_STATE_MASK;
|
|
purb->flags |= URB_FLAG_STATE_DOORBELL;
|
|
press_doorbell(ehci);
|
|
}
|
|
else if (pipe_content->trans_type == USB_ENDPOINT_XFER_INT)
|
|
{
|
|
ehci_remove_int_from_schedule(ehci, purb);
|
|
|
|
purb->flags &= ~URB_FLAG_STATE_MASK;
|
|
purb->flags |= URB_FLAG_STATE_FINISHED;
|
|
purb->flags &= ~URB_FLAG_IN_SCHEDULE;
|
|
}
|
|
else // unknown transfer or iso transfer
|
|
TRAP();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
case 3:
|
|
{
|
|
if (pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_INT)
|
|
{
|
|
ehci_deactivate_urb(purb);
|
|
if (pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ||
|
|
pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL)
|
|
ehci_remove_bulk_from_schedule(ehci, purb);
|
|
else
|
|
ehci_remove_int_from_schedule(ehci, purb);
|
|
|
|
purb->flags &= ~URB_FLAG_STATE_MASK;
|
|
purb->flags |= URB_FLAG_STATE_DOORBELL;
|
|
|
|
press_doorbell(ehci);
|
|
|
|
}
|
|
else // unknown transfer or iso transfer
|
|
DO_NOTHING;
|
|
purb->status = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
default:
|
|
TRAP();
|
|
}
|
|
}
|
|
case URB_FLAG_STATE_DOORBELL:
|
|
{
|
|
if (doorbell_rings == FALSE)
|
|
return STATUS_SUCCESS;
|
|
|
|
purb->flags &= ~URB_FLAG_STATE_MASK;
|
|
purb->flags |= URB_FLAG_STATE_FINISHED;
|
|
purb->flags &= ~URB_FLAG_IN_SCHEDULE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static ULONG
|
|
ehci_scan_iso_error(PEHCI_DEV ehci, PURB purb)
|
|
// we only report the first error of the ITDs, purb->status is the status code
|
|
// return the raw status for ehci_set_error_code
|
|
{
|
|
PURB_HS_PIPE_CONTENT pipe_content;
|
|
PEHCI_SITD_CONTENT psitd_content;
|
|
PEHCI_ITD_CONTENT pitd_content;
|
|
PLIST_ENTRY pthis, pnext;
|
|
LONG i;
|
|
|
|
if (ehci == NULL || purb == NULL)
|
|
return 0;
|
|
|
|
pipe_content = (PURB_HS_PIPE_CONTENT) & purb->pipe;
|
|
if (pipe_content->trans_type != USB_ENDPOINT_XFER_ISOC)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ListFirst(&purb->trasac_list, pthis);
|
|
if (pipe_content->speed_high)
|
|
{
|
|
while (pthis)
|
|
{
|
|
pitd_content = (PEHCI_ITD_CONTENT) itd_from_list_entry(pthis);
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
if (pitd_content->status_slot[i].status & ITD_ANY_ERROR)
|
|
break;
|
|
}
|
|
if (i < 8)
|
|
{
|
|
// error occured
|
|
return purb->status = pitd_content->status_slot[i].status;
|
|
}
|
|
ListNext(&purb->trasac_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (pthis)
|
|
{
|
|
psitd_content = (PEHCI_SITD_CONTENT) sitd_from_list_entry(pthis);
|
|
if (psitd_content->status & SITD_ANY_ERROR)
|
|
{
|
|
// error occured
|
|
if (psitd_content->s_mask == 0x04 &&
|
|
psitd_content->c_mask == 0x70 && psitd_content->bytes_to_transfer == 1)
|
|
return purb->status = 0;
|
|
|
|
return purb->status = psitd_content->status;
|
|
}
|
|
ListNext(&purb->trasac_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
ehci_isr(PKINTERRUPT interrupt, PVOID context)
|
|
// we can not use endp here for it is within the dev scope, and
|
|
// we can not acquire the dev-lock, fortunately we saved some
|
|
// info in purb->pipe in ehci_internal_submit_XXX.
|
|
{
|
|
|
|
PEHCI_DEV ehci;
|
|
ULONG status;
|
|
#if DBG
|
|
ULONG urb_count;
|
|
#endif
|
|
PLIST_ENTRY pthis, pnext;
|
|
PURB purb;
|
|
BOOLEAN door_bell_rings;
|
|
ULONG cur_frame;
|
|
/*
|
|
* Read the interrupt status, and write it back to clear the
|
|
* interrupt cause
|
|
*/
|
|
ehci = (PEHCI_DEV) context;
|
|
if (ehci == NULL)
|
|
return FALSE;
|
|
|
|
status = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + EHCI_USBSTS));
|
|
cur_frame = EHCI_READ_PORT_ULONG((PULONG) (ehci->port_base + EHCI_FRINDEX));
|
|
|
|
status &= (EHCI_ERROR_INT | STS_INT | STS_IAA);
|
|
if (!status) /* shared interrupt, not mine */
|
|
{
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_isr(): not our int\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
/* clear it */
|
|
EHCI_WRITE_PORT_ULONG((PULONG) (ehci->port_base + EHCI_USBSTS), status);
|
|
|
|
if (status & EHCI_ERROR_INT)
|
|
{
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_isr(): current ehci status=0x%x\n", status));
|
|
}
|
|
else
|
|
{
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_isr(): congratulations, no error occurs\n"));
|
|
}
|
|
|
|
|
|
if (status & STS_FATAL)
|
|
{
|
|
DbgPrint("ehci_isr(): host system error, PCI problems?\n");
|
|
for(;;);
|
|
|
|
}
|
|
|
|
if (status & STS_HALT) //&& !ehci->is_suspended
|
|
{
|
|
DbgPrint("ehci_isr(): host controller halted. very bad\n");
|
|
/* FIXME: Reset the controller, fix the offending TD */
|
|
// reset is performed in dpc
|
|
}
|
|
|
|
|
|
door_bell_rings = ((status & STS_IAA) != 0);
|
|
|
|
// scan to remove those due
|
|
#if DBG
|
|
urb_count = dbg_count_list(&ehci->urb_list);
|
|
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_isr(): urb# in process is %d\n", urb_count));
|
|
#endif
|
|
|
|
ListFirst(&ehci->urb_list, pthis);
|
|
while (pthis)
|
|
{
|
|
purb = (PURB) pthis;
|
|
ehci_isr_removing_urb(ehci, purb, door_bell_rings, cur_frame);
|
|
ListNext(&ehci->urb_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
|
|
KeInsertQueueDpc(&ehci->pdev_ext->ehci_dpc, (PVOID) status, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
#ifndef INCLUDE_EHCI
|
|
VOID
|
|
ehci_unload(IN PDRIVER_OBJECT DriverObject)
|
|
{
|
|
PDEVICE_OBJECT pdev;
|
|
PEHCI_DEVICE_EXTENSION pdev_ext;
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
LONG i;
|
|
|
|
pdev = DriverObject->DeviceObject;
|
|
|
|
if (pdev == NULL)
|
|
return;
|
|
|
|
pdev_ext = pdev->DeviceExtension;
|
|
if (pdev_ext == NULL)
|
|
return;
|
|
|
|
dev_mgr = &g_dev_mgr;
|
|
if (dev_mgr == NULL)
|
|
return;
|
|
//
|
|
// set the termination flag
|
|
//
|
|
dev_mgr->term_flag = TRUE;
|
|
//
|
|
// wake up the thread if it is
|
|
//
|
|
KeSetEvent(&dev_mgr->wake_up_event, 0, FALSE);
|
|
KeWaitForSingleObject(dev_mgr->pthread, Executive, KernelMode, TRUE, NULL);
|
|
ObDereferenceObject(dev_mgr->pthread);
|
|
dev_mgr->pthread = NULL;
|
|
|
|
dev_mgr_release_hcd(dev_mgr);
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
generic_dispatch_irp(IN PDEVICE_OBJECT dev_obj, IN PIRP irp)
|
|
{
|
|
PDEVEXT_HEADER dev_ext;
|
|
|
|
dev_ext = (PDEVEXT_HEADER) dev_obj->DeviceExtension;
|
|
|
|
if (dev_ext && dev_ext->dispatch)
|
|
return dev_ext->dispatch(dev_obj, irp);
|
|
|
|
irp->IoStatus.Information = 0;
|
|
|
|
EXIT_DISPATCH(STATUS_UNSUCCESSFUL, irp);
|
|
}
|
|
|
|
VOID
|
|
generic_start_io(IN PDEVICE_OBJECT dev_obj, IN PIRP irp)
|
|
{
|
|
PDEVEXT_HEADER dev_ext;
|
|
|
|
KIRQL old_irql;
|
|
|
|
IoAcquireCancelSpinLock(&old_irql);
|
|
if (irp != dev_obj->CurrentIrp || irp->Cancel)
|
|
{
|
|
IoReleaseCancelSpinLock(old_irql);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
IoSetCancelRoutine(irp, NULL);
|
|
IoReleaseCancelSpinLock(old_irql);
|
|
}
|
|
|
|
dev_ext = (PDEVEXT_HEADER) dev_obj->DeviceExtension;
|
|
|
|
if (dev_ext && dev_ext->start_io)
|
|
{
|
|
dev_ext->start_io(dev_obj, irp);
|
|
return;
|
|
}
|
|
|
|
irp->IoStatus.Information = 0;
|
|
irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
IoStartNextPacket(dev_obj, FALSE);
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
BOOLEAN fRes;
|
|
|
|
#if DBG
|
|
// should be done before any debug output is done.
|
|
// read our debug verbosity level from the registry
|
|
//NetacOD_GetRegistryDword( NetacOD_REGISTRY_PARAMETERS_PATH, //absolute registry path
|
|
// L"DebugLevel", // REG_DWORD ValueName
|
|
// &gDebugLevel ); // Value receiver
|
|
|
|
// debug_level = DBGLVL_MAXIMUM;
|
|
#endif
|
|
|
|
ehci_dbg_print_cond(DBGLVL_MINIMUM, DEBUG_UHCI,
|
|
("Entering DriverEntry(), RegistryPath=\n %ws\n", RegistryPath->Buffer));
|
|
|
|
// Remember our driver object, for when we create our child PDO
|
|
usb_driver_obj = DriverObject;
|
|
|
|
//
|
|
// Create dispatch points for create, close, unload
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = generic_dispatch_irp;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = generic_dispatch_irp;
|
|
DriverObject->DriverUnload = ehci_unload;
|
|
|
|
// User mode DeviceIoControl() calls will be routed here
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = generic_dispatch_irp;
|
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = generic_dispatch_irp;
|
|
|
|
// User mode ReadFile()/WriteFile() calls will be routed here
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = generic_dispatch_irp;
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = generic_dispatch_irp;
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = generic_dispatch_irp;
|
|
DriverObject->MajorFunction[IRP_MJ_SCSI] = generic_dispatch_irp;
|
|
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = generic_dispatch_irp;
|
|
|
|
DriverObject->DriverStartIo = generic_start_io;
|
|
// routines for handling system PNP and power management requests
|
|
//DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = generic_dispatch_irp;
|
|
|
|
// The Functional Device Object (FDO) will not be created for PNP devices until
|
|
// this routine is called upon device plug-in.
|
|
RtlZeroMemory(&g_dev_mgr, sizeof(USB_DEV_MANAGER));
|
|
g_dev_mgr.usb_driver_obj = DriverObject;
|
|
|
|
ehci_probe(DriverObject, RegistryPath, &g_dev_mgr);
|
|
|
|
if (dev_mgr_strobe(&g_dev_mgr) == FALSE)
|
|
{
|
|
dev_mgr_release_hcd(&g_dev_mgr);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
dev_mgr_start_hcd(&g_dev_mgr);
|
|
ehci_dbg_print_cond(DBGLVL_DEFAULT, DEBUG_UHCI, ("DriverEntry(): exiting... (%x)\n", ntStatus));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
//note: the initialization will be in the following order
|
|
// dev_mgr_strobe
|
|
// ehci_start
|
|
|
|
// to kill dev_mgr_thread:
|
|
// dev_mgr->term_flag = TRUE;
|
|
// KeSetEvent( &dev_mgr->wake_up_event );
|
|
// this piece of code must run at passive-level
|
|
//
|
|
//
|