mirror of
https://github.com/reactos/reactos.git
synced 2024-11-11 01:04:11 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
1563 lines
43 KiB
C
1563 lines
43 KiB
C
/**
|
|
* devmgr.c - USB driver stack project for Windows NT 4.0
|
|
*
|
|
* Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
|
|
*
|
|
* This program/include file is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as published
|
|
* by the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program/include file is distributed in the hope that it will be
|
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program (in the main directory of the distribution, the file
|
|
* COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
|
|
* Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "usbdriver.h"
|
|
|
|
#define realloc_buf( pdEV, puRB ) \
|
|
{\
|
|
PBYTE data_buf;\
|
|
int i;\
|
|
data_buf = usb_alloc_mem( NonPagedPool, ( pdEV )->desc_buf_size += 1024 );\
|
|
if (!data_buf)\
|
|
{\
|
|
goto LBL_OUT;\
|
|
}\
|
|
RtlZeroMemory( data_buf, ( pdEV )->desc_buf_size );\
|
|
for( i = 0; i < ( LONG )( puRB )->context; i++ )\
|
|
{\
|
|
data_buf[ i ] = ( pdEV )->desc_buf[ i ];\
|
|
}\
|
|
usb_free_mem( ( pdEV )->desc_buf );\
|
|
( pdEV )->desc_buf = data_buf;\
|
|
( pdEV )->pusb_dev_desc = ( PUSB_DEVICE_DESC )( pdEV )->desc_buf;\
|
|
( puRB )->data_buffer = &data_buf[ ( LONG ) ( puRB )->context ];\
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------
|
|
|
|
USB_DRIVER g_driver_list[DEVMGR_MAX_DRIVERS];
|
|
USB_DEV_MANAGER g_dev_mgr;
|
|
|
|
|
|
//----------------------------------------------------------
|
|
BOOLEAN
|
|
dev_mgr_set_if_driver(PUSB_DEV_MANAGER dev_mgr,
|
|
DEV_HANDLE if_handle,
|
|
PUSB_DRIVER pdriver,
|
|
PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle, and must have dev_lock acquired.
|
|
)
|
|
{
|
|
ULONG i;
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (dev_mgr == NULL || if_handle == 0 || pdriver == NULL)
|
|
return FALSE;
|
|
|
|
i = if_idx_from_handle(if_handle);
|
|
if (pdev != NULL)
|
|
{
|
|
if (dev_state(pdev) < USB_DEV_STATE_BEFORE_ZOMB)
|
|
{
|
|
pdev->usb_config->interf[i].pif_drv = pdriver;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (usb_query_and_lock_dev(dev_mgr, if_handle, &pdev) != STATUS_SUCCESS)
|
|
return FALSE;
|
|
|
|
lock_dev(pdev, TRUE);
|
|
if (dev_state(pdev) != USB_DEV_STATE_ZOMB)
|
|
{
|
|
pdev->usb_config->interf[i].pif_drv = pdriver;
|
|
}
|
|
unlock_dev(pdev, TRUE);
|
|
usb_unlock_dev(pdev);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_set_driver(PUSB_DEV_MANAGER dev_mgr,
|
|
DEV_HANDLE dev_handle,
|
|
PUSB_DRIVER pdriver,
|
|
PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle
|
|
)
|
|
{
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (dev_mgr == NULL || dev_handle == 0 || pdriver == NULL)
|
|
return FALSE;
|
|
|
|
if (pdev != NULL)
|
|
{
|
|
if (dev_state(pdev) < USB_DEV_STATE_BEFORE_ZOMB)
|
|
{
|
|
pdev->dev_driver = pdriver;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev) != STATUS_SUCCESS)
|
|
return FALSE;
|
|
|
|
lock_dev(pdev, FALSE);
|
|
if (dev_state(pdev) < USB_DEV_STATE_BEFORE_ZOMB)
|
|
{
|
|
pdev->dev_driver = pdriver;
|
|
}
|
|
unlock_dev(pdev, FALSE);
|
|
usb_unlock_dev(pdev);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_post_event(PUSB_DEV_MANAGER dev_mgr, PUSB_EVENT event)
|
|
{
|
|
KIRQL old_irql;
|
|
|
|
if (dev_mgr == NULL || event == NULL)
|
|
return FALSE;
|
|
|
|
KeAcquireSpinLock(&dev_mgr->event_list_lock, &old_irql);
|
|
InsertTailList(&dev_mgr->event_list, &event->event_link);
|
|
KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql);
|
|
|
|
KeSetEvent(&dev_mgr->wake_up_event, 0, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
dev_mgr_driver_entry_init(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdrvr)
|
|
{
|
|
// Device Info
|
|
|
|
RtlZeroMemory(pdrvr, sizeof(USB_DRIVER) * DEVMGR_MAX_DRIVERS);
|
|
|
|
pdrvr[RH_DRIVER_IDX].driver_init = rh_driver_init; // in fact, this routine will init the rh device rather that the driver struct.
|
|
pdrvr[RH_DRIVER_IDX].driver_destroy = rh_driver_destroy; // we do not need rh to destroy currently, since that may means fatal hardware failure
|
|
|
|
pdrvr[HUB_DRIVER_IDX].driver_init = hub_driver_init; //no need, since dev_mgr is also a hub driver
|
|
pdrvr[HUB_DRIVER_IDX].driver_destroy = hub_driver_destroy;
|
|
|
|
pdrvr[UMSS_DRIVER_IDX].driver_init = umss_if_driver_init;
|
|
pdrvr[UMSS_DRIVER_IDX].driver_destroy = umss_if_driver_destroy;
|
|
|
|
pdrvr[COMP_DRIVER_IDX].driver_init = compdev_driver_init;
|
|
pdrvr[COMP_DRIVER_IDX].driver_destroy = compdev_driver_destroy;
|
|
|
|
pdrvr[GEN_DRIVER_IDX].driver_init = gendrv_driver_init;
|
|
pdrvr[GEN_DRIVER_IDX].driver_destroy = gendrv_driver_destroy;
|
|
|
|
pdrvr[GEN_IF_DRIVER_IDX].driver_init = gendrv_if_driver_init;
|
|
pdrvr[GEN_IF_DRIVER_IDX].driver_destroy = gendrv_if_driver_destroy;
|
|
|
|
pdrvr[MOUSE_DRIVER_IDX].driver_init = mouse_driver_init;
|
|
pdrvr[MOUSE_DRIVER_IDX].driver_destroy = mouse_driver_destroy;
|
|
|
|
pdrvr[KEYBOARD_DRIVER_IDX].driver_init = kbd_driver_init;
|
|
pdrvr[KEYBOARD_DRIVER_IDX].driver_destroy = kbd_driver_destroy;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_strobe(PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
PUSB_EVENT pevent;
|
|
HANDLE thread_handle;
|
|
|
|
if (dev_mgr == NULL)
|
|
return FALSE;
|
|
if (dev_mgr->hcd_count == 0)
|
|
return FALSE;
|
|
|
|
dev_mgr->term_flag = FALSE;
|
|
|
|
if (dev_mgr->hcd_count == 0)
|
|
return FALSE;
|
|
|
|
KeInitializeSpinLock(&dev_mgr->event_list_lock);
|
|
InitializeListHead(&dev_mgr->event_list);
|
|
init_event_pool(&dev_mgr->event_pool);
|
|
|
|
pevent = alloc_event(&dev_mgr->event_pool, 1);
|
|
if (pevent == NULL)
|
|
{
|
|
destroy_event_pool(&dev_mgr->event_pool);
|
|
return FALSE;
|
|
}
|
|
|
|
pevent->flags = USB_EVENT_FLAG_ACTIVE;
|
|
pevent->event = USB_EVENT_INIT_DEV_MGR;
|
|
|
|
pevent->process_queue = event_list_default_process_queue;
|
|
pevent->process_event = (PROCESS_EVENT)dev_mgr_event_init;
|
|
|
|
pevent->context = (ULONG) dev_mgr;
|
|
|
|
KeInitializeEvent(&dev_mgr->wake_up_event, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&dev_mgr->drivers_inited, NotificationEvent, FALSE);
|
|
|
|
InsertTailList(&dev_mgr->event_list, &pevent->event_link);
|
|
|
|
if (PsCreateSystemThread(&thread_handle, 0, NULL, NULL, NULL, dev_mgr_thread, dev_mgr) != STATUS_SUCCESS)
|
|
{
|
|
destroy_event_pool(&dev_mgr->event_pool);
|
|
return FALSE;
|
|
}
|
|
|
|
ObReferenceObjectByHandle(thread_handle,
|
|
THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID *) & dev_mgr->pthread, NULL);
|
|
|
|
ZwClose(thread_handle);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_event_init(PUSB_DEV pdev, //always null. we do not use this param
|
|
ULONG event, ULONG context, ULONG param)
|
|
{
|
|
LARGE_INTEGER due_time;
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
LONG i;
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("dev_mgr_event_init(): dev_mgr=0x%x, event=0x%x\n", context, event));
|
|
dev_mgr = (PUSB_DEV_MANAGER) context;
|
|
if (dev_mgr == NULL)
|
|
return FALSE;
|
|
|
|
if (event != USB_EVENT_INIT_DEV_MGR)
|
|
return FALSE;
|
|
|
|
//dev_mgr->root_hub = NULL;
|
|
KeInitializeTimer(&dev_mgr->dev_mgr_timer);
|
|
|
|
KeInitializeDpc(&dev_mgr->dev_mgr_timer_dpc, dev_mgr_timer_dpc_callback, (PVOID) dev_mgr);
|
|
|
|
KeInitializeSpinLock(&dev_mgr->timer_svc_list_lock);
|
|
InitializeListHead(&dev_mgr->timer_svc_list);
|
|
init_timer_svc_pool(&dev_mgr->timer_svc_pool);
|
|
dev_mgr->timer_click = 0;
|
|
|
|
init_irp_list(&dev_mgr->irp_list);
|
|
|
|
KeInitializeSpinLock(&dev_mgr->dev_list_lock);
|
|
InitializeListHead(&dev_mgr->dev_list);
|
|
|
|
dev_mgr->hub_count = 0;
|
|
InitializeListHead(&dev_mgr->hub_list);
|
|
|
|
dev_mgr->conn_count = 0;
|
|
dev_mgr->driver_list = g_driver_list;
|
|
|
|
dev_mgr_driver_entry_init(dev_mgr, dev_mgr->driver_list);
|
|
|
|
for(i = 0; i < DEVMGR_MAX_DRIVERS; i++)
|
|
{
|
|
if (dev_mgr->driver_list[i].driver_init == NULL)
|
|
continue;
|
|
|
|
if (dev_mgr->driver_list[i].driver_init(dev_mgr, &dev_mgr->driver_list[i]) == FALSE)
|
|
break;
|
|
}
|
|
if (i == DEVMGR_MAX_DRIVERS)
|
|
{
|
|
due_time.QuadPart = -(DEV_MGR_TIMER_INTERVAL_NS - 10);
|
|
|
|
KeSetTimerEx(&dev_mgr->dev_mgr_timer,
|
|
due_time, DEV_MGR_TIMER_INTERVAL_MS, &dev_mgr->dev_mgr_timer_dpc);
|
|
|
|
/* Signal we're done initing */
|
|
KeSetEvent(&dev_mgr->drivers_inited, 0, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
i--;
|
|
|
|
for(; i >= 0; i--)
|
|
{
|
|
if (dev_mgr->driver_list[i].driver_destroy)
|
|
dev_mgr->driver_list[i].driver_destroy(dev_mgr, &dev_mgr->driver_list[i]);
|
|
}
|
|
|
|
KeCancelTimer(&dev_mgr->dev_mgr_timer);
|
|
KeRemoveQueueDpc(&dev_mgr->dev_mgr_timer_dpc);
|
|
KeSetEvent(&dev_mgr->drivers_inited, 0, FALSE);
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
VOID
|
|
dev_mgr_destroy(PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
LONG i;
|
|
// oops...
|
|
KeCancelTimer(&dev_mgr->dev_mgr_timer);
|
|
KeRemoveQueueDpc(&dev_mgr->dev_mgr_timer_dpc);
|
|
|
|
for(i = DEVMGR_MAX_DRIVERS - 1; i >= 0; i--)
|
|
dev_mgr->driver_list[i].driver_destroy(dev_mgr, &dev_mgr->driver_list[i]);
|
|
|
|
destroy_irp_list(&dev_mgr->irp_list);
|
|
destroy_timer_svc_pool(&dev_mgr->timer_svc_pool);
|
|
destroy_event_pool(&dev_mgr->event_pool);
|
|
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
dev_mgr_thread(PVOID context)
|
|
{
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PUSB_EVENT pevent;
|
|
PLIST_ENTRY pthis, pnext;
|
|
USB_EVENT usb_event;
|
|
LARGE_INTEGER time_out;
|
|
NTSTATUS status;
|
|
BOOLEAN dev_mgr_inited;
|
|
KIRQL old_irql;
|
|
LONG i;
|
|
|
|
dev_mgr = (PUSB_DEV_MANAGER) context;
|
|
dev_mgr_inited = FALSE;
|
|
usb_cal_cpu_freq();
|
|
time_out.u.LowPart = (10 * 1000 * 1000) * 100 - 1; //1 minutes
|
|
time_out.u.HighPart = 0;
|
|
time_out.QuadPart = -time_out.QuadPart;
|
|
|
|
//usb_dbg_print( DBGLVL_MAXIMUM + 1, ( "dev_mgr_thread(): current uhci status=0x%x\n", uhci_status( dev_mgr->pdev_ext->uhci ) ) );
|
|
|
|
while (dev_mgr->term_flag == FALSE)
|
|
{
|
|
KeAcquireSpinLock(&dev_mgr->event_list_lock, &old_irql);
|
|
if (IsListEmpty(&dev_mgr->event_list) == TRUE)
|
|
{
|
|
KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql);
|
|
status = KeWaitForSingleObject(&dev_mgr->wake_up_event, Executive, KernelMode, TRUE, &time_out);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_thread(): current element in event list is 0x%x\n", \
|
|
dbg_count_list( &dev_mgr->event_list ) ) ); */
|
|
|
|
dev_mgr_inited = TRUE; //since we have post one event, if this statement is executed, dev_mgr_event_init must be called sometime later or earlier
|
|
|
|
ListFirst(&dev_mgr->event_list, pthis);
|
|
pevent = struct_ptr(pthis, USB_EVENT, event_link);
|
|
|
|
while (pevent && ((pevent->flags & USB_EVENT_FLAG_ACTIVE) == 0))
|
|
{
|
|
//skip inactive ones
|
|
ListNext(&dev_mgr->event_list, &pevent->event_link, pnext);
|
|
pevent = struct_ptr(pnext, USB_EVENT, event_link);
|
|
}
|
|
|
|
if (pevent != NULL)
|
|
{
|
|
if (pevent->process_queue == NULL)
|
|
pevent->process_queue = event_list_default_process_queue;
|
|
|
|
pevent->process_queue(&dev_mgr->event_list, &dev_mgr->event_pool, pevent, &usb_event);
|
|
}
|
|
else
|
|
{
|
|
//no active event
|
|
KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql);
|
|
status = KeWaitForSingleObject(&dev_mgr->wake_up_event, Executive, KernelMode, TRUE, &time_out // 10 minutes
|
|
);
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("dev_mgr_thread(): wake up, reason=0x%x\n", status));
|
|
continue;
|
|
}
|
|
|
|
KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql);
|
|
|
|
if (usb_event.process_event)
|
|
{
|
|
usb_event.process_event(usb_event.pdev, usb_event.event, usb_event.context, usb_event.param);
|
|
}
|
|
else
|
|
{
|
|
event_list_default_process_event(usb_event.pdev,
|
|
usb_event.event, usb_event.context, usb_event.param);
|
|
}
|
|
}
|
|
|
|
if (dev_mgr_inited)
|
|
{
|
|
for(i = 0; i < dev_mgr->hcd_count; i++)
|
|
dev_mgr_disconnect_dev(dev_mgr->hcd_array[i]->hcd_get_root_hub(dev_mgr->hcd_array[i]));
|
|
dev_mgr_destroy(dev_mgr);
|
|
}
|
|
PsTerminateSystemThread(0);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
dev_mgr_timer_dpc_callback(PKDPC Dpc, PVOID context, PVOID SystemArgument1, PVOID SystemArgument2)
|
|
{
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
LIST_HEAD templist;
|
|
PLIST_ENTRY pthis, pnext;
|
|
static ULONG ticks = 0;
|
|
|
|
ticks++;
|
|
dev_mgr = (PUSB_DEV_MANAGER) context;
|
|
if (dev_mgr == NULL)
|
|
return;
|
|
|
|
dev_mgr->timer_click++;
|
|
InitializeListHead(&templist);
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&dev_mgr->timer_svc_list_lock);
|
|
if (IsListEmpty(&dev_mgr->timer_svc_list) == TRUE)
|
|
{
|
|
KeReleaseSpinLockFromDpcLevel(&dev_mgr->timer_svc_list_lock);
|
|
return;
|
|
}
|
|
|
|
ListFirst(&dev_mgr->timer_svc_list, pthis);
|
|
while (pthis)
|
|
{
|
|
((PTIMER_SVC) pthis)->counter++;
|
|
ListNext(&dev_mgr->timer_svc_list, pthis, pnext);
|
|
if (((PTIMER_SVC) pthis)->counter >= ((PTIMER_SVC) pthis)->threshold)
|
|
{
|
|
RemoveEntryList(pthis);
|
|
InsertTailList(&templist, pthis);
|
|
}
|
|
pthis = pnext;
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&dev_mgr->timer_svc_list_lock);
|
|
|
|
|
|
while (IsListEmpty(&templist) == FALSE)
|
|
{
|
|
pthis = RemoveHeadList(&templist);
|
|
((PTIMER_SVC) pthis)->func(((PTIMER_SVC) pthis)->pdev, (PVOID) ((PTIMER_SVC) pthis)->context);
|
|
KeAcquireSpinLockAtDpcLevel(&dev_mgr->timer_svc_list_lock);
|
|
free_timer_svc(&dev_mgr->timer_svc_pool, (PTIMER_SVC) pthis);
|
|
KeReleaseSpinLockFromDpcLevel(&dev_mgr->timer_svc_list_lock);
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_request_timer_svc(PUSB_DEV_MANAGER dev_mgr,
|
|
PUSB_DEV pdev, ULONG context, ULONG due_time, TIMER_SVC_HANDLER handler)
|
|
{
|
|
PTIMER_SVC timer_svc;
|
|
KIRQL old_irql;
|
|
|
|
if (dev_mgr == NULL || pdev == NULL || due_time == 0 || handler == NULL)
|
|
return FALSE;
|
|
|
|
KeAcquireSpinLock(&dev_mgr->timer_svc_list_lock, &old_irql);
|
|
timer_svc = alloc_timer_svc(&dev_mgr->timer_svc_pool, 1);
|
|
if (timer_svc == NULL)
|
|
{
|
|
KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql);
|
|
return FALSE;
|
|
}
|
|
timer_svc->pdev = pdev;
|
|
timer_svc->threshold = due_time;
|
|
timer_svc->func = handler;
|
|
timer_svc->counter = 0;
|
|
|
|
InsertTailList(&dev_mgr->timer_svc_list, &timer_svc->timer_svc_link);
|
|
KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql);
|
|
return TRUE;
|
|
}
|
|
|
|
BYTE
|
|
dev_mgr_alloc_addr(PUSB_DEV_MANAGER dev_mgr, PHCD hcd)
|
|
{
|
|
// alloc a usb addr for the device within 1-128
|
|
if (dev_mgr == NULL || hcd == NULL)
|
|
return 0xff;
|
|
|
|
return hcd->hcd_alloc_addr(hcd);
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_free_addr(PUSB_DEV_MANAGER dev_mgr, PUSB_DEV pdev, BYTE addr)
|
|
{
|
|
PHCD hcd;
|
|
if (addr & 0x80)
|
|
return FALSE;
|
|
|
|
if (dev_mgr == NULL || pdev == NULL)
|
|
return FALSE;
|
|
|
|
hcd = pdev->hcd;
|
|
if (hcd == NULL)
|
|
return FALSE;
|
|
hcd->hcd_free_addr(hcd, addr);
|
|
return TRUE;
|
|
}
|
|
|
|
PUSB_DEV
|
|
dev_mgr_alloc_device(PUSB_DEV_MANAGER dev_mgr, PHCD hcd)
|
|
{
|
|
BYTE addr;
|
|
PUSB_DEV pdev;
|
|
|
|
if ((addr = dev_mgr_alloc_addr(dev_mgr, hcd)) == 0xff)
|
|
return NULL;
|
|
|
|
pdev = usb_alloc_mem(NonPagedPool, sizeof(USB_DEV));
|
|
if (pdev == NULL)
|
|
return NULL;
|
|
|
|
RtlZeroMemory(pdev, sizeof(USB_DEV));
|
|
|
|
KeInitializeSpinLock(&pdev->dev_lock);
|
|
dev_mgr->conn_count++;
|
|
|
|
pdev->flags = USB_DEV_STATE_RESET; //class | cur_state | low speed
|
|
pdev->ref_count = 0;
|
|
pdev->dev_addr = addr;
|
|
|
|
pdev->hcd = hcd;
|
|
|
|
pdev->dev_id = dev_mgr->conn_count; //will be used to compose dev_handle
|
|
|
|
InitializeListHead(&pdev->default_endp.urb_list);
|
|
pdev->default_endp.pusb_if = (PUSB_INTERFACE) pdev;
|
|
pdev->default_endp.flags = USB_ENDP_FLAG_DEFAULT_ENDP; //toggle | busy-count | stall | default-endp
|
|
|
|
return pdev;
|
|
}
|
|
|
|
VOID
|
|
dev_mgr_free_device(PUSB_DEV_MANAGER dev_mgr, PUSB_DEV pdev)
|
|
{
|
|
if (pdev == NULL || dev_mgr == NULL)
|
|
return;
|
|
|
|
dev_mgr_free_addr(dev_mgr, pdev, pdev->dev_addr);
|
|
if (pdev->usb_config && pdev != pdev->hcd->hcd_get_root_hub(pdev->hcd))
|
|
{
|
|
//root hub has its config and desc buf allocated together,
|
|
//so no usb_config allocated seperately
|
|
dev_mgr_destroy_usb_config(pdev->usb_config);
|
|
pdev->usb_config = NULL;
|
|
}
|
|
if (pdev->desc_buf)
|
|
{
|
|
usb_free_mem(pdev->desc_buf);
|
|
pdev->desc_buf = NULL;
|
|
}
|
|
usb_free_mem(pdev);
|
|
pdev = NULL;
|
|
return;
|
|
}
|
|
|
|
//called when a disconnect is detected on the port
|
|
VOID
|
|
dev_mgr_disconnect_dev(PUSB_DEV pdev)
|
|
{
|
|
PLIST_ENTRY pthis, pnext;
|
|
PHUB2_EXTENSION phub_ext = NULL;
|
|
PUSB_CONFIGURATION pconfig;
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PHCD hcd;
|
|
BOOLEAN is_hub, found;
|
|
ULONG dev_id;
|
|
int i;
|
|
|
|
USE_NON_PENDING_IRQL;
|
|
|
|
if (pdev == NULL)
|
|
return;
|
|
|
|
found = FALSE;
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("dev_mgr_disconnect_dev(): entering, pdev=0x%x\n", pdev));
|
|
lock_dev(pdev, FALSE);
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_BEFORE_ZOMB;
|
|
dev_mgr = dev_mgr_from_dev(pdev);
|
|
unlock_dev(pdev, FALSE);
|
|
|
|
// notify dev_driver that the dev stops function before any operations
|
|
if (pdev->dev_driver && pdev->dev_driver->disp_tbl.dev_stop)
|
|
pdev->dev_driver->disp_tbl.dev_stop(dev_mgr, dev_handle_from_dev(pdev));
|
|
|
|
//safe to use the dev pointer in this function.
|
|
lock_dev(pdev, FALSE);
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_ZOMB;
|
|
hcd = pdev->hcd;
|
|
dev_id = pdev->dev_id;
|
|
unlock_dev(pdev, FALSE);
|
|
|
|
if (dev_mgr == NULL)
|
|
return;
|
|
|
|
hcd->hcd_remove_device(hcd, pdev);
|
|
|
|
//disconnect its children
|
|
if ((pdev->flags & USB_DEV_CLASS_MASK) == USB_DEV_CLASS_HUB ||
|
|
(pdev->flags & USB_DEV_CLASS_MASK) == USB_DEV_CLASS_ROOT_HUB)
|
|
{
|
|
phub_ext = hub_ext_from_dev(pdev);
|
|
if (phub_ext)
|
|
{
|
|
for(i = 1; i <= phub_ext->port_count; i++)
|
|
{
|
|
if (phub_ext->child_dev[i])
|
|
{
|
|
dev_mgr_disconnect_dev(phub_ext->child_dev[i]);
|
|
phub_ext->child_dev[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pconfig = pdev->usb_config;
|
|
|
|
//remove event belong to the dev
|
|
is_hub = ((pdev->flags & USB_DEV_CLASS_MASK) == USB_DEV_CLASS_HUB);
|
|
|
|
if (phub_ext && is_hub)
|
|
{
|
|
for(i = 1; i <= phub_ext->port_count; i++)
|
|
{
|
|
found = hub_remove_reset_event(pdev, i, FALSE);
|
|
if (found)
|
|
break;
|
|
}
|
|
}
|
|
|
|
//free event of the dev from the event list
|
|
KeAcquireSpinLock(&dev_mgr->event_list_lock, &old_irql);
|
|
ListFirst(&dev_mgr->event_list, pthis);
|
|
while (pthis)
|
|
{
|
|
ListNext(&dev_mgr->event_list, pthis, pnext);
|
|
if (((PUSB_EVENT) pthis)->pdev == pdev)
|
|
{
|
|
PLIST_ENTRY p1;
|
|
RemoveEntryList(pthis);
|
|
if ((((PUSB_EVENT) pthis)->flags & USB_EVENT_FLAG_QUE_TYPE) != USB_EVENT_FLAG_NOQUE)
|
|
{
|
|
//has a queue, re-insert the queue
|
|
if ((p1 = (PLIST_ENTRY) ((PUSB_EVENT) pthis)->pnext))
|
|
{
|
|
InsertHeadList(&dev_mgr->event_list, p1);
|
|
free_event(&dev_mgr->event_pool, struct_ptr(pthis, USB_EVENT, event_link));
|
|
pthis = p1;
|
|
//note: this queue will be examined again in the next loop
|
|
//to find the matched dev in the queue
|
|
continue;
|
|
}
|
|
}
|
|
free_event(&dev_mgr->event_pool, struct_ptr(pthis, USB_EVENT, event_link));
|
|
}
|
|
else if (((((PUSB_EVENT) pthis)->flags & USB_EVENT_FLAG_QUE_TYPE)
|
|
!= USB_EVENT_FLAG_NOQUE) && ((PUSB_EVENT) pthis)->pnext)
|
|
{
|
|
//has a queue, examine the queue
|
|
PUSB_EVENT p1, p2;
|
|
p1 = (PUSB_EVENT) pthis;
|
|
p2 = p1->pnext;
|
|
while (p2)
|
|
{
|
|
if (p2->pdev == pdev)
|
|
{
|
|
p1->pnext = p2->pnext;
|
|
p2->pnext = NULL;
|
|
free_event(&dev_mgr->event_pool, p2);
|
|
p2 = p1->pnext;
|
|
}
|
|
else
|
|
{
|
|
p1 = p2;
|
|
p2 = p2->pnext;
|
|
}
|
|
}
|
|
}
|
|
pthis = pnext;
|
|
}
|
|
KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql);
|
|
|
|
// found indicates the reset event on one of the dev's port in process
|
|
if (found)
|
|
hub_start_next_reset_port(dev_mgr_from_dev(pdev), FALSE);
|
|
|
|
// remove timer-svc belonging to the dev
|
|
KeAcquireSpinLock(&dev_mgr->timer_svc_list_lock, &old_irql);
|
|
ListFirst(&dev_mgr->timer_svc_list, pthis);
|
|
i = 0;
|
|
while (pthis)
|
|
{
|
|
ListNext(&dev_mgr->timer_svc_list, pthis, pnext);
|
|
if (((PUSB_EVENT) pthis)->pdev == pdev)
|
|
{
|
|
RemoveEntryList(pthis);
|
|
free_timer_svc(&dev_mgr->timer_svc_pool, struct_ptr(pthis, TIMER_SVC, timer_svc_link));
|
|
i++;
|
|
}
|
|
pthis = pnext;
|
|
}
|
|
KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql);
|
|
|
|
// release the refcount
|
|
if (i)
|
|
{
|
|
lock_dev(pdev, FALSE);
|
|
pdev->ref_count -= i;
|
|
unlock_dev(pdev, FALSE);
|
|
}
|
|
|
|
// wait for all the reference count be released
|
|
for(;;)
|
|
{
|
|
LARGE_INTEGER interval;
|
|
|
|
lock_dev(pdev, FALSE);
|
|
if (pdev->ref_count == 0)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
break;
|
|
}
|
|
unlock_dev(pdev, FALSE);
|
|
// Wait two ms.
|
|
interval.QuadPart = -20000;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &interval);
|
|
}
|
|
|
|
if (pdev->dev_driver && pdev->dev_driver->disp_tbl.dev_disconnect)
|
|
pdev->dev_driver->disp_tbl.dev_disconnect(dev_mgr, dev_handle_from_dev(pdev));
|
|
|
|
// we put it here to let handle valid before disconnect
|
|
KeAcquireSpinLock(&dev_mgr->dev_list_lock, &old_irql);
|
|
ListFirst(&dev_mgr->dev_list, pthis);
|
|
while (pthis)
|
|
{
|
|
if (((PUSB_DEV) pthis) == pdev)
|
|
{
|
|
RemoveEntryList(pthis);
|
|
break;
|
|
}
|
|
ListNext(&dev_mgr->dev_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
KeReleaseSpinLock(&dev_mgr->dev_list_lock, old_irql);
|
|
|
|
|
|
if (pdev != pdev->hcd->hcd_get_root_hub(pdev->hcd))
|
|
{
|
|
dev_mgr_free_device(dev_mgr, pdev);
|
|
}
|
|
else
|
|
{
|
|
//rh_destroy( pdev );
|
|
//TRAP();
|
|
//destroy it in dev_mgr_destroy
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//called in hub_set_address_completion
|
|
BOOLEAN
|
|
dev_mgr_start_config_dev(PUSB_DEV pdev)
|
|
{
|
|
PBYTE data_buf;
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
PURB purb;
|
|
PHCD hcd;
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
hcd_dbg_print(DBGLVL_MAXIMUM, ("dev_mgr_start_config_dev: pdev=%p\n", pdev));
|
|
|
|
if (pdev == NULL)
|
|
return FALSE;
|
|
|
|
lock_dev(pdev, TRUE);
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
hcd = pdev->hcd;
|
|
|
|
//first, get device descriptor
|
|
purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
|
|
data_buf = usb_alloc_mem(NonPagedPool, 512);
|
|
if (purb == NULL || data_buf == NULL)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(purb, sizeof(URB));
|
|
RtlZeroMemory(data_buf, 512);
|
|
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
|
|
purb->data_buffer = data_buf; // user data
|
|
purb->data_length = 8; // get partial desc
|
|
|
|
pdev->desc_buf = data_buf;
|
|
pdev->desc_buf_size = 512;
|
|
|
|
purb->pdev = pdev;
|
|
purb->pendp = &pdev->default_endp; //pipe for current transfer
|
|
|
|
purb->completion = dev_mgr_get_desc_completion;
|
|
purb->reference = 0;
|
|
|
|
InitializeListHead(&purb->trasac_list);
|
|
|
|
psetup->bmRequestType = 0x80;
|
|
psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
|
|
psetup->wValue = (USB_DT_DEVICE << 8) | 0;
|
|
psetup->wIndex = 0;
|
|
psetup->wLength = 8; //sizeof( USB_DEVICE_DESC );
|
|
unlock_dev(pdev, TRUE);
|
|
|
|
if (hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb) != STATUS_PENDING)
|
|
{
|
|
usb_free_mem(purb);
|
|
usb_free_mem(data_buf);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
dev_mgr_get_desc_completion(PURB purb, PVOID context)
|
|
{
|
|
PUSB_DEV pdev;
|
|
PUSB_CONFIGURATION_DESC pconfig_desc;
|
|
PUSB_ENDPOINT pendp;
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
NTSTATUS status;
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
PHCD hcd;
|
|
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (purb == NULL)
|
|
return;
|
|
|
|
hcd_dbg_print(DBGLVL_MAXIMUM,
|
|
("dev_mgr_get_desc_completion: purb->reference=%d\n", purb->reference));
|
|
|
|
pdev = purb->pdev;
|
|
pendp = purb->pendp;
|
|
|
|
if (pdev == NULL || pendp == NULL)
|
|
{
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
return;
|
|
}
|
|
|
|
lock_dev(pdev, TRUE);
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
pendp = &pdev->default_endp;
|
|
dev_mgr = dev_mgr_from_dev(pdev);
|
|
hcd = pdev->hcd;
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
|
|
if (usb_error(purb->status))
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
hcd_dbg_print(DBGLVL_MAXIMUM,
|
|
("dev_mgr_get_desc_completion: can not get dev desc ref=0x%x, status=0x%x\n",
|
|
purb->reference, purb->status));
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
switch (purb->reference)
|
|
{
|
|
case 0:
|
|
{
|
|
//only partial dev_desc
|
|
//enable the dev specific default endp maxpacketsize
|
|
pdev->pusb_dev_desc = (PUSB_DEVICE_DESC) purb->data_buffer;
|
|
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
psetup->wLength = sizeof(USB_DEVICE_DESC);
|
|
|
|
//get the complete dev_desc
|
|
purb->reference = 1;
|
|
purb->status = 0;
|
|
purb->data_length = sizeof(USB_DEVICE_DESC);
|
|
|
|
unlock_dev(pdev, TRUE);
|
|
|
|
status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb);
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
goto LBL_OUT;
|
|
}
|
|
return;
|
|
}
|
|
case 1:
|
|
{
|
|
//let's begin to get config descriptors.
|
|
if (pdev->pusb_dev_desc->bNumConfigurations == 0)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
purb->data_buffer += sizeof(USB_DEVICE_DESC);
|
|
purb->data_length = 8;
|
|
purb->reference++;
|
|
purb->context = (PVOID) sizeof(USB_DEVICE_DESC);
|
|
purb->status = 0;
|
|
|
|
psetup->wValue = (USB_DT_CONFIG << 8) | 0;
|
|
psetup->wLength = 8;
|
|
unlock_dev(pdev, TRUE);
|
|
|
|
status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb);
|
|
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
goto LBL_OUT;
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
{
|
|
LONG config_idx;
|
|
config_idx = (purb->reference >> 1) - 1;
|
|
if ((purb->reference & 1) == 0)
|
|
{
|
|
//partial config desc is obtained.
|
|
pconfig_desc = (PUSB_CONFIGURATION_DESC) purb->data_buffer;
|
|
if (pconfig_desc->wTotalLength >= 1024)
|
|
{
|
|
//treat as an error
|
|
unlock_dev(pdev, TRUE);
|
|
goto LBL_OUT;
|
|
|
|
}
|
|
|
|
if (pconfig_desc->wTotalLength > (USHORT) (pdev->desc_buf_size - (LONG) purb->context))
|
|
{
|
|
//rewind the 8-byte hdr
|
|
*((PULONG) & context) -= 8;
|
|
realloc_buf(pdev, purb);
|
|
}
|
|
purb->data_length = pconfig_desc->wTotalLength;
|
|
psetup->wLength = pconfig_desc->wTotalLength;
|
|
purb->reference++;
|
|
unlock_dev(pdev, TRUE);
|
|
status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb);
|
|
if (status != STATUS_PENDING)
|
|
goto LBL_OUT;
|
|
|
|
}
|
|
else
|
|
{
|
|
//complete desc is returned.
|
|
if (config_idx + 1 < pdev->pusb_dev_desc->bNumConfigurations)
|
|
{
|
|
//still have configurations left
|
|
*((PULONG) & context) += psetup->wLength;
|
|
purb->data_buffer = &pdev->desc_buf[(LONG) context];
|
|
purb->data_length = 8;
|
|
psetup->wLength = 8;
|
|
psetup->wValue = (((USB_DT_CONFIG) << 8) | (config_idx + 1));
|
|
purb->reference++;
|
|
purb->context = context;
|
|
|
|
if (((LONG) context) + 8 > pdev->desc_buf_size)
|
|
realloc_buf(pdev, purb);
|
|
|
|
purb->status = 0;
|
|
unlock_dev(pdev, TRUE);
|
|
status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb);
|
|
if (status != STATUS_PENDING)
|
|
goto LBL_OUT;
|
|
}
|
|
else
|
|
{
|
|
//config descriptors have all been fetched
|
|
unlock_dev(pdev, TRUE);
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
|
|
// load driver for the device
|
|
dev_mgr_start_select_driver(pdev);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
LBL_OUT:
|
|
if (purb)
|
|
{
|
|
usb_free_mem(purb);
|
|
purb = NULL;
|
|
}
|
|
|
|
lock_dev(pdev, TRUE);
|
|
if (dev_state(pdev) != USB_DEV_STATE_ZOMB)
|
|
{
|
|
if (pdev->desc_buf)
|
|
{
|
|
usb_free_mem(pdev->desc_buf);
|
|
pdev->desc_buf_size = 0;
|
|
pdev->desc_buf = NULL;
|
|
pdev->pusb_dev_desc = NULL;
|
|
pdev->usb_config = NULL;
|
|
}
|
|
}
|
|
unlock_dev(pdev, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_start_select_driver(PUSB_DEV pdev)
|
|
{
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PUSB_EVENT pevent;
|
|
BOOLEAN bret;
|
|
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (pdev == NULL)
|
|
return FALSE;
|
|
|
|
dev_mgr = dev_mgr_from_dev(pdev);
|
|
KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock);
|
|
lock_dev(pdev, TRUE);
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
bret = FALSE;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
pevent = alloc_event(&dev_mgr->event_pool, 1);
|
|
if (pevent == NULL)
|
|
{
|
|
bret = FALSE;
|
|
goto LBL_OUT;
|
|
}
|
|
pevent->flags = USB_EVENT_FLAG_ACTIVE;
|
|
pevent->event = USB_EVENT_DEFAULT;
|
|
pevent->pdev = pdev;
|
|
pevent->context = 0;
|
|
pevent->param = 0;
|
|
pevent->pnext = 0; //vertical queue for serialized operation
|
|
pevent->process_event = dev_mgr_event_select_driver;
|
|
pevent->process_queue = event_list_default_process_queue;
|
|
|
|
InsertTailList(&dev_mgr->event_list, &pevent->event_link);
|
|
KeSetEvent(&dev_mgr->wake_up_event, 0, FALSE);
|
|
bret = TRUE;
|
|
|
|
LBL_OUT:
|
|
unlock_dev(pdev, TRUE);
|
|
KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock);
|
|
return bret;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_connect_to_dev(PVOID Parameter)
|
|
{
|
|
PUSB_DEV pdev;
|
|
DEV_HANDLE dev_handle;
|
|
NTSTATUS status;
|
|
PUSB_DRIVER pdriver;
|
|
PDEV_CONNECT_DATA pcd = (PDEV_CONNECT_DATA) Parameter;
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
DEV_CONNECT_DATA param;
|
|
|
|
if (pcd == NULL)
|
|
return FALSE;
|
|
dev_handle = pcd->dev_handle;
|
|
pdriver = pcd->pdriver;
|
|
dev_mgr = pcd->dev_mgr;
|
|
|
|
param.dev_mgr = dev_mgr;
|
|
param.pdriver = pdriver;
|
|
param.dev_handle = 0; //not used
|
|
|
|
status = usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev);
|
|
if (status != STATUS_SUCCESS)
|
|
return FALSE;
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("dev_mgr_connect_to_dev(): about to call driver's dev_connect\n"));
|
|
status = pdriver->disp_tbl.dev_connect(¶m, dev_handle);
|
|
usb_unlock_dev(pdev);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
dev_mgr_event_select_driver(PUSB_DEV pdev, ULONG event, ULONG context, ULONG param)
|
|
{
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PUSB_DRIVER pdriver, pcand;
|
|
LONG credit, match, i;
|
|
DEV_HANDLE handle = 0;
|
|
DEV_CONNECT_DATA cd;
|
|
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
UNREFERENCED_PARAMETER(param);
|
|
UNREFERENCED_PARAMETER(context);
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("dev_mgr_event_select_driver(): pdev=%p event=0x%x\n", pdev, event));
|
|
|
|
if (pdev == NULL)
|
|
return;
|
|
|
|
lock_dev(pdev, FALSE);
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
return;
|
|
}
|
|
dev_mgr = dev_mgr_from_dev(pdev);
|
|
|
|
pcand = NULL;
|
|
match = 0;
|
|
for(i = HUB_DRIVER_IDX; i < DEVMGR_MAX_DRIVERS; i++)
|
|
{
|
|
//bypass root-hub driver with idx zero
|
|
pdriver = (PUSB_DRIVER) & dev_mgr->driver_list[i];
|
|
|
|
if (pdriver->driver_desc.flags & USB_DRIVER_FLAG_DEV_CAPABLE)
|
|
credit = dev_mgr_score_driver_for_dev(dev_mgr, pdriver, pdev->pusb_dev_desc);
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
if (credit > match)
|
|
pcand = pdriver, match = credit;
|
|
|
|
}
|
|
|
|
if (match)
|
|
{
|
|
// we set class driver here
|
|
// pdev->dev_driver = pcand;
|
|
handle = usb_make_handle(pdev->dev_id, 0, 0);
|
|
}
|
|
unlock_dev(pdev, FALSE);
|
|
|
|
if (match)
|
|
{
|
|
|
|
cd.dev_handle = handle;
|
|
cd.pdriver = pcand;
|
|
cd.dev_mgr = dev_mgr;
|
|
|
|
if (dev_mgr_connect_to_dev(&cd))
|
|
return;
|
|
|
|
// ExInitializeWorkItem( pwork_item, dev_mgr_connect_to_dev, ( PVOID )pcd );
|
|
// ExQueueWorkItem( pwork_item, DelayedWorkQueue );
|
|
}
|
|
cd.dev_handle = handle;
|
|
cd.pdriver = &dev_mgr->driver_list[GEN_DRIVER_IDX];
|
|
cd.dev_mgr = dev_mgr;
|
|
dev_mgr_connect_to_dev(&cd);
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_build_usb_endp(PUSB_INTERFACE pif, PUSB_ENDPOINT pendp, PUSB_ENDPOINT_DESC pendp_desc)
|
|
{
|
|
if (pendp == NULL || pif == NULL || pendp_desc == NULL)
|
|
return FALSE;
|
|
|
|
pendp->flags = 0;
|
|
InitializeListHead(&pendp->urb_list); //pending urb queue
|
|
pendp->pusb_if = pif;
|
|
pendp->pusb_endp_desc = pendp_desc;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_build_usb_if(PUSB_CONFIGURATION pcfg, PUSB_INTERFACE pif, PUSB_INTERFACE_DESC pif_desc, BOOLEAN alt_if)
|
|
{
|
|
LONG i;
|
|
PUSB_ENDPOINT_DESC pendp_desc;
|
|
PBYTE pbuf;
|
|
|
|
if (pcfg == NULL || pif == NULL || pif_desc == NULL)
|
|
return FALSE;
|
|
|
|
if (alt_if == FALSE)
|
|
{
|
|
pif->endp_count = pif_desc->bNumEndpoints > MAX_ENDPS_PER_IF
|
|
? MAX_ENDPS_PER_IF : pif_desc->bNumEndpoints;
|
|
|
|
pif->pif_drv = NULL;
|
|
pif->pusb_config = pcfg;
|
|
pif->pusb_if_desc = pif_desc;
|
|
pif->if_ext_size = 0;
|
|
pif->if_ext = NULL;
|
|
|
|
InitializeListHead(&pif->altif_list);
|
|
pif->altif_count = 0;
|
|
|
|
pbuf = &((PBYTE) pif_desc)[sizeof(USB_INTERFACE_DESC)];
|
|
|
|
i = 0;
|
|
while (i < pif->endp_count)
|
|
{
|
|
pendp_desc = (PUSB_ENDPOINT_DESC)pbuf;
|
|
|
|
// check if it's an endpoint descriptor
|
|
if (pendp_desc->bDescriptorType == USB_DT_ENDPOINT)
|
|
{
|
|
// add it
|
|
dev_mgr_build_usb_endp(pif, &pif->endp[i], pendp_desc);
|
|
i++;
|
|
}
|
|
|
|
// skip to the next one
|
|
pbuf += pendp_desc->bLength;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PUSB_INTERFACE paltif;
|
|
PLIST_ENTRY pthis, pnext;
|
|
|
|
pif->altif_count++;
|
|
paltif = usb_alloc_mem(NonPagedPool, sizeof(USB_INTERFACE));
|
|
if (!paltif) return FALSE;
|
|
|
|
RtlZeroMemory(paltif, sizeof(USB_INTERFACE));
|
|
InsertTailList(&pif->altif_list, &paltif->altif_list);
|
|
paltif->pif_drv = NULL;
|
|
paltif->pusb_config = pcfg;
|
|
paltif->pusb_if_desc = pif_desc;
|
|
paltif->if_ext_size = 0;
|
|
paltif->if_ext = NULL;
|
|
paltif->endp_count = pif_desc->bNumEndpoints > MAX_ENDPS_PER_IF
|
|
? MAX_ENDPS_PER_IF : pif_desc->bNumEndpoints;
|
|
|
|
ListFirst(&pif->altif_list, pthis);
|
|
|
|
while (pthis)
|
|
{
|
|
//synchronize the altif_count;
|
|
PUSB_INTERFACE pthis_if;
|
|
pthis_if = (PUSB_INTERFACE) (((PBYTE) pthis) - offsetof(USB_INTERFACE, altif_list));
|
|
pthis_if->altif_count = pif->altif_count;
|
|
ListNext(&pif->altif_list, pthis, pnext);
|
|
pthis = pnext;
|
|
}
|
|
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
dev_mgr_build_usb_config(PUSB_DEV pdev, PBYTE pbuf, ULONG config_val, LONG config_count)
|
|
{
|
|
PUSB_CONFIGURATION pcfg;
|
|
PUSB_INTERFACE_DESC pif_desc;
|
|
PUSB_INTERFACE pif;
|
|
int i;
|
|
LONG if_count;
|
|
|
|
if (pdev == NULL || pbuf == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
pdev->usb_config = usb_alloc_mem(NonPagedPool, sizeof(USB_CONFIGURATION));
|
|
pcfg = pdev->usb_config;
|
|
|
|
if (pdev->usb_config == NULL)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
RtlZeroMemory(pcfg, sizeof(USB_CONFIGURATION));
|
|
pcfg->pusb_config_desc = usb_find_config_desc_by_val(pbuf, config_val, config_count);
|
|
|
|
if (pcfg->pusb_config_desc == NULL)
|
|
{
|
|
usb_free_mem(pcfg);
|
|
pdev->usb_config = NULL;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
pcfg->if_count = pcfg->pusb_config_desc->bNumInterfaces;
|
|
pcfg->pusb_dev = pdev;
|
|
pif_desc = (PUSB_INTERFACE_DESC) & ((PBYTE) pcfg->pusb_config_desc)[sizeof(USB_CONFIGURATION_DESC)];
|
|
if_count = pcfg->if_count;
|
|
|
|
for(i = 0; i < if_count; i++, pif_desc++)
|
|
{
|
|
if (pif_desc->bAlternateSetting == 0)
|
|
{
|
|
dev_mgr_build_usb_if(pcfg, &pcfg->interf[i], pif_desc, FALSE);
|
|
}
|
|
else
|
|
{
|
|
pif = &pcfg->interf[i-1];
|
|
dev_mgr_build_usb_if(pcfg, pif, pif_desc, TRUE);
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
dev_mgr_destroy_usb_config(PUSB_CONFIGURATION pcfg)
|
|
{
|
|
long i;
|
|
PLIST_ENTRY pthis;
|
|
PUSB_INTERFACE pif;
|
|
|
|
if (pcfg == NULL)
|
|
return FALSE;
|
|
|
|
for(i = 0; i < pcfg->if_count; i++)
|
|
{
|
|
pif = &pcfg->interf[i];
|
|
|
|
if (pif->altif_count)
|
|
{
|
|
ListFirst(&pif->altif_list, pthis);
|
|
while (pthis)
|
|
{
|
|
PUSB_INTERFACE pthis_if;
|
|
pthis_if = (PUSB_INTERFACE) (((PBYTE) pthis) - offsetof(USB_INTERFACE, altif_list));
|
|
RemoveEntryList(pthis);
|
|
usb_free_mem(pthis_if);
|
|
if (IsListEmpty(&pif->altif_list) == TRUE)
|
|
break;
|
|
|
|
ListFirst(&pif->altif_list, pthis);
|
|
}
|
|
}
|
|
}
|
|
usb_free_mem(pcfg);
|
|
return TRUE;
|
|
}
|
|
|
|
#define is_dev_product_match( pdriVER, pdev_DESC ) \
|
|
( ( pdriVER )->driver_desc.vendor_id == ( pdev_DESC )->idVendor \
|
|
&& ( pdriVER )->driver_desc.product_id == ( pdev_DESC )->idProduct )
|
|
|
|
LONG
|
|
dev_mgr_score_driver_for_dev(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver, PUSB_DEVICE_DESC pdev_desc)
|
|
{
|
|
LONG credit = 0;
|
|
|
|
UNREFERENCED_PARAMETER(dev_mgr);
|
|
|
|
//assume supports all the sub_class are supported if sub_class is zero
|
|
if (pdriver->driver_desc.dev_class == pdev_desc->bDeviceClass)
|
|
{
|
|
if (pdriver->driver_desc.dev_sub_class == 0 && pdriver->driver_desc.dev_protocol == 0)
|
|
credit = 3;
|
|
else if (pdriver->driver_desc.dev_sub_class == pdev_desc->bDeviceSubClass)
|
|
{
|
|
if (pdriver->driver_desc.dev_protocol == 0)
|
|
credit = 6;
|
|
else if (pdriver->driver_desc.dev_protocol == pdev_desc->bDeviceProtocol)
|
|
credit = 9;
|
|
}
|
|
}
|
|
|
|
if (is_dev_product_match(pdriver, pdev_desc))
|
|
credit += 20;
|
|
|
|
return credit;
|
|
}
|
|
|
|
LONG
|
|
dev_mgr_score_driver_for_if(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver, PUSB_INTERFACE_DESC pif_desc)
|
|
{
|
|
LONG credit;
|
|
|
|
if (pdriver == NULL
|
|
|| !(pdriver->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE) || pif_desc == NULL || dev_mgr == NULL)
|
|
return 0;
|
|
|
|
if (is_header_match((PBYTE) pif_desc, USB_DT_INTERFACE) == FALSE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
credit = 0;
|
|
if ((pdriver->driver_desc.if_class == pif_desc->bInterfaceClass))
|
|
{
|
|
if (pdriver->driver_desc.if_sub_class == 0 && pdriver->driver_desc.if_protocol == 0)
|
|
credit = 2;
|
|
if (pdriver->driver_desc.if_sub_class == pif_desc->bInterfaceSubClass)
|
|
{
|
|
if (pdriver->driver_desc.if_protocol == 0)
|
|
credit = 4;
|
|
if (pdriver->driver_desc.if_protocol == pif_desc->bInterfaceProtocol)
|
|
credit = 6;
|
|
}
|
|
}
|
|
else
|
|
credit = 1;
|
|
|
|
return credit;
|
|
}
|
|
|
|
#define is_equal_driver( pd1, pd2, ret ) \
|
|
{\
|
|
int i;\
|
|
ret = TRUE;\
|
|
PUSB_DRIVER pdr1, pdr2;\
|
|
pdr1 = ( PUSB_DRIVER )( pd1 );\
|
|
pdr2 = ( PUSB_DRIVER ) ( pd2 );\
|
|
for( i = 0; i < 16; i++ )\
|
|
{\
|
|
if( pdr1->driver_name[ i ] != pdr2->driver_name[ i ] )\
|
|
{\
|
|
ret = FALSE;\
|
|
break;\
|
|
}\
|
|
}\
|
|
}
|
|
|
|
//return value is the hcd id
|
|
UCHAR
|
|
dev_mgr_register_hcd(PUSB_DEV_MANAGER dev_mgr, PHCD hcd)
|
|
{
|
|
if (dev_mgr == NULL || hcd == NULL)
|
|
return 0xff;
|
|
|
|
if (dev_mgr->hcd_count >= MAX_HCDS)
|
|
return 0xff;
|
|
|
|
dev_mgr->hcd_array[dev_mgr->hcd_count++] = hcd;
|
|
return dev_mgr->hcd_count - 1;
|
|
}
|
|
|
|
VOID
|
|
dev_mgr_deregister_hcd(PUSB_DEV_MANAGER dev_mgr, UCHAR hcd_id)
|
|
{
|
|
UCHAR i;
|
|
|
|
if (dev_mgr == NULL || hcd_id >= MAX_HCDS - 1)
|
|
return;
|
|
|
|
for (i = hcd_id; i < dev_mgr->hcd_count - 1; i++)
|
|
dev_mgr->hcd_array[i] = dev_mgr->hcd_array[i + 1];
|
|
|
|
dev_mgr->hcd_count--;
|
|
}
|
|
|
|
BOOLEAN
|
|
dev_mgr_register_irp(PUSB_DEV_MANAGER dev_mgr, PIRP pirp, PURB purb)
|
|
{
|
|
if (dev_mgr == NULL)
|
|
return FALSE;
|
|
|
|
if (add_irp_to_list(&dev_mgr->irp_list, pirp, purb))
|
|
{
|
|
return TRUE;
|
|
}
|
|
TRAP();
|
|
return FALSE;
|
|
}
|
|
|
|
//caller must guarantee that when this func is called,
|
|
//the urb associated must exist.
|
|
PURB
|
|
dev_mgr_remove_irp(PUSB_DEV_MANAGER dev_mgr, PIRP pirp)
|
|
{
|
|
PURB purb;
|
|
if (dev_mgr == NULL)
|
|
return NULL;
|
|
|
|
purb = remove_irp_from_list(&dev_mgr->irp_list, pirp, NULL);
|
|
return purb;
|
|
}
|
|
|
|
VOID
|
|
dev_mgr_cancel_irp(PDEVICE_OBJECT dev_obj, PIRP pirp)
|
|
{
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PDEVEXT_HEADER pdev_ext_hdr;
|
|
|
|
pdev_ext_hdr = (PDEVEXT_HEADER) dev_obj->DeviceExtension;
|
|
dev_mgr = pdev_ext_hdr->dev_mgr;
|
|
|
|
if (dev_obj->CurrentIrp == pirp)
|
|
{
|
|
IoReleaseCancelSpinLock(pirp->CancelIrql);
|
|
// we did not IoStartNextPacket, leave it for the urb completion
|
|
}
|
|
else
|
|
{
|
|
KeRemoveEntryDeviceQueue(&dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry);
|
|
IoReleaseCancelSpinLock(pirp->CancelIrql);
|
|
|
|
pirp->IoStatus.Information = 0;
|
|
pirp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(pirp, IO_NO_INCREMENT);
|
|
// the device queue is moved on, no need to call IoStartNextPacket
|
|
return;
|
|
}
|
|
|
|
//
|
|
// remove the irp and call the dev_mgr_cancel_irp
|
|
// the completion will be done in urb completion
|
|
//
|
|
remove_irp_from_list(&dev_mgr->irp_list, pirp, dev_mgr);
|
|
return;
|
|
|
|
}
|
|
|
|
// release the hcd
|
|
VOID
|
|
dev_mgr_release_hcd(PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
LONG i;
|
|
PHCD hcd;
|
|
for(i = 0; i < dev_mgr->hcd_count; i++)
|
|
{
|
|
hcd = dev_mgr->hcd_array[i];
|
|
hcd->hcd_release(hcd);
|
|
dev_mgr->hcd_array[i] = 0;
|
|
}
|
|
dev_mgr->hcd_count = 0;
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
dev_mgr_start_hcd(PUSB_DEV_MANAGER dev_mgr)
|
|
{
|
|
LONG i;
|
|
PHCD hcd;
|
|
for(i = 0; i < dev_mgr->hcd_count; i++)
|
|
{
|
|
hcd = dev_mgr->hcd_array[i];
|
|
hcd->hcd_start(hcd);
|
|
}
|
|
return;
|
|
}
|