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