/** * hub.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" //---------------------------------------------------------- //event pool routines #define crash_machine() \ { ( ( PUSB_DEV ) 0 )->flags = 0x12345; } #define hub_if_from_dev( pdEV, pIF ) \ {\ int i;\ for( i = 0; i < pdEV->usb_config->if_count; i ++ )\ {\ if( pdEV->usb_config->interf[ i ].pusb_if_desc->bInterfaceClass\ == USB_CLASS_HUB )\ {\ break;\ }\ }\ \ if( i < pdEV->usb_config->if_count )\ pIF = &pdev->usb_config->interf[ i ];\ else\ pIF = NULL;\ \ } extern ULONG cpu_clock_freq; BOOLEAN hub_check_reset_port_status(PUSB_DEV pdev, LONG port_idx); VOID hub_reexamine_port_status_queue(PUSB_DEV hub_dev, ULONG port_idx, BOOLEAN from_dpc); void hub_int_completion(PURB purb, PVOID pcontext); VOID hub_get_port_status_completion(PURB purb, PVOID context); VOID hub_clear_port_feature_completion(PURB purb, PVOID context); VOID hub_event_examine_status_que(PUSB_DEV pdev, ULONG event, ULONG context, //hub_ext ULONG param //port_idx ); VOID hub_timer_wait_dev_stable(PUSB_DEV pdev, PVOID context //port-index ); VOID hub_event_dev_stable(PUSB_DEV pdev, ULONG event, ULONG context, //hub_ext ULONG param //port_idx ); VOID hub_post_esq_event(PUSB_DEV pdev, BYTE port_idx, PROCESS_EVENT pe); void hub_set_cfg_completion(PURB purb, PVOID pcontext); void hub_get_hub_desc_completion(PURB purb, PVOID pcontext); NTSTATUS hub_start_int_request(PUSB_DEV pdev); BOOLEAN hub_connect(PDEV_CONNECT_DATA init_param, DEV_HANDLE dev_handle); BOOLEAN hub_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle); BOOLEAN hub_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle); NTSTATUS hub_disable_port_request(PUSB_DEV pdev, UCHAR port_idx); VOID hub_start_reset_port_completion(PURB purb, PVOID context); BOOLEAN init_event_pool(PUSB_EVENT_POOL pool) { int i; if (pool == NULL) return FALSE; if ((pool->event_array = usb_alloc_mem(NonPagedPool, sizeof(USB_EVENT) * MAX_EVENTS)) == NULL) return FALSE; InitializeListHead(&pool->free_que); KeInitializeSpinLock(&pool->pool_lock); pool->total_count = MAX_EVENTS; pool->free_count = 0; for(i = 0; i < MAX_EVENTS; i++) { free_event(pool, &pool->event_array[i]); } return TRUE; } BOOLEAN free_event(PUSB_EVENT_POOL pool, PUSB_EVENT pevent) { if (pool == NULL || pevent == NULL) { return FALSE; } RtlZeroMemory(pevent, sizeof(USB_EVENT)); InsertTailList(&pool->free_que, &pevent->event_link); pool->free_count++; usb_dbg_print(DBGLVL_MAXIMUM + 1, ("free_event(): alloced=0x%x, addr=0x%x\n", MAX_EVENTS - pool->free_count, pevent)); return TRUE; } //null if failed PUSB_EVENT alloc_event(PUSB_EVENT_POOL pool, LONG count) { PUSB_EVENT NewEvent; if (pool == NULL || count != 1) return NULL; if (pool->free_count == 0) return NULL; NewEvent = (PUSB_EVENT) RemoveHeadList(&pool->free_que); pool->free_count--; usb_dbg_print(DBGLVL_MAXIMUM + 1, ("alloc_event(): alloced=0x%x, addr=0x%x\n", MAX_EVENTS - pool->free_count, NewEvent)); return NewEvent; } BOOLEAN destroy_event_pool(PUSB_EVENT_POOL pool) { if (pool == NULL) return FALSE; InitializeListHead(&pool->free_que); pool->free_count = pool->total_count = 0; usb_free_mem(pool->event_array); pool->event_array = NULL; return TRUE; } VOID event_list_default_process_event(PUSB_DEV pdev, ULONG event, ULONG context, ULONG param) { UNREFERENCED_PARAMETER(param); UNREFERENCED_PARAMETER(context); UNREFERENCED_PARAMETER(event); UNREFERENCED_PARAMETER(pdev); } //---------------------------------------------------------- //timer_svc pool routines BOOLEAN init_timer_svc_pool(PTIMER_SVC_POOL pool) { int i; if (pool == NULL) return FALSE; pool->timer_svc_array = usb_alloc_mem(NonPagedPool, sizeof(TIMER_SVC) * MAX_TIMER_SVCS); InitializeListHead(&pool->free_que); pool->free_count = 0; pool->total_count = MAX_TIMER_SVCS; KeInitializeSpinLock(&pool->pool_lock); for(i = 0; i < MAX_TIMER_SVCS; i++) { free_timer_svc(pool, &pool->timer_svc_array[i]); } return TRUE; } BOOLEAN free_timer_svc(PTIMER_SVC_POOL pool, PTIMER_SVC ptimer) { if (pool == NULL || ptimer == NULL) return FALSE; RtlZeroMemory(ptimer, sizeof(TIMER_SVC)); InsertTailList(&pool->free_que, &ptimer->timer_svc_link); pool->free_count++; return TRUE; } //null if failed PTIMER_SVC alloc_timer_svc(PTIMER_SVC_POOL pool, LONG count) { PTIMER_SVC NewTimer; if (pool == NULL || count != 1) return NULL; if (pool->free_count <= 0) return NULL; NewTimer = (PTIMER_SVC) RemoveHeadList(&pool->free_que); pool->free_count--; return NewTimer; } BOOLEAN destroy_timer_svc_pool(PTIMER_SVC_POOL pool) { if (pool == NULL) return FALSE; usb_free_mem(pool->timer_svc_array); pool->timer_svc_array = NULL; InitializeListHead(&pool->free_que); pool->free_count = 0; pool->total_count = 0; return TRUE; } VOID event_list_default_process_queue(PLIST_HEAD event_list, PUSB_EVENT_POOL event_pool, PUSB_EVENT usb_event, PUSB_EVENT out_event) { //remove the first event from the event list, and copy it to //out_event if (event_list == NULL || event_pool == NULL || usb_event == NULL || out_event == NULL) return; RemoveEntryList(&usb_event->event_link); RtlCopyMemory(out_event, usb_event, sizeof(USB_EVENT)); free_event(event_pool, usb_event); return; } BOOLEAN psq_enqueue(PPORT_STATUS_QUEUE psq, ULONG status) { if (psq == NULL) return FALSE; if (psq_is_full(psq)) return FALSE; psq->port_status[psq->status_count].wPortChange = HIWORD(status); psq->port_status[psq->status_count].wPortStatus = LOWORD(status); psq->status_count++; usb_dbg_print(DBGLVL_MAXIMUM, ("psq_enqueue(): last status=0x%x, status count=0x%x, port_flag=0x%x\n", status, psq->status_count, psq->port_flags)); return TRUE; } VOID psq_init(PPORT_STATUS_QUEUE psq) { RtlZeroMemory(psq, sizeof(PORT_STATUS_QUEUE)); psq->port_flags = STATE_IDLE | USB_PORT_FLAG_DISABLE; } //return 0xffffffff if no element ULONG psq_outqueue(PPORT_STATUS_QUEUE psq) { ULONG status; if (psq == NULL) return 0; if (psq_is_empty(psq)) return 0; status = ((PULONG) & psq->port_status)[0]; psq->port_status[0] = psq->port_status[1]; psq->port_status[1] = psq->port_status[2]; psq->port_status[2] = psq->port_status[3]; psq->status_count--; return status; } BOOLEAN psq_push(PPORT_STATUS_QUEUE psq, ULONG status) { if (psq == NULL) return FALSE; status = ((PULONG) & psq->port_status)[0]; psq->port_status[3] = psq->port_status[2]; psq->port_status[2] = psq->port_status[1]; psq->port_status[1] = psq->port_status[0]; ((PULONG) & psq->port_status)[0] = status; psq->status_count++; psq->status_count = ((4 > psq->status_count) ? psq->status_count : 4); return TRUE; } BOOLEAN hub_driver_init(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver) { UNREFERENCED_PARAMETER(dev_mgr); //init driver structure, no PNP table functions pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE; pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID pdriver->driver_desc.product_id = 0xffff; // USB Product ID. pdriver->driver_desc.release_num = 0xffff; // Release Number of Device pdriver->driver_desc.config_val = 0; // Configuration Value pdriver->driver_desc.if_num = 0; // Interface Number pdriver->driver_desc.if_class = USB_CLASS_HUB; // Interface Class pdriver->driver_desc.if_sub_class = 0; // Interface SubClass pdriver->driver_desc.if_protocol = 0; // Interface Protocol pdriver->driver_desc.driver_name = "USB hub"; // Driver name for Name Registry pdriver->driver_desc.dev_class = USB_CLASS_HUB; pdriver->driver_desc.dev_sub_class = 0; // Device Subclass pdriver->driver_desc.dev_protocol = 0; // Protocol Info. //pdriver->driver_init = hub_driver_init; // initialized in dev_mgr_init_driver //pdriver->driver_destroy = hub_driver_destroy; pdriver->driver_ext = 0; pdriver->driver_ext_size = 0; pdriver->disp_tbl.version = 1; pdriver->disp_tbl.dev_connect = hub_connect; pdriver->disp_tbl.dev_disconnect = hub_disconnect; pdriver->disp_tbl.dev_stop = hub_stop; pdriver->disp_tbl.dev_reserved = NULL; return TRUE; } BOOLEAN hub_driver_destroy(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver) { UNREFERENCED_PARAMETER(dev_mgr); pdriver->driver_ext = NULL; return TRUE; } void hub_reset_pipe_completion(PURB purb, //only for reference, can not be released PVOID context) { PUSB_DEV pdev; PUSB_ENDPOINT pendp; USE_BASIC_NON_PENDING_IRQL; UNREFERENCED_PARAMETER(context); if (purb == NULL) { return; } pdev = purb->pdev; pendp = purb->pendp; lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); return; } if (usb_error(purb->status)) { //simply retry it unlock_dev(pdev, TRUE); //usb_free_mem( purb ); return; } unlock_dev(pdev, TRUE); pdev = purb->pdev; hub_start_int_request(pdev); return; } NTSTATUS hub_start_int_request(PUSB_DEV pdev) { PURB purb; PUSB_INTERFACE pif; PHUB2_EXTENSION hub_ext; NTSTATUS status; PHCD hcd; USE_BASIC_NON_PENDING_IRQL; if (pdev == NULL) return STATUS_INVALID_PARAMETER; lock_dev(pdev, FALSE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, FALSE); return STATUS_DEVICE_DOES_NOT_EXIST; } purb = usb_alloc_mem(NonPagedPool, sizeof(URB)); if (purb == NULL) { unlock_dev(pdev, FALSE); return STATUS_NO_MEMORY; } RtlZeroMemory(purb, sizeof(URB)); purb->flags = 0; purb->status = STATUS_SUCCESS; hub_ext = hub_ext_from_dev(pdev); purb->data_buffer = hub_ext->int_data_buf; purb->data_length = (hub_ext->port_count + 7) / 8; hub_if_from_dev(pdev, pif); usb_dbg_print(DBGLVL_ULTRA, ("hub_start_int_request(): pdev=0x%x, pif=0x%x\n", pdev, pif)); purb->pendp = &pif->endp[0]; purb->pdev = pdev; purb->completion = hub_int_completion; purb->context = hub_ext; purb->pirp = NULL; purb->reference = 0; hcd = pdev->hcd; unlock_dev(pdev, FALSE); status = hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb); if (status != STATUS_PENDING) { usb_free_mem(purb); purb = NULL; } return status; } void hub_int_completion(PURB purb, PVOID pcontext) { PUSB_DEV pdev; PHUB2_EXTENSION hub_ext; ULONG port_idx; PUSB_CTRL_SETUP_PACKET psetup; NTSTATUS status; LONG i; PHCD hcd; USE_BASIC_NON_PENDING_IRQL; if (purb == NULL) return; if (pcontext == NULL) { usb_free_mem(purb); return; } usb_dbg_print(DBGLVL_ULTRA, ("hub_int_completion(): entering...\n")); pdev = purb->pdev; hub_ext = pcontext; lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); usb_free_mem(purb); return; } hcd = pdev->hcd; if (purb->status == STATUS_SUCCESS) { for(i = 1; i <= hub_ext->port_count; i++) { if (hub_ext->int_data_buf[i >> 3] & (1 << i)) { break; } } if (i > hub_ext->port_count) { //no status change, re-initialize the int request unlock_dev(pdev, TRUE); usb_free_mem(purb); hub_start_int_request(pdev); return; } port_idx = (ULONG)i; //re-use the urb to get port status purb->pendp = &pdev->default_endp; purb->data_buffer = (PUCHAR) & hub_ext->port_status; purb->data_length = sizeof(USB_PORT_STATUS); purb->pdev = pdev; purb->context = hub_ext; purb->pdev = pdev; purb->completion = hub_get_port_status_completion; purb->reference = port_idx; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0xa3; //host-device class other recepient psetup->bRequest = USB_REQ_GET_STATUS; psetup->wValue = 0; psetup->wIndex = (USHORT) port_idx; psetup->wLength = 4; purb->pirp = NULL; unlock_dev(pdev, TRUE); status = hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb); if (usb_error(status)) { usb_free_mem(purb); purb = NULL; } else if (status == STATUS_SUCCESS) { // this is for root hub hcd->hcd_generic_urb_completion(purb, purb->context); } return; } else { unlock_dev(pdev, TRUE); if (usb_halted(purb->status)) { //let's reset pipe usb_reset_pipe(pdev, purb->pendp, hub_reset_pipe_completion, NULL); } //unexpected error usb_free_mem(purb); purb = NULL; } return; } VOID hub_get_port_status_completion(PURB purb, PVOID context) { PUSB_DEV pdev; PUSB_ENDPOINT pendp; BYTE port_idx; PHUB2_EXTENSION hub_ext; PUSB_CTRL_SETUP_PACKET psetup; NTSTATUS status; PHCD hcd; USE_BASIC_NON_PENDING_IRQL; if (purb == NULL || context == NULL) return; usb_dbg_print(DBGLVL_MAXIMUM, ("hub_get_port_feature_completion(): entering...\n")); pdev = purb->pdev; pendp = purb->pendp; lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); usb_free_mem(purb); return; } hcd = pdev->hcd; if (usb_error(purb->status)) { unlock_dev(pdev, TRUE); purb->status = 0; //simply retry the request refer to item 55 in document status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb); if (status != STATUS_PENDING) { if (status == STATUS_SUCCESS) { hcd->hcd_generic_urb_completion(purb, purb->context); } else { // // must be fatal error // FIXME: better to pass it to the completion for further // processing? // usb_free_mem(purb); } } return; } hub_ext = hub_ext_from_dev(pdev); port_idx = (BYTE) purb->reference; usb_dbg_print(DBGLVL_MAXIMUM, ("hub_get_port_stataus_completion(): port_idx=0x%x, hcd =0x%x, \ pdev=0x%x, purb=0x%x, hub_ext=0x%x, portsc=0x%x \n", port_idx, pdev->hcd, pdev, purb, hub_ext, *((PULONG) purb->data_buffer))); psq_enqueue(&hub_ext->port_status_queue[port_idx], *((PULONG) purb->data_buffer)); //reuse the urb to clear the feature RtlZeroMemory(purb, sizeof(URB)); purb->data_buffer = NULL; purb->data_length = 0; purb->pendp = &pdev->default_endp; purb->pdev = pdev; purb->context = (PVOID) & hub_ext->port_status; purb->pdev = pdev; purb->completion = hub_clear_port_feature_completion; purb->reference = port_idx; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0x23; //host-device class port recepient psetup->bRequest = USB_REQ_CLEAR_FEATURE; psetup->wIndex = port_idx; psetup->wLength = 0; purb->pirp = NULL; if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_CONNECTION) { psetup->wValue = USB_PORT_FEAT_C_CONNECTION; } else if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_ENABLE) { psetup->wValue = USB_PORT_FEAT_C_ENABLE; } else if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_SUSPEND) { psetup->wValue = USB_PORT_FEAT_C_SUSPEND; } else if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_OVERCURRENT) { psetup->wValue = USB_PORT_FEAT_C_OVER_CURRENT; } else if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_RESET) { psetup->wValue = USB_PORT_FEAT_C_RESET; } unlock_dev(pdev, TRUE); status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb); // if( status != STATUS_SUCCESS ) if (status != STATUS_PENDING) { hcd->hcd_generic_urb_completion(purb, purb->context); } /*else if( usb_error( status ) ) { usb_free_mem( purb ); return; } */ return; } VOID hub_clear_port_feature_completion(PURB purb, PVOID context) { BYTE port_idx; LONG i; BOOLEAN event_post, brh; ULONG pc; PHCD hcd; NTSTATUS status; PUSB_DEV pdev; PHUB2_EXTENSION hub_ext = NULL; PUSB_DEV_MANAGER dev_mgr; PUSB_CTRL_SETUP_PACKET psetup; USE_BASIC_NON_PENDING_IRQL; if (purb == NULL) return; if (context == NULL) { usb_free_mem(purb); return; } usb_dbg_print(DBGLVL_MAXIMUM, ("hub_clear_port_feature_completion(): entering...\n")); pdev = purb->pdev; port_idx = (BYTE) purb->reference; lock_dev(pdev, TRUE); dev_mgr = dev_mgr_from_dev(pdev); hcd = pdev->hcd; brh = (BOOLEAN) (dev_class(pdev) == USB_DEV_CLASS_ROOT_HUB); if (usb_error(purb->status)) { unlock_dev(pdev, TRUE); purb->status = 0; // retry the request status = hcd->hcd_submit_urb(hcd, purb->pdev, purb->pendp, purb); if (status != STATUS_PENDING) { if (status == STATUS_SUCCESS) { hcd->hcd_generic_urb_completion(purb, purb->context); } else { // // FIXME: should we pass the error to the completion directly // instead of forstall it here? // // do not think the device is workable, no requests to it any more. // including the int polling if (purb) usb_free_mem(purb); if (port_idx) hub_check_reset_port_status(pdev, port_idx); } } return; } if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); usb_free_mem(purb); return; } pc = ((PUSB_PORT_STATUS) context)->wPortChange; if (pc) { // the bits are tested in ascending order if (pc & USB_PORT_STAT_C_CONNECTION) { pc &= ~USB_PORT_STAT_C_CONNECTION; } else if (pc & USB_PORT_STAT_C_ENABLE) { pc &= ~USB_PORT_STAT_C_ENABLE; } else if (pc & USB_PORT_STAT_C_SUSPEND) { pc &= ~USB_PORT_STAT_C_SUSPEND; } else if (pc & USB_PORT_STAT_C_OVERCURRENT) { pc &= ~USB_PORT_STAT_C_OVERCURRENT; } else if (pc & USB_PORT_STAT_C_RESET) { pc &= ~USB_PORT_STAT_C_RESET; } } ((PUSB_PORT_STATUS) context)->wPortChange = (USHORT) pc; hub_ext = hub_ext_from_dev(pdev); if (pc) { //some other status change on the port still active psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_CONNECTION) { psetup->wValue = USB_PORT_FEAT_C_CONNECTION; } else if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_ENABLE) { psetup->wValue = USB_PORT_FEAT_C_ENABLE; } else if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_SUSPEND) { psetup->wValue = USB_PORT_FEAT_C_SUSPEND; } else if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_OVERCURRENT) { psetup->wValue = USB_PORT_FEAT_C_OVER_CURRENT; } else if (hub_ext->port_status.wPortChange & USB_PORT_STAT_C_RESET) { psetup->wValue = USB_PORT_FEAT_C_RESET; } unlock_dev(pdev, TRUE); status = hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb); if (status != STATUS_PENDING) { if (status == STATUS_SUCCESS) { usb_dbg_print(DBGLVL_MAXIMUM, ("hub_clear_port_status_completion(): port_idx=0x%x, hcd=0x%x, \ pdev=0x%x, purb=0x%x, hub_ext=0x%x, wPortChange=0x%x \n", port_idx, pdev->hcd, pdev, purb, hub_ext, pc)); hcd->hcd_generic_urb_completion(purb, purb->context); } else { usb_dbg_print(DBGLVL_MAXIMUM, (" hub_clear_port_feature_completion(): \ error=0x%x\n", status)); // usb_free_mem( purb ); goto LBL_SCAN_PORT_STAT; } } return; } for(i = 1; i <= hub_ext->port_count; i++) { if (hub_ext->int_data_buf[i >> 3] & (1 << i)) { break; } } //clear the port-change map, we have get port i's status. hub_ext->int_data_buf[i >> 3] &= ~(1 << i); //rescan to find some other port that has status change for(i = 1; i <= hub_ext->port_count; i++) { if (hub_ext->int_data_buf[i >> 3] & (1 << i)) { break; } } if (i <= hub_ext->port_count) { //still has port-change pending, get the port status change port_idx = (UCHAR) i; //re-use the urb purb->data_buffer = (PUCHAR) & hub_ext->port_status; purb->data_length = sizeof(USB_PORT_STATUS); purb->pendp = &pdev->default_endp; purb->pdev = pdev; purb->context = hub_ext; purb->pdev = pdev; purb->completion = hub_get_port_status_completion; purb->reference = port_idx; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0xa3; //host-device class other recepient psetup->bRequest = USB_REQ_GET_STATUS; psetup->wValue = 0; psetup->wIndex = port_idx; psetup->wLength = 4; purb->pirp = NULL; unlock_dev(pdev, TRUE); status = hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb); if (status != STATUS_PENDING) { if (status == STATUS_SUCCESS) { hcd->hcd_generic_urb_completion(purb, purb->context); } else { //must be fatal error // usb_free_mem( purb ); goto LBL_SCAN_PORT_STAT; } } return; } unlock_dev(pdev, TRUE); LBL_SCAN_PORT_STAT: //all status changes are cleared if (purb) usb_free_mem(purb); purb = NULL; KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock); lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { // // if reset is in process, the dev_mgr_disconnect_dev will continue // the following resets // unlock_dev(pdev, TRUE); KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock); return; } //at last we wake up thread if some port have status change to process port_idx = 0; for(i = 1, event_post = FALSE; i <= hub_ext->port_count; i++) { if (psq_is_empty(&hub_ext->port_status_queue[i]) == FALSE) { if (port_state(hub_ext->port_status_queue[i].port_flags) == STATE_IDLE || port_state(hub_ext->port_status_queue[i].port_flags) == STATE_WAIT_ADDRESSED) { // have status in the queue pending // STATE_WAIT_ADDRESSED is added to avoid some bad mannered // hub to disturb the reset process hub_post_esq_event(pdev, (BYTE) i, hub_event_examine_status_que); } else if (port_state(hub_ext->port_status_queue[i].port_flags) == STATE_WAIT_RESET_COMPLETE) { //there is only one reset at one time port_idx = (BYTE) i; } } } unlock_dev(pdev, TRUE); KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock); if (port_idx) hub_check_reset_port_status(pdev, port_idx); //reinitialize the int request, here to reduce some uncertainty of concurrency hub_start_int_request(pdev); return; } VOID hub_event_examine_status_que(PUSB_DEV pdev, ULONG event, ULONG context, //hub_ext ULONG param //port_idx ) { PHUB2_EXTENSION hub_ext; USB_PORT_STATUS ps; PUSB_DEV pchild_dev; PTIMER_SVC ptimer; PUSB_DEV_MANAGER dev_mgr; USE_NON_PENDING_IRQL; UNREFERENCED_PARAMETER(event); if (pdev == NULL || context == 0 || param == 0) return; while (TRUE) { lock_dev(pdev, FALSE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, FALSE); break; } dev_mgr = dev_mgr_from_dev(pdev); hub_ext = hub_ext_from_dev(pdev); if (psq_is_empty(&hub_ext->port_status_queue[param])) { set_port_state(hub_ext->port_status_queue[param].port_flags, STATE_IDLE); unlock_dev(pdev, FALSE); break; } *((ULONG *) & ps) = psq_outqueue(&hub_ext->port_status_queue[param]); pchild_dev = hub_ext->child_dev[param]; hub_ext->child_dev[param] = 0; usb_dbg_print(DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): dev_addr=0x%x, port=0x%x, wPortChange=0x%x, wPortStatus=0x%x\n", pdev->dev_addr, param, ps.wPortChange, ps.wPortStatus)); unlock_dev(pdev, FALSE); if (pchild_dev != NULL) dev_mgr_disconnect_dev(pchild_dev); if (((ps.wPortChange & USB_PORT_STAT_C_ENABLE) && ((pdev->flags & USB_DEV_CLASS_MASK) != USB_DEV_CLASS_ROOT_HUB)) || (ps.wPortChange & USB_PORT_STAT_C_OVERCURRENT) || (ps.wPortChange & USB_PORT_STAT_C_RESET) || ((ps.wPortChange & USB_PORT_STAT_C_CONNECTION) && !(ps.wPortStatus & USB_PORT_STAT_CONNECTION))) { usb_dbg_print(DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): error occured, portc=0x%x, ports=0x%x\n", ps.wPortChange, ps.wPortStatus)); lock_dev(pdev, FALSE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, FALSE); break; } if (psq_is_empty(&hub_ext->port_status_queue[param])) { set_port_state(hub_ext->port_status_queue[param].port_flags, STATE_IDLE); } else { set_port_state(hub_ext->port_status_queue[param].port_flags, STATE_EXAMINE_STATUS_QUE); } unlock_dev(pdev, FALSE); continue; } else if ((ps.wPortChange & USB_PORT_STAT_C_CONNECTION) && (ps.wPortStatus & USB_PORT_STAT_CONNECTION) && psq_is_empty(&hub_ext->port_status_queue[param])) { KeAcquireSpinLock(&dev_mgr->timer_svc_list_lock, &old_irql); lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql); usb_dbg_print(DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): dev lost\n")); break; } ptimer = alloc_timer_svc(&dev_mgr->timer_svc_pool, 1); if (ptimer == NULL) { unlock_dev(pdev, TRUE); KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql); usb_dbg_print(DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): timer can not allocated\n")); break; } //a new connection usb_dbg_print(DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): new connection comes\n")); ptimer->counter = 0; ptimer->threshold = 21; //100 ms if (ps.wPortStatus & USB_PORT_STAT_LOW_SPEED) ptimer->threshold = 51; //500 ms ptimer->context = param; ptimer->pdev = pdev; ptimer->func = hub_timer_wait_dev_stable; InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link); pdev->ref_count++; set_port_state(hub_ext->port_status_queue[param].port_flags, STATE_WAIT_STABLE); unlock_dev(pdev, TRUE); KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql); break; } else { usb_dbg_print(DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): unknown error\n")); continue; } } return; } VOID hub_timer_wait_dev_stable(PUSB_DEV pdev, PVOID context //port-index ) { PHUB2_EXTENSION hub_ext; ULONG param; PUSB_DEV_MANAGER dev_mgr; USE_BASIC_NON_PENDING_IRQL; if (pdev == NULL || context == 0) return; dev_mgr = dev_mgr_from_dev(pdev); param = (ULONG) context; KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock); lock_dev(pdev, TRUE); pdev->ref_count--; if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { goto LBL_OUT; } hub_ext = hub_ext_from_dev(pdev); if (!psq_is_empty(&hub_ext->port_status_queue[param])) { //error occured, normally we should not receive event here set_port_state(hub_ext->port_status_queue[param].port_flags, STATE_EXAMINE_STATUS_QUE); hub_post_esq_event(pdev, (BYTE) param, hub_event_examine_status_que); } else { set_port_state(hub_ext->port_status_queue[param].port_flags, STATE_WAIT_RESET); hub_post_esq_event(pdev, (BYTE) param, hub_event_dev_stable); } LBL_OUT: unlock_dev(pdev, TRUE); KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock); return; } VOID hub_event_dev_stable(PUSB_DEV pdev, ULONG event, ULONG context, //hub_ext ULONG param //port_idx ) { PHUB2_EXTENSION hub_ext; PUSB_EVENT pevent, pevent1; PLIST_ENTRY pthis, pnext; BOOLEAN que_exist; PHCD hcd; PUSB_DEV_MANAGER dev_mgr; NTSTATUS status; PURB purb; PUSB_CTRL_SETUP_PACKET psetup; USE_NON_PENDING_IRQL; UNREFERENCED_PARAMETER(event); if (pdev == NULL || context == 0 || param == 0) return; dev_mgr = dev_mgr_from_dev(pdev); KeAcquireSpinLock(&dev_mgr->event_list_lock, &old_irql); lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) goto LBL_OUT; hub_ext = hub_ext_from_dev(pdev); hcd = pdev->hcd; pevent = alloc_event(&dev_mgr->event_pool, 1); if (pevent == NULL) goto LBL_OUT; pevent->event = USB_EVENT_WAIT_RESET_PORT; pevent->pdev = pdev; pevent->context = (ULONG) hub_ext; pevent->param = param; pevent->flags = USB_EVENT_FLAG_QUE_RESET; pevent->process_event = NULL; //hub_event_reset_port_complete; pevent->process_queue = NULL; //hub_event_reset_process_queue; pevent->pnext = NULL; ListFirst(&dev_mgr->event_list, pthis); que_exist = FALSE; while (pthis) { //insert the event in to the wait-queue pevent1 = (PUSB_EVENT) pthis; if (pevent1->event == USB_EVENT_WAIT_RESET_PORT) { while (pevent1->pnext) pevent1 = pevent1->pnext; pevent1->pnext = pevent; que_exist = TRUE; break; } ListNext(&dev_mgr->event_list, pthis, pnext); pthis = pnext; } if (!que_exist) { //Let's start a reset port request InsertHeadList(&dev_mgr->event_list, &pevent->event_link); purb = usb_alloc_mem(NonPagedPool, sizeof(URB)); if (!purb) goto LBL_OUT; RtlZeroMemory(purb, sizeof(URB)); purb->data_buffer = NULL; purb->data_length = 0; purb->pendp = &pdev->default_endp; purb->context = hub_ext; purb->pdev = pdev; purb->completion = hub_start_reset_port_completion; //hub_int_completion; purb->reference = param; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0x23; //host-device other recepient psetup->bRequest = USB_REQ_SET_FEATURE; psetup->wValue = USB_PORT_FEAT_RESET; psetup->wIndex = (USHORT) param; psetup->wLength = 0; purb->pirp = NULL; //enter another state set_port_state(hub_ext->port_status_queue[param].port_flags, STATE_WAIT_RESET_COMPLETE); unlock_dev(pdev, TRUE); KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); status = hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb); if (status != STATUS_PENDING) { //must be fatal error usb_free_mem(purb); hub_reexamine_port_status_queue(pdev, param, FALSE); if (hub_remove_reset_event(pdev, param, FALSE)) hub_start_next_reset_port(dev_mgr, FALSE); } return; } LBL_OUT: unlock_dev(pdev, TRUE); KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); return; } VOID hub_start_reset_port_completion(PURB purb, PVOID context) { PUSB_DEV pdev; PUSB_ENDPOINT pendp; PUSB_DEV_MANAGER dev_mgr = NULL; NTSTATUS status = STATUS_SUCCESS; ULONG port_idx; PHCD hcd; USE_BASIC_NON_PENDING_IRQL; if (purb == NULL) return; if (context == NULL) { //fatal error no retry. usb_free_mem(purb); return; } pdev = purb->pdev; pendp = purb->pendp; port_idx = purb->reference; lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); usb_free_mem(purb); goto LBL_FREE_EVENT; } hcd = pdev->hcd; dev_mgr = dev_mgr_from_dev(pdev); unlock_dev(pdev, TRUE); status = purb->status; usb_free_mem(purb); if (!usb_error(status)) { return; } LBL_FREE_EVENT: //since we have no patient to retry the dev, we should remove the event of //wait_reset_port on the port from the event list. and if possible, start //another reset process. note other port on the dev still have chance to be //reset if necessary. hub_reexamine_port_status_queue(pdev, port_idx, TRUE); if (hub_remove_reset_event(pdev, port_idx, TRUE)) hub_start_next_reset_port(dev_mgr, TRUE); return; } VOID hub_set_address_completion(PURB purb, PVOID context) { PUSB_DEV pdev, hub_dev; PUSB_ENDPOINT pendp; PUSB_DEV_MANAGER dev_mgr; NTSTATUS status; ULONG port_idx; PHCD hcd; USE_BASIC_NON_PENDING_IRQL; hcd_dbg_print(DBGLVL_MAXIMUM, ("hub_set_address_completion: purb=%p context=%p\n", purb, context)); if (purb == NULL) return; if (context == NULL) { //fatal error no retry. usb_free_mem(purb); return; } pdev = purb->pdev; pendp = purb->pendp; port_idx = purb->reference; lock_dev(pdev, TRUE); hcd = pdev->hcd; dev_mgr = dev_mgr_from_dev(pdev); hub_dev = pdev->parent_dev; port_idx = pdev->port_idx; if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); usb_free_mem(purb); //some error occured, let's start the next reset event goto LBL_RESET_NEXT; } pdev->flags &= ~USB_DEV_STATE_MASK; pdev->flags |= USB_DEV_STATE_ADDRESSED; unlock_dev(pdev, TRUE); status = purb->status; if (usb_error(status)) { //retry the urb purb->status = 0; hcd_dbg_print(DBGLVL_MAXIMUM, ("hub_set_address_completion: can not set address\n")); status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb); //some error occured, disable the port if (status != STATUS_PENDING) { usb_free_mem(purb); status = hub_disable_port_request(hub_dev, (UCHAR) port_idx); } return; } usb_free_mem(purb); //let address settle usb_wait_ms_dpc(10); //let's config the dev dev_mgr_start_config_dev(pdev); LBL_RESET_NEXT: //second, remove the event in the queue hub_reexamine_port_status_queue(hub_dev, port_idx, TRUE); if (hub_remove_reset_event(hub_dev, port_idx, TRUE)) hub_start_next_reset_port(dev_mgr, TRUE); return; }; VOID hub_disable_port_completion(PURB purb, PVOID pcontext) { PUSB_DEV pdev; PUSB_DEV_MANAGER dev_mgr; UCHAR port_idx; PUSB_ENDPOINT pendp; PUSB_CTRL_SETUP_PACKET psetup; UNREFERENCED_PARAMETER(pcontext); if (purb == NULL) return; pdev = purb->pdev; pendp = purb->pendp; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; port_idx = (UCHAR) psetup->wIndex; dev_mgr = dev_mgr_from_dev(pdev); usb_free_mem(purb); hub_reexamine_port_status_queue(pdev, port_idx, TRUE); if (hub_remove_reset_event(pdev, port_idx, TRUE)) hub_start_next_reset_port(dev_mgr, TRUE); return; } //caller should guarantee the validity of the dev NTSTATUS hub_disable_port_request(PUSB_DEV pdev, UCHAR port_idx) { PURB purb; PUSB_ENDPOINT pendp; PHUB2_EXTENSION hub_ext; PUSB_CTRL_SETUP_PACKET psetup; NTSTATUS status; PHCD hcd; USE_BASIC_NON_PENDING_IRQL; if (pdev == NULL || port_idx == 0) return STATUS_INVALID_PARAMETER; lock_dev(pdev, FALSE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, FALSE); return STATUS_DEVICE_DOES_NOT_EXIST; } purb = usb_alloc_mem(NonPagedPool, sizeof(URB)); if (purb == NULL) { unlock_dev(pdev, FALSE); return STATUS_NO_MEMORY; } RtlZeroMemory(purb, sizeof(URB)); purb->flags = 0; purb->status = STATUS_SUCCESS; hub_ext = hub_ext_from_dev(pdev); purb->data_buffer = NULL; purb->data_length = 0; pendp = purb->pendp = &pdev->default_endp; purb->pdev = pdev; purb->completion = hub_disable_port_completion; purb->context = hub_ext; purb->pirp = NULL; purb->reference = 0; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0x23; //host-device other recepient psetup->bRequest = USB_REQ_CLEAR_FEATURE; //clear_feature psetup->wValue = USB_PORT_FEAT_ENABLE; psetup->wIndex = (USHORT) port_idx; psetup->wLength = 0; purb->pirp = NULL; //enter another state hcd = pdev->hcd; unlock_dev(pdev, FALSE); status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb); if (status == STATUS_PENDING) return status; usb_free_mem(purb); return status; } BOOLEAN hub_remove_reset_event(PUSB_DEV pdev, ULONG port_idx, BOOLEAN from_dpc) { PUSB_DEV_MANAGER dev_mgr; PLIST_ENTRY pthis, pnext; PUSB_EVENT pevent, pnext_event; BOOLEAN found; KIRQL old_irql = 0; if (pdev == NULL) return FALSE; if (port_idx == 0) return FALSE; dev_mgr = dev_mgr_from_dev(pdev); found = FALSE; if (from_dpc) KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock); else KeAcquireSpinLock(&dev_mgr->event_list_lock, &old_irql); ListFirst(&dev_mgr->event_list, pthis); while (pthis) { pevent = (PUSB_EVENT) pthis; if (pevent->event == USB_EVENT_WAIT_RESET_PORT && (pevent->flags & USB_EVENT_FLAG_QUE_TYPE) == USB_EVENT_FLAG_QUE_RESET) { if (pevent->pdev == pdev && pevent->param == port_idx) { //remove it RemoveEntryList(&pevent->event_link); pnext_event = pevent->pnext; free_event(&dev_mgr->event_pool, pevent); if (pnext_event) InsertHeadList(&dev_mgr->event_list, &pnext_event->event_link); found = TRUE; break; } } ListNext(&dev_mgr->event_list, pthis, pnext); pthis = pnext; } if (from_dpc) KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock); else KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); return found; } BOOLEAN hub_start_next_reset_port(PUSB_DEV_MANAGER dev_mgr, BOOLEAN from_dpc) { PLIST_ENTRY pthis, pnext; PUSB_EVENT pevent, pnext_event; PUSB_DEV pdev = NULL; PHUB2_EXTENSION hub_ext; BOOLEAN bret; PURB purb = NULL; BOOLEAN processed; PUSB_CTRL_SETUP_PACKET psetup; PHCD hcd = NULL; USE_NON_PENDING_IRQL; if (dev_mgr == NULL) return FALSE; bret = FALSE; processed = FALSE; if (from_dpc) KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock); else KeAcquireSpinLock(&dev_mgr->event_list_lock, &old_irql); ListFirst(&dev_mgr->event_list, pthis); while ((pevent = (PUSB_EVENT) pthis)) { while (pevent->event == USB_EVENT_WAIT_RESET_PORT && (pevent->flags & USB_EVENT_FLAG_QUE_TYPE) == USB_EVENT_FLAG_QUE_RESET) { processed = TRUE; pdev = pevent->pdev; lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); pnext_event = pevent->pnext; free_event(&dev_mgr->event_pool, pevent); pevent = pnext_event; if (pevent == NULL) { bret = FALSE; break; } continue; } purb = usb_alloc_mem(NonPagedPool, sizeof(URB)); if (!purb) { if (from_dpc) KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock); else KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); return FALSE; } RtlZeroMemory(purb, sizeof(URB)); purb->data_buffer = NULL; purb->data_length = 0; purb->pendp = &pdev->default_endp; hub_ext = hub_ext_from_dev(pdev); purb->context = hub_ext; purb->pdev = pdev; purb->completion = hub_start_reset_port_completion; purb->reference = pevent->param; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0x23; //host-device other recepient psetup->bRequest = 3; //set_feature psetup->wValue = USB_PORT_FEAT_RESET; psetup->wIndex = (USHORT) pevent->param; psetup->wLength = 0; purb->pirp = NULL; hcd = pdev->hcd; set_port_state(hub_ext->port_status_queue[pevent->param].port_flags, STATE_WAIT_RESET_COMPLETE); unlock_dev(pdev, TRUE); bret = TRUE; break; } if (!processed) { ListNext(&dev_mgr->event_list, pthis, pnext); pthis = pnext; } else break; } if (from_dpc) KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock); else KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); if (processed && bret) { if (hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb) != STATUS_PENDING) { //fatal error usb_free_mem(purb); bret = FALSE; //do not know what to do } } if (pthis == NULL) bret = TRUE; return bret; } // //must have event-list-lock and dev-lock acquired // VOID hub_post_esq_event(PUSB_DEV pdev, BYTE port_idx, PROCESS_EVENT pe) { PUSB_DEV_MANAGER dev_mgr; PUSB_EVENT pevent; if (pdev == NULL || port_idx == 0 || pe == NULL) return; dev_mgr = dev_mgr_from_dev(pdev); pevent = alloc_event(&dev_mgr->event_pool, 1); if (!pevent) return; pevent->event = USB_EVENT_DEFAULT; pevent->process_queue = event_list_default_process_queue; pevent->process_event = pe; pevent->context = (ULONG) hub_ext_from_dev(pdev); pevent->param = port_idx; pevent->flags = USB_EVENT_FLAG_ACTIVE; pevent->pdev = pdev; pevent->pnext = NULL; InsertTailList(&dev_mgr->event_list, &pevent->event_link); KeSetEvent(&dev_mgr->wake_up_event, 0, FALSE); // usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_post_esq_event(): current element in event list is 0x%x\n", // dbg_count_list( &dev_mgr->event_list ) ) ); return; } // called only in hub_clear_port_feature_completion BOOLEAN hub_check_reset_port_status(PUSB_DEV pdev, LONG port_idx) { PUSB_DEV_MANAGER dev_mgr; PHUB2_EXTENSION hub_ext; BOOLEAN bReset; USB_PORT_STATUS port_status; PUSB_DEV pdev2; PURB purb2; PHCD hcd; PUSB_CTRL_SETUP_PACKET psetup; ULONG status; USE_BASIC_NON_PENDING_IRQL; //let's check whether the status change is a reset complete usb_dbg_print(DBGLVL_MAXIMUM, ("hub_check_reset_port_status(): entering...\n")); dev_mgr = dev_mgr_from_dev(pdev); KeAcquireSpinLockAtDpcLevel(&dev_mgr->dev_list_lock); lock_dev(pdev, TRUE); dev_mgr = dev_mgr_from_dev(pdev); hcd = pdev->hcd; if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); KeReleaseSpinLockFromDpcLevel(&dev_mgr->dev_list_lock); return FALSE; } hub_ext = hub_ext_from_dev(pdev); port_status = psq_peek(&hub_ext->port_status_queue[port_idx], 0); bReset = FALSE; if (port_status.wPortChange & USB_PORT_STAT_C_RESET) bReset = TRUE; pdev2 = NULL; purb2 = NULL; if (bReset && (port_state(hub_ext->port_status_queue[port_idx].port_flags) == STATE_WAIT_RESET_COMPLETE) && (psq_count(&hub_ext->port_status_queue[port_idx]) == 1)) { // a port-reset complete, empty the queue, keep the state psq_outqueue(&hub_ext->port_status_queue[port_idx]); set_port_state(hub_ext->port_status_queue[port_idx].port_flags, STATE_WAIT_ADDRESSED); //let's new a dev, and start the set-addr request if (hub_ext->child_dev[port_idx] == 0) { pdev2 = hub_ext->child_dev[port_idx] = dev_mgr_alloc_device(dev_mgr, hcd); if (pdev2) { purb2 = usb_alloc_mem(NonPagedPool, sizeof(URB)); if (!purb2) { dev_mgr_free_device(dev_mgr, pdev2); pdev2 = hub_ext->child_dev[port_idx] = NULL; } else { if (port_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) { pdev2->flags |= USB_DEV_FLAG_LOW_SPEED; } else if (port_status.wPortStatus & USB_PORT_STAT_HIGH_SPEED) pdev2->flags |= USB_DEV_FLAG_HIGH_SPEED; pdev2->parent_dev = pdev; pdev2->port_idx = (UCHAR) port_idx; pdev2->ref_count++; RtlZeroMemory(purb2, sizeof(URB)); purb2->pdev = pdev2; purb2->pendp = &pdev2->default_endp; purb2->context = hub_ext; purb2->completion = hub_set_address_completion; InitializeListHead(&purb2->trasac_list); purb2->reference = port_idx; purb2->pirp = 0; psetup = (PUSB_CTRL_SETUP_PACKET) purb2->setup_packet; psetup->bmRequestType = 0; psetup->bRequest = USB_REQ_SET_ADDRESS; psetup->wValue = pdev2->dev_addr; } } } if (pdev2 && purb2) { //creation success, emit the urb //add to dev list InsertTailList(&dev_mgr->dev_list, &pdev2->dev_link); unlock_dev(pdev, TRUE); KeReleaseSpinLockFromDpcLevel(&dev_mgr->dev_list_lock); status = hcd->hcd_submit_urb(hcd, pdev2, purb2->pendp, purb2); lock_dev(pdev2, TRUE); pdev2->ref_count--; usb_dbg_print(DBGLVL_MAXIMUM, ("hub_check_reset_port_status(): new dev ref_count=0x%x\n", pdev2->ref_count)); unlock_dev(pdev2, TRUE); if (status != STATUS_PENDING) { usb_free_mem(purb2); //??? do we need to lock it for SMP? //dev_mgr_free_device( dev_mgr, pdev2 ), let dev_mgr_thread to clean it; // disable the port if (hub_disable_port_request(pdev, (UCHAR) port_idx) != STATUS_PENDING) goto LBL_RESET_FAIL; } return TRUE; } } else { usb_dbg_print(DBGLVL_MAXIMUM, ("hub_check_reset_port_status(): not a correct reset status\n")); } unlock_dev(pdev, TRUE); KeReleaseSpinLockFromDpcLevel(&dev_mgr->dev_list_lock); LBL_RESET_FAIL: //Any event other than reset cause the reset process stall and another //pending reset-port requeset is serviced hub_reexamine_port_status_queue(pdev, port_idx, TRUE); if (hub_remove_reset_event(pdev, port_idx, TRUE)) hub_start_next_reset_port(dev_mgr, TRUE); return FALSE; } VOID hub_reexamine_port_status_queue(PUSB_DEV hub_dev, ULONG port_idx, BOOLEAN from_dpc) { PHUB2_EXTENSION hub_ext; PUSB_DEV_MANAGER dev_mgr; USE_NON_PENDING_IRQL; if (hub_dev == NULL || port_idx == 0) return; dev_mgr = dev_mgr_from_dev(hub_dev); if (from_dpc) KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock); else KeAcquireSpinLock(&dev_mgr->event_list_lock, &old_irql); lock_dev(hub_dev, TRUE); if (dev_state(hub_dev) != USB_DEV_STATE_ZOMB) { hub_ext = hub_ext_from_dev(hub_dev); if (psq_is_empty(&hub_ext->port_status_queue[port_idx])) { set_port_state(hub_ext->port_status_queue[port_idx].port_flags, STATE_IDLE); } else { set_port_state(hub_ext->port_status_queue[port_idx].port_flags, STATE_EXAMINE_STATUS_QUE); hub_post_esq_event(hub_dev, (UCHAR) port_idx, hub_event_examine_status_que); } } unlock_dev(hub_dev, TRUE); if (from_dpc) KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock); else KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); return; } BOOLEAN hub_connect(PDEV_CONNECT_DATA param, DEV_HANDLE dev_handle) { URB urb, *purb; CHAR buf[512]; DEV_HANDLE endp_handle; USB_DEVICE_DESC dev_desc; PUSB_CONFIGURATION_DESC pcfg_desc; PUSB_INTERFACE_DESC pif_desc; PUSB_CTRL_SETUP_PACKET psetup; NTSTATUS status; LONG i, j, found, cfg_val = 0; PUSB_DEV_MANAGER dev_mgr; PUSB_DEV pdev; if (param == NULL || dev_handle == 0) return FALSE; dev_mgr = param->dev_mgr; pcfg_desc = (PUSB_CONFIGURATION_DESC) buf; endp_handle = dev_handle | 0xffff; UsbBuildGetDescriptorRequest(&urb, endp_handle, USB_DT_DEVICE, 0, 0, (&dev_desc), (sizeof(USB_DEVICE_DESC)), NULL, 0, 0); status = usb_submit_urb(dev_mgr, &urb); if (status != STATUS_SUCCESS) return FALSE; found = FALSE; for(i = 0; i < dev_desc.bNumConfigurations; i++) { UsbBuildGetDescriptorRequest(&urb, endp_handle, USB_DT_CONFIG, (USHORT) i, 0, buf, 512, NULL, 0, 0); status = usb_submit_urb(dev_mgr, &urb); if (status != STATUS_SUCCESS) { return FALSE; } status = usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev); if (status != STATUS_SUCCESS) return FALSE; pif_desc = (PUSB_INTERFACE_DESC) & buf[sizeof(USB_CONFIGURATION_DESC)]; for(j = 0; j < pcfg_desc->bNumInterfaces; j++) { if (pif_desc->bInterfaceClass == USB_CLASS_HUB && pif_desc->bInterfaceSubClass == 0 && pif_desc->bNumEndpoints == 1) { if ((pif_desc->bInterfaceProtocol > 0 && pif_desc->bInterfaceProtocol < 3) || (pif_desc->bInterfaceProtocol == 0 && pdev->flags & USB_DEV_FLAG_HIGH_SPEED) || (pif_desc->bInterfaceProtocol == 0 && !usb2(pdev->hcd))) { found = TRUE; cfg_val = pcfg_desc->bConfigurationValue; break; } } if (usb_skip_if_and_altif((PBYTE *) & pif_desc) == FALSE) { break; } } usb_unlock_dev(pdev); if (found) break; if (usb_skip_one_config((PBYTE *) & pcfg_desc) == FALSE) { break; } } if (found) { purb = usb_alloc_mem(NonPagedPool, sizeof(URB)); if (purb == NULL) return FALSE; psetup = (PUSB_CTRL_SETUP_PACKET) (purb)->setup_packet; urb_init((purb)); purb->endp_handle = endp_handle; purb->data_buffer = NULL; purb->data_length = 0; purb->completion = hub_set_cfg_completion; purb->context = dev_mgr; purb->reference = (LONG) param->pdriver; psetup->bmRequestType = 0; psetup->bRequest = USB_REQ_SET_CONFIGURATION; psetup->wValue = (USHORT) cfg_val; psetup->wIndex = 0; psetup->wLength = 0; status = usb_submit_urb(dev_mgr, purb); if (status != STATUS_PENDING) { usb_free_mem(purb); return FALSE; } return TRUE; } return FALSE; } VOID hub_set_interface_completion(PURB purb, PVOID pcontext); VOID hub_set_cfg_completion(PURB purb, PVOID pcontext) { PUSB_DEV_MANAGER dev_mgr; PUSB_DRIVER pdriver; ULONG endp_handle, dev_handle; PUSB_CTRL_SETUP_PACKET psetup; UCHAR if_idx = 0; PUSB_DEV pdev; PUSB_INTERFACE pif; BOOLEAN high_speed, multiple_tt; NTSTATUS status; USE_BASIC_NON_PENDING_IRQL; if (purb == NULL || pcontext == NULL) return; //pdev = NULL; dev_mgr = (PUSB_DEV_MANAGER) pcontext; endp_handle = purb->endp_handle; dev_handle = endp_handle & 0xffff0000; pdriver = (PUSB_DRIVER) purb->reference; high_speed = FALSE; multiple_tt = FALSE; if (purb->status != STATUS_SUCCESS) { goto LBL_ERROR; } status = usb_query_and_lock_dev(dev_mgr, purb->endp_handle, &pdev); if (status != STATUS_SUCCESS) { usb_unlock_dev(pdev); goto LBL_ERROR; } lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); usb_unlock_dev(pdev); goto LBL_ERROR; } if (pdev->flags & USB_DEV_FLAG_HIGH_SPEED) { high_speed = TRUE; hub_if_from_dev(pdev, pif); if (pif->altif_count) { multiple_tt = TRUE; if_idx = pif - &pdev->usb_config->interf[0]; } } unlock_dev(pdev, TRUE); usb_unlock_dev(pdev); if (!high_speed || !multiple_tt) { hub_set_interface_completion(purb, pcontext); return; } psetup = (PUSB_CTRL_SETUP_PACKET) (purb)->setup_packet; urb_init((purb)); // set the mult-tt if exist purb->endp_handle = endp_handle; purb->data_buffer = NULL; purb->data_length = 0; purb->completion = hub_set_interface_completion; purb->context = dev_mgr; purb->reference = (LONG) pdriver; psetup->bmRequestType = 0; psetup->bRequest = USB_REQ_SET_INTERFACE; psetup->wValue = (USHORT) 1; // alternate tt psetup->wIndex = if_idx; // if index psetup->wLength = 0; status = usb_submit_urb(dev_mgr, purb); if (status == STATUS_PENDING) return; LBL_ERROR: usb_free_mem(purb); purb = NULL; return; } void hub_set_interface_completion(PURB purb, PVOID pcontext) { NTSTATUS status; PUSB_CTRL_SETUP_PACKET psetup; PUSB_DEV_MANAGER dev_mgr; PBYTE dev_ext; DEV_HANDLE endp_handle; PUSB_DRIVER pdriver; if (purb == NULL || pcontext == NULL) return; //pdev = NULL; dev_mgr = (PUSB_DEV_MANAGER) pcontext; endp_handle = purb->endp_handle; pdriver = (PUSB_DRIVER) purb->reference; if (purb->status != STATUS_SUCCESS) { usb_free_mem(purb); return; } dev_ext = usb_alloc_mem(NonPagedPool, sizeof(HUB2_EXTENSION)); if (dev_ext == NULL) { goto LBL_OUT; } // //acquire hub descriptor // RtlZeroMemory(dev_ext, sizeof(HUB2_EXTENSION)); urb_init(purb); purb->data_buffer = (PUCHAR) & ((HUB2_EXTENSION *) dev_ext)->hub_desc; purb->endp_handle = endp_handle; purb->data_length = sizeof(USB_HUB_DESCRIPTOR); purb->completion = hub_get_hub_desc_completion; purb->context = (PVOID) dev_mgr; purb->reference = (ULONG) dev_ext; purb->pirp = (PIRP) pdriver; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0xa0; psetup->bRequest = USB_REQ_GET_DESCRIPTOR; psetup->wValue = (0x29 << 8); psetup->wLength = sizeof(USB_HUB_DESCRIPTOR); status = usb_submit_urb(dev_mgr, purb); if (status != STATUS_PENDING) { usb_free_mem(dev_ext); goto LBL_OUT; } return; LBL_OUT: //clear the dev_driver fields in the dev. usb_free_mem(purb); return; } VOID hub_power_on_port_completion(PURB purb, PVOID pcontext) { PUSB_DEV_MANAGER dev_mgr; if (purb == NULL) return; if (pcontext == NULL) goto LBL_OUT; dev_mgr = (PUSB_DEV_MANAGER) pcontext; if (purb->status != STATUS_SUCCESS) { usb_dbg_print(DBGLVL_MAXIMUM, ("hub_power_on_port_completion(): port%d power on failed\n", purb->reference)); } else { usb_dbg_print(DBGLVL_MAXIMUM, ("hub_power_on_port_completion(): port%d power on succeed\n", purb->reference)); } LBL_OUT: usb_free_mem(purb); return; } NTSTATUS hub_power_on_port(PUSB_DEV pdev, UCHAR port_idx) { NTSTATUS status; PUSB_CTRL_SETUP_PACKET psetup; PUSB_DEV_MANAGER dev_mgr; PURB purb; PHCD hcd; USE_BASIC_NON_PENDING_IRQL; if (pdev == NULL || port_idx == 0) return STATUS_INVALID_PARAMETER; purb = usb_alloc_mem(NonPagedPool, sizeof(URB)); if (purb == NULL) return STATUS_NO_MEMORY; urb_init(purb); lock_dev(pdev, FALSE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, FALSE); status = STATUS_DEVICE_DOES_NOT_EXIST; goto LBL_OUT; } dev_mgr = dev_mgr_from_dev(pdev); hcd = pdev->hcd; purb->completion = hub_power_on_port_completion; purb->context = (PVOID) dev_mgr; purb->reference = (ULONG) port_idx; purb->pdev = pdev; purb->pendp = &pdev->default_endp; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0x23; psetup->bRequest = USB_REQ_SET_FEATURE; psetup->wValue = USB_PORT_FEAT_POWER; psetup->wIndex = (WORD) port_idx; psetup->wLength = 0; unlock_dev(pdev, FALSE); status = hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb); if (status != STATUS_PENDING) { goto LBL_OUT; } return STATUS_PENDING; LBL_OUT: usb_free_mem(purb); return status; } void hub_get_hub_desc_completion(PURB purb, PVOID pcontext) { PUSB_DEV_MANAGER dev_mgr; PHUB2_EXTENSION hub_ext; PUSB_DEV pdev; LONG i; PUSB_INTERFACE pif = NULL; ULONG status; LONG port_count; PUSB_DRIVER pdriver; DEV_HANDLE dev_handle; USE_BASIC_NON_PENDING_IRQL; if (purb == NULL) { return; } dev_mgr = (PUSB_DEV_MANAGER) pcontext; hub_ext = (PHUB2_EXTENSION) purb->reference; pdriver = (PUSB_DRIVER) purb->pirp; dev_handle = purb->endp_handle & 0xffff0000; if (pcontext == NULL || purb->reference == 0) goto LBL_OUT; if (purb->status != STATUS_SUCCESS) { goto LBL_OUT; } // obtain the pointer to the dev status = usb_query_and_lock_dev(dev_mgr, purb->endp_handle, &pdev); if (status != STATUS_SUCCESS) { usb_unlock_dev(pdev); goto LBL_OUT; } // safe to release the pdev ref since we are in urb completion usb_unlock_dev(pdev); lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB || dev_mgr_set_driver(dev_mgr, dev_handle, pdriver, pdev) == FALSE) { unlock_dev(pdev, TRUE); goto LBL_OUT; } //transit the state to configured pdev->flags &= ~USB_DEV_STATE_MASK; pdev->flags |= USB_DEV_STATE_CONFIGURED; pdev->dev_ext = (PBYTE) hub_ext; pdev->dev_ext_size = sizeof(HUB2_EXTENSION); port_count = hub_ext->port_count = hub_ext->hub_desc.bNbrPorts; hub_ext->pdev = pdev; for(i = 0; i < pdev->usb_config->if_count; i++) { pif = &pdev->usb_config->interf[i]; if (pif->pusb_if_desc->bInterfaceClass == USB_CLASS_HUB && pif->pusb_if_desc->bInterfaceSubClass == 0 && pif->pusb_if_desc->bInterfaceProtocol < 3 && pif->pusb_if_desc->bNumEndpoints == 1) { hub_ext->pif = pif; break; } } for(i = 0; i < MAX_HUB_PORTS + 1; i++) { psq_init((PPORT_STATUS_QUEUE) hub_ext->port_status_queue); } hub_ext->multiple_tt = (pif->pusb_if_desc->bInterfaceProtocol == 2); unlock_dev(pdev, TRUE); usb_free_mem(purb); hub_start_int_request(pdev); for(i = 0; i < port_count; i++) { hub_power_on_port(pdev, (UCHAR) (i + 1)); } return; LBL_OUT: if (purb) usb_free_mem(purb); if (hub_ext) usb_free_mem(hub_ext); return; } BOOLEAN hub_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle) { UNREFERENCED_PARAMETER(dev_mgr); UNREFERENCED_PARAMETER(dev_handle); return TRUE; } BOOLEAN hub_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle) { PUSB_DEV pdev; //special use of usb_query and lock dev if (usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev) != STATUS_SUCCESS) { //will never be success, since the dev is already in zomb state //at this point, the dev is valid, ref_count is of none use, //no need to lock it if (pdev) { usb_free_mem(pdev->dev_ext); } } return TRUE; } static BOOLEAN hub_lock_unlock_tt(PUSB_DEV pdev, UCHAR port_idx, UCHAR type, BOOLEAN lock) { PHUB2_EXTENSION dev_ext; PULONG pmap = NULL; USE_BASIC_NON_PENDING_IRQL; if (pdev == NULL || port_idx > 127) return FALSE; lock_dev(pdev, FALSE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, FALSE); return FALSE; } dev_ext = hub_ext_from_dev(pdev); if (dev_ext == NULL) { unlock_dev(pdev, FALSE); return FALSE; } if (type == USB_ENDPOINT_XFER_INT || type == USB_ENDPOINT_XFER_ISOC) { pmap = dev_ext->tt_status_map; } else if (type == USB_ENDPOINT_XFER_BULK || type == USB_ENDPOINT_XFER_CONTROL) { pmap = dev_ext->tt_bulk_map; } if (lock) { if (pmap[port_idx >> 5] & (1 << port_idx)) { unlock_dev(pdev, FALSE); return FALSE; } pmap[port_idx >> 5] |= (1 << port_idx); } else { pmap[port_idx >> 5] &= ~(1 << port_idx); } unlock_dev(pdev, FALSE); return TRUE; } BOOLEAN hub_lock_tt(PUSB_DEV pdev, UCHAR port_idx, UCHAR type // transfer type ) { return hub_lock_unlock_tt(pdev, port_idx, type, TRUE); } BOOLEAN hub_unlock_tt(PUSB_DEV pdev, UCHAR port_idx, UCHAR type) { return hub_lock_unlock_tt(pdev, port_idx, type, FALSE); } VOID hub_clear_tt_buffer_completion(PURB purb, PVOID context) { PUSB_CTRL_SETUP_PACKET psetup; PURB_HS_PIPE_CONTENT pipe_content; PHUB2_EXTENSION hub_ext; PHCD hcd; if (purb == NULL || context == NULL) return; hub_ext = (PHUB2_EXTENSION) context; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; pipe_content = (PURB_HS_PIPE_CONTENT) & purb->reference; hub_unlock_tt(purb->pdev, (UCHAR) psetup->wIndex, (UCHAR) pipe_content->trans_type); usb_free_mem(purb); purb = NULL; hcd = hub_ext->pdev->hcd; // let those blocked urbs ( sharing the same tt )have chance to be scheduled if (hcd && usb2(hcd)) hcd->hcd_submit_urb(hcd, NULL, NULL, NULL); return; } // send CLEAR_TT_BUFFER to the hub BOOLEAN hub_clear_tt_buffer(PUSB_DEV pdev, URB_HS_PIPE_CONTENT pipe_content, UCHAR port_idx) { PURB purb; PUSB_CTRL_SETUP_PACKET psetup; PHUB2_EXTENSION hub_ext; PHCD hcd; NTSTATUS status; USE_BASIC_NON_PENDING_IRQL; if (pdev == NULL) return FALSE; if (pipe_content.speed_high) return FALSE; lock_dev(pdev, FALSE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, FALSE); return FALSE; } hub_ext = hub_ext_from_dev(pdev); if (hub_ext == NULL) { unlock_dev(pdev, FALSE); return FALSE; } purb = usb_alloc_mem(NonPagedPool, sizeof(URB)); if (purb == NULL) { unlock_dev(pdev, FALSE); return FALSE; } RtlZeroMemory(purb, sizeof(URB)); purb->flags = 0; purb->status = STATUS_SUCCESS; purb->data_buffer = NULL; purb->data_length = 0; // ( hub_ext->port_count + 7 ) / 8; // hub_if_from_dev( pdev, pif ); purb->pendp = &pdev->default_endp; purb->pdev = pdev; purb->completion = hub_clear_tt_buffer_completion; purb->context = hub_ext; purb->reference = *((PLONG) & pipe_content); purb->pirp = NULL; hcd = pdev->hcd; psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet; psetup->bmRequestType = 0x23; //host-device class other recepient psetup->bRequest = HUB_REQ_CLEAR_TT_BUFFER; psetup->wValue = (USHORT) ((pipe_content.endp_addr) | (pipe_content.dev_addr << 4) | (pipe_content.trans_type << 10) | (pipe_content.trans_dir << 15)); if (hub_ext->multiple_tt) { psetup->wIndex = (USHORT) port_idx; } else psetup->wIndex = 1; psetup->wLength = 0; unlock_dev(pdev, FALSE); status = hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb); if (status != STATUS_PENDING) { usb_free_mem(purb); purb = NULL; return FALSE; } return TRUE; } VOID hub_event_clear_tt_buffer(PUSB_DEV pdev, //always null. we do not use this param ULONG event, ULONG context, ULONG param) { UNREFERENCED_PARAMETER(event); hub_clear_tt_buffer(pdev, *((PURB_HS_PIPE_CONTENT) & context), (UCHAR) param); return; } VOID hub_post_clear_tt_event(PUSB_DEV pdev, BYTE port_idx, ULONG pipe) { PUSB_DEV_MANAGER dev_mgr; PUSB_EVENT pevent; USE_NON_PENDING_IRQL; dev_mgr = dev_mgr_from_dev(pdev); KeAcquireSpinLock(&dev_mgr->event_list_lock, &old_irql); lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, TRUE); KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); return; } pevent = alloc_event(&dev_mgr->event_pool, 1); if (pevent == NULL) { unlock_dev(pdev, TRUE); KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); TRAP(); return; } pevent->event = USB_EVENT_WAIT_RESET_PORT; pevent->pdev = pdev; pevent->context = pipe; pevent->param = port_idx; pevent->flags = USB_EVENT_FLAG_ACTIVE; pevent->process_queue = event_list_default_process_queue; pevent->process_event = hub_event_clear_tt_buffer; pevent->pnext = NULL; InsertTailList(&dev_mgr->event_list, &pevent->event_link); unlock_dev(pdev, TRUE); KeReleaseSpinLock(&dev_mgr->event_list_lock, old_irql); KeSetEvent(&dev_mgr->wake_up_event, 0, FALSE); return; } BOOLEAN init_irp_list(PIRP_LIST irp_list) { LONG i; KeInitializeSpinLock(&irp_list->irp_list_lock); InitializeListHead(&irp_list->irp_busy_list); InitializeListHead(&irp_list->irp_free_list); irp_list->irp_list_element_array = usb_alloc_mem(NonPagedPool, sizeof(IRP_LIST_ELEMENT) * MAX_IRP_LIST_SIZE); if (irp_list->irp_list_element_array == NULL) return FALSE; RtlZeroMemory(irp_list->irp_list_element_array, sizeof(IRP_LIST_ELEMENT) * MAX_IRP_LIST_SIZE); for(i = 0; i < MAX_IRP_LIST_SIZE; i++) { InsertTailList(&irp_list->irp_free_list, &irp_list->irp_list_element_array[i].irp_link); } irp_list->irp_free_list_count = MAX_IRP_LIST_SIZE; return TRUE; } VOID destroy_irp_list(PIRP_LIST irp_list) { InitializeListHead(&irp_list->irp_busy_list); InitializeListHead(&irp_list->irp_free_list); usb_free_mem(irp_list->irp_list_element_array); irp_list->irp_list_element_array = NULL; irp_list->irp_free_list_count = 0; return; } BOOLEAN add_irp_to_list(PIRP_LIST irp_list, PIRP pirp, PURB purb) { KIRQL old_irql; PIRP_LIST_ELEMENT pile; if (irp_list == NULL || pirp == NULL || purb == NULL) return FALSE; IoAcquireCancelSpinLock(&old_irql); KeAcquireSpinLockAtDpcLevel(&irp_list->irp_list_lock); if (irp_list->irp_free_list_count == 0) { KeReleaseSpinLockFromDpcLevel(&irp_list->irp_list_lock); IoReleaseCancelSpinLock(old_irql); return FALSE; } pile = (PIRP_LIST_ELEMENT) RemoveHeadList(&irp_list->irp_free_list); pile->pirp = pirp; pile->purb = purb; irp_list->irp_free_list_count--; InsertTailList(&irp_list->irp_busy_list, &pile->irp_link); (void)IoSetCancelRoutine(pirp, dev_mgr_cancel_irp); KeReleaseSpinLockFromDpcLevel(&irp_list->irp_list_lock); IoReleaseCancelSpinLock(old_irql); return TRUE; } PURB remove_irp_from_list(PIRP_LIST irp_list, PIRP pirp, PUSB_DEV_MANAGER dev_mgr //if dev_mgr is not NULL, the urb needs to be canceled ) { PIRP_LIST_ELEMENT pile; PLIST_ENTRY pthis, pnext; PURB purb; DEV_HANDLE endp_handle; PUSB_DEV pdev; PUSB_ENDPOINT pendp; PHCD hcd; USE_NON_PENDING_IRQL; if (irp_list == NULL || pirp == NULL) return NULL; KeAcquireSpinLock(&irp_list->irp_list_lock, &old_irql); if (irp_list->irp_free_list_count == MAX_IRP_LIST_SIZE) { KeReleaseSpinLock(&irp_list->irp_list_lock, old_irql); return NULL; } purb = NULL; ListFirst(&irp_list->irp_busy_list, pthis); while (pthis) { pile = struct_ptr(pthis, IRP_LIST_ELEMENT, irp_link); if (pile->pirp == pirp) { purb = pile->purb; pile->pirp = NULL; pile->purb = NULL; RemoveEntryList(pthis); InsertTailList(&irp_list->irp_free_list, pthis); irp_list->irp_free_list_count++; break; } ListNext(&irp_list->irp_busy_list, pthis, pnext); pthis = pnext; } if (purb == NULL) { // not found KeReleaseSpinLock(&irp_list->irp_list_lock, old_irql); return NULL; } endp_handle = purb->endp_handle; KeReleaseSpinLock(&irp_list->irp_list_lock, old_irql); if (dev_mgr) { // indicate we needs to cancel the urb, this condition happens only in cancel routine // we should notice that even the hcd_cancel_urb is called, the irp may not be canceled // if the urb does not exist in any queue of the host controller driver, indicating // it is being processed by dpc. Thus, the dpc will certainly prevent the irp in // completion from being canceled at the same time. On the other hand, if the // hcd_cancel_urb succeeds, it either directly complete the irp or queue the dpc for // irp completion. So, there won't be two simutaneous threads processing the same // irp. if (usb_query_and_lock_dev(dev_mgr, endp_handle, &pdev) != STATUS_SUCCESS) return NULL; lock_dev(pdev, TRUE); if (dev_state(pdev) == USB_DEV_STATE_ZOMB) { unlock_dev(pdev, FALSE); usb_unlock_dev(pdev); return NULL; } hcd = pdev->hcd; endp_from_handle(pdev, endp_handle, pendp); unlock_dev(pdev, TRUE); hcd->hcd_cancel_urb(hcd, pdev, pendp, purb); usb_unlock_dev(pdev); return NULL; } return purb; } BOOLEAN irp_list_empty(PIRP_LIST irp_list) { KIRQL old_irql; BOOLEAN ret; KeAcquireSpinLock(&irp_list->irp_list_lock, &old_irql); ret = (BOOLEAN) (irp_list->irp_free_list_count == MAX_IRP_LIST_SIZE); KeReleaseSpinLock(&irp_list->irp_list_lock, old_irql); return ret; } BOOLEAN irp_list_full(PIRP_LIST irp_list) { KIRQL old_irql; BOOLEAN ret; KeAcquireSpinLock(&irp_list->irp_list_lock, &old_irql); ret = (BOOLEAN) (irp_list->irp_free_list_count == 0); KeReleaseSpinLock(&irp_list->irp_list_lock, old_irql); return ret; }