/** * 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; }