?? ehci.c
字號:
return new;
}
BOOLEAN
destroy_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool)
{
if (pool == NULL)
return FALSE;
InitializeListHead(&pool->free_que);
pool->free_count = pool->total_count = 0;
usb_free_mem(pool->pending_endp_array);
pool->pending_endp_array = NULL;
return TRUE;
}
#else
#define ehci_wait_ms uhci_wait_ms
extern VOID uhci_wait_ms(PEHCI_DEV ehci, LONG ms);
extern BOOLEAN init_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool);
extern BOOLEAN free_pending_endp(PUHCI_PENDING_ENDP_POOL pool, PUHCI_PENDING_ENDP pending_endp);
extern PUHCI_PENDING_ENDP alloc_pending_endp(PUHCI_PENDING_ENDP_POOL pool, LONG count);
extern BOOLEAN destroy_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool);
#endif
//end of pending endpoint pool funcs
static VOID NTAPI
ehci_cancel_pending_endp_urb(IN PVOID Parameter)
{
PLIST_ENTRY abort_list;
PUSB_DEV pdev;
PURB purb;
USE_BASIC_NON_PENDING_IRQL;
abort_list = (PLIST_ENTRY) Parameter;
if (abort_list == NULL)
return;
while (IsListEmpty(abort_list) == FALSE)
{
//these devs are protected by purb's ref-count
purb = (PURB) RemoveHeadList(abort_list);
pdev = purb->pdev;
// purb->status is set when they are added to abort_list
ehci_generic_urb_completion(purb, purb->context);
lock_dev(pdev, FALSE);
pdev->ref_count--;
unlock_dev(pdev, FALSE);
}
usb_free_mem(abort_list);
return;
}
static BOOLEAN
ehci_process_pending_endp(PEHCI_DEV ehci)
{
PUSB_DEV pdev;
LIST_ENTRY temp_list, abort_list;
PLIST_ENTRY pthis;
PURB purb;
PUSB_ENDPOINT pendp;
NTSTATUS can_submit = STATUS_SUCCESS;
PWORK_QUEUE_ITEM pwork_item;
PLIST_ENTRY cancel_list;
PUSB_DEV pparent = NULL;
UCHAR port_idx = 0;
BOOLEAN tt_needed;
UCHAR hub_addr = 0;
USE_BASIC_IRQL;
if (ehci == NULL)
return FALSE;
InitializeListHead(&temp_list);
InitializeListHead(&abort_list);
purb = NULL;
ehci_dbg_print(DBGLVL_MEDIUM, ("ehci_process_pending_endp(): entering..., ehci=0x%x\n", ehci));
lock_pending_endp_list(&ehci->pending_endp_list_lock);
while (IsListEmpty(&ehci->pending_endp_list) == FALSE)
{
ehci_dbg_print(DBGLVL_MAXIMUM, ("ehci_process_pending_endp(): pending_endp_list=0x%x\n",
&ehci->pending_endp_list));
tt_needed = FALSE;
pthis = RemoveHeadList(&ehci->pending_endp_list);
pendp = ((PUHCI_PENDING_ENDP) pthis)->pendp;
pdev = dev_from_endp(pendp);
lock_dev(pdev, TRUE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, TRUE);
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
//delegate to ehci_remove_device for remiving the purb queue on the endpoint
continue;
}
if ((pdev->flags & USB_DEV_FLAG_HIGH_SPEED) == 0)
{
// prepare split transaction
unlock_dev(pdev, TRUE);
// pparent won't be removed when pending_endp_list_lock is acquired.
get_parent_hs_hub(pdev, pparent, port_idx);
if (pparent == NULL)
{
TRAP();
ehci_dbg_print(DBGLVL_MEDIUM,
("ehci_process_pending_endp(): full/low speed device with no parent!!!\n"));
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
continue;
}
if (hub_lock_tt(pparent, port_idx, (UCHAR) endp_type(pendp)) == FALSE)
{
lock_dev(pdev, TRUE);
if (dev_state(pdev) != USB_DEV_STATE_ZOMB)
{
// reinsert the pending-endp to the list
InsertTailList(&temp_list, pthis);
unlock_dev(pdev, TRUE);
}
else
{
// delegate to ehci_remove_device for purb removal
unlock_dev(pdev, TRUE);
free_pending_endp(&ehci->pending_endp_pool,
struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
}
continue;
}
// backup the hub address for future use
hub_addr = pparent->dev_addr;
lock_dev(pdev, TRUE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, TRUE);
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
hub_unlock_tt(pparent, port_idx, (UCHAR) endp_type(pendp));
continue;
}
tt_needed = TRUE;
// go on processing
}
if (endp_state(pendp) == USB_ENDP_FLAG_STALL)
{
while (IsListEmpty(&pendp->urb_list) == FALSE)
{
purb = (PURB) RemoveHeadList(&pendp->urb_list);
purb->status = USB_STATUS_ENDPOINT_HALTED;
InsertTailList(&abort_list, (LIST_ENTRY *) purb);
}
InitializeListHead(&pendp->urb_list);
unlock_dev(pdev, TRUE);
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
if (tt_needed)
hub_unlock_tt(pparent, port_idx, (UCHAR) endp_type(pendp));
continue;
}
if (IsListEmpty(&pendp->urb_list) == FALSE)
{
purb = (PURB) RemoveHeadList(&pendp->urb_list);
ASSERT(purb);
}
else
{
InitializeListHead(&pendp->urb_list);
unlock_dev(pdev, TRUE);
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
if (tt_needed)
hub_unlock_tt(pparent, port_idx, (UCHAR) endp_type(pendp));
continue;
}
if (tt_needed)
{
((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->hub_addr = hub_addr;
((PURB_HS_CONTEXT_CONTENT) & purb->hs_context)->port_idx = port_idx;
}
// if can_submit is STATUS_SUCCESS, the purb is inserted into the schedule
switch (endp_type(pendp))
{
case USB_ENDPOINT_XFER_BULK:
{
can_submit = ehci_internal_submit_bulk(ehci, purb);
break;
}
case USB_ENDPOINT_XFER_CONTROL:
{
can_submit = ehci_internal_submit_ctrl(ehci, purb);
break;
}
case USB_ENDPOINT_XFER_INT:
{
can_submit = ehci_internal_submit_int(ehci, purb);
break;
}
case USB_ENDPOINT_XFER_ISOC:
{
can_submit = ehci_internal_submit_iso(ehci, purb);
break;
}
}
if (can_submit == STATUS_NO_MORE_ENTRIES)
{
//no enough bandwidth or tds
InsertHeadList(&pendp->urb_list, (PLIST_ENTRY) purb);
InsertTailList(&temp_list, pthis);
}
else
{
// otherwise error or success
free_pending_endp(&ehci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
if (can_submit != STATUS_SUCCESS)
{
//abort these URBs
InsertTailList(&abort_list, (LIST_ENTRY *) purb);
purb->status = can_submit;
}
}
unlock_dev(pdev, TRUE);
if (can_submit != STATUS_SUCCESS && tt_needed)
{
hub_unlock_tt(pparent, port_idx, (UCHAR) endp_type(pendp));
}
}
if (IsListEmpty(&temp_list) == FALSE)
{
//re-append them to the pending_endp_list
ListFirst(&temp_list, pthis);
RemoveEntryList(&temp_list);
MergeList(&ehci->pending_endp_list, pthis);
}
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
if (IsListEmpty(&abort_list) == FALSE)
{
PLIST_ENTRY pthis;
cancel_list = (PLIST_ENTRY) usb_alloc_mem(NonPagedPool, sizeof(WORK_QUEUE_ITEM) + sizeof(LIST_ENTRY));
ASSERT(cancel_list);
ListFirst(&abort_list, pthis);
RemoveEntryList(&abort_list);
InsertTailList(pthis, cancel_list);
pwork_item = (PWORK_QUEUE_ITEM) & cancel_list[1];
// we do not need to worry the ehci_cancel_pending_endp_urb running when the
// driver is unloading since purb-reference count will prevent the dev_mgr to
// quit till all the reference count to the dev drop to zero.
ExInitializeWorkItem(pwork_item, ehci_cancel_pending_endp_urb, (PVOID) cancel_list);
ExQueueWorkItem(pwork_item, DelayedWorkQueue);
}
return TRUE;
}
NTSTATUS
ehci_submit_urb(PEHCI_DEV ehci, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
{
int i;
PUHCI_PENDING_ENDP pending_endp;
NTSTATUS status;
USE_BASIC_IRQL;
if (ehci == NULL)
return STATUS_INVALID_PARAMETER;
if (pdev == NULL || pendp == NULL || purb == NULL)
{
// give a chance to those pending urb, especially for clearing hub tt
ehci_process_pending_endp(ehci);
return STATUS_INVALID_PARAMETER;
}
lock_pending_endp_list(&ehci->pending_endp_list_lock);
lock_dev(pdev, TRUE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
status = purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
goto LBL_OUT;
}
if (dev_class(pdev) == USB_DEV_CLASS_ROOT_HUB)
{
unlock_dev(pdev, TRUE);
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
status = ehci_rh_submit_urb(pdev, purb);
return status;
}
if (pendp)
purb->pendp = pendp;
else
purb->pendp = &pdev->default_endp;
if (dev_from_endp(purb->pendp) != pdev)
{
status = purb->status = STATUS_INVALID_PARAMETER;
goto LBL_OUT;
}
if (endp_state(purb->pendp) == USB_ENDP_FLAG_STALL)
{
status = purb->status = USB_STATUS_ENDPOINT_HALTED;
goto LBL_OUT;
}
if ((pdev->flags & USB_DEV_FLAG_HIGH_SPEED) == 0)
{
// wait one ms
usb_wait_ms_dpc(1);
}
purb->pdev = pdev;
purb->rest_bytes = purb->data_length;
if (endp_type(purb->pendp) == USB_ENDPOINT_XFER_BULK)
purb->bytes_to_transfer = (purb->data_length > EHCI_MAX_SIZE_TRANSFER ? EHCI_MAX_SIZE_TRANSFER : purb->data_length); //multiple transfer for large data block
else
purb->bytes_to_transfer = purb->data_length;
ehci_dbg_print(DBGLVL_MEDIUM, ("ehci_submit_urb(): bytes_to_transfer=0x%x\n", purb->bytes_to_transfer));
purb->bytes_transfered = 0;
InitializeListHead(&purb->trasac_list);
purb->last_finished_td = &purb->trasac_list;
purb->flags &= ~(URB_FLAG_STATE_MASK | URB_FLAG_IN_SCHEDULE | URB_FLAG_FORCE_CANCEL);
purb->flags |= URB_FLAG_STATE_PENDING;
i = IsListEmpty(&pendp->urb_list);
InsertTailList(&pendp->urb_list, &purb->urb_link);
pdev->ref_count++; //for purb reference
if (i == FALSE)
{
//there is purb pending, simply queue it and return
status = purb->status = STATUS_PENDING;
goto LBL_OUT;
}
else if (usb_endp_busy_count(purb->pendp) && endp_type(purb->pendp) != USB_ENDPOINT_XFER_ISOC)
{
//
//No purb waiting but purb overlap not allowed,
//so leave it in queue and return, will be scheduled
//later
//
status = purb->status = STATUS_PENDING;
goto LBL_OUT;
}
pending_endp = alloc_pending_endp(&ehci->pending_endp_pool, 1);
if (pending_endp == NULL)
{
//panic
status = purb->status = STATUS_UNSUCCESSFUL;
goto LBL_OUT2;
}
pending_endp->pendp = purb->pendp;
InsertTailList(&ehci->pending_endp_list, (PLIST_ENTRY) pending_endp);
unlock_dev(pdev, TRUE);
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
ehci_process_pending_endp(ehci);
return STATUS_PENDING;
LBL_OUT2:
pdev->ref_count--;
RemoveEntryList((PLIST_ENTRY) purb);
LBL_OUT:
unlock_dev(pdev, TRUE);
unlock_pending_endp_list(&ehci->pending_endp_list_lock);
ehci_process_pending_endp(ehci);
return status;
}
static NTSTATUS
ehci_set_error_code(PURB purb, ULONG raw_status)
{
PURB_HS_PIPE_CONTENT pipe_content;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -