📄 hub.c
字号:
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));
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);
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))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -