uhci.c
来自「ReactOS是一些高手根据Windows XP的内核编写出的类XP。内核实现机」· C语言 代码 · 共 2,164 行 · 第 1/5 页
C
2,164 行
//re-append them to the pending_endp_list
ListFirst(&temp_list, pthis);
RemoveEntryList(&temp_list);
MergeList(&uhci->pending_endp_list, pthis);
}
unlock_pending_endp_list(&uhci->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 uhci_cancel_pending_endp_urb running when the
// driver is unloading since it will prevent the dev_mgr to quit till all the
// reference count to the dev drop to zero.
ExInitializeWorkItem(pwork_item, uhci_cancel_pending_endp_urb, (PVOID) cancel_list);
ExQueueWorkItem(pwork_item, DelayedWorkQueue);
}
return TRUE;
}
NTSTATUS
uhci_submit_urb(PUHCI_DEV uhci, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
{
int i;
PUHCI_PENDING_ENDP pending_endp;
NTSTATUS status;
USE_BASIC_IRQL;
if (uhci == NULL || pdev == NULL || pendp == NULL || purb == NULL)
{
uhci_dbg_print(DBGLVL_MEDIUM,
("uhci_submit_urb(): uhci=0x%x, pdev=0x%x, pendp=0x%x, purb=0x%x "
"called with invalid param!\n", uhci, pdev, pendp, purb));
return STATUS_INVALID_PARAMETER;
}
lock_pending_endp_list(&uhci->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(&uhci->pending_endp_list_lock);
status = uhci_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)
{
uhci_dbg_print(DBGLVL_MEDIUM,
("uhci_submit_urb(): dev_from_endp=0x%x\n, pdev=0x%x, pendp=0x%x "
"devices mismatch!\n", dev_from_endp(purb->pendp), pdev, pendp));
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;
}
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 > purb->pendp->pusb_endp_desc->wMaxPacketSize * UHCI_MAX_TDS_PER_TRANSFER ? purb->pendp->pusb_endp_desc->wMaxPacketSize * UHCI_MAX_TDS_PER_TRANSFER : purb->data_length); //multiple transfer for large data block
else
purb->bytes_to_transfer = purb->data_length;
uhci_dbg_print(DBGLVL_MEDIUM, ("uhci_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 urb reference
if (i == FALSE)
{
//there is urb 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 urb waiting but urb 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(&uhci->pending_endp_pool, 1);
if (pending_endp == NULL)
{
//panic
status = purb->status = STATUS_UNSUCCESSFUL;
goto LBL_OUT2;
}
pending_endp->pendp = purb->pendp;
InsertTailList(&uhci->pending_endp_list, (PLIST_ENTRY) pending_endp);
unlock_dev(pdev, TRUE);
unlock_pending_endp_list(&uhci->pending_endp_list_lock);
uhci_process_pending_endp(uhci);
return STATUS_PENDING;
LBL_OUT2:
pdev->ref_count--;
RemoveEntryList((PLIST_ENTRY) purb);
LBL_OUT:
unlock_dev(pdev, TRUE);
unlock_pending_endp_list(&uhci->pending_endp_list_lock);
return status;
}
NTSTATUS
uhci_set_error_code(PURB urb, ULONG raw_status)
{
if ((raw_status & TD_CTRL_ANY_ERROR) == 0)
{
//test if the urb is canceled
if (urb->flags & URB_FLAG_FORCE_CANCEL)
urb->status = STATUS_CANCELLED;
else
urb->status = STATUS_SUCCESS;
}
else if (raw_status & TD_CTRL_BABBLE)
urb->status = USB_STATUS_DATA_OVERRUN;
else if (raw_status & TD_CTRL_STALLED)
urb->status = USB_STATUS_STALL_PID;
else if (raw_status & TD_CTRL_DBUFERR)
urb->status = USB_STATUS_BUFFER_OVERRUN;
else if (raw_status & TD_CTRL_CRCTIMEO)
urb->status = USB_STATUS_CRC;
else if (raw_status & TD_CTRL_BITSTUFF)
urb->status = USB_STATUS_BTSTUFF;
else
urb->status = STATUS_UNSUCCESSFUL;
return urb->status;
}
BOOLEAN NTAPI
uhci_sync_remove_urb_finished(PVOID context)
{
PUHCI_DEV uhci;
PLIST_ENTRY pthis, pnext, ptemp;
PURB purb;
PSYNC_PARAM pparam;
pparam = (PSYNC_PARAM) context;
uhci = pparam->uhci;
ptemp = (PLIST_ENTRY) pparam->context;
if (uhci == NULL)
{
return (UCHAR) (pparam->ret = FALSE);
}
ListFirst(&uhci->urb_list, pthis);
while (pthis)
{
//remove urbs not in the schedule
ListNext(&uhci->urb_list, pthis, pnext);
purb = (PURB) pthis;
if ((purb->flags & URB_FLAG_IN_SCHEDULE) == 0)
{
//finished or canceled( not apply for split bulk ).
purb->flags &= ~URB_FLAG_STATE_MASK;
purb->flags |= URB_FLAG_STATE_FINISHED;
RemoveEntryList(pthis);
InsertTailList(ptemp, pthis);
}
pthis = pnext;
}
pparam->ret = TRUE;
return (UCHAR) TRUE;
}
BOOLEAN
uhci_drop_fsbr(PUHCI_DEV uhci)
{
if (uhci == NULL)
return (UCHAR) FALSE;
uhci->fsbr_cnt--;
if (uhci->fsbr_cnt <= 0)
{
uhci->skel_term_qh->link = UHCI_PTR_TERM;
uhci->fsbr_cnt = 0;
}
return (UCHAR) TRUE;
}
VOID NTAPI
uhci_dpc_callback(PKDPC dpc, PVOID context, PVOID sysarg1, PVOID sysarg2)
{
PUHCI_DEV uhci;
LIST_HEAD temp_list;
PLIST_ENTRY pthis, pnext;
PURB purb;
PQH_EXTENSION pqhe;
PUHCI_PENDING_ENDP pending_endp;
PUSB_DEV pdev;
PUSB_ENDPOINT pendp;
BOOLEAN finished;
LONG i, j;
ULONG uhci_status, urb_status, toggle = 0;
SYNC_PARAM sync_param;
USE_BASIC_NON_PENDING_IRQL;
UNREFERENCED_PARAMETER(dpc);
UNREFERENCED_PARAMETER(sysarg2);
uhci = (PUHCI_DEV) context;
if (uhci == NULL)
return;
uhci_status = (ULONG) sysarg1;
InitializeListHead(&temp_list);
sync_param.uhci = uhci;
sync_param.context = (PVOID) & temp_list;
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_dpc_callback(): entering..., uhci=0x%x\n", uhci));
//remove finished urb from uhci's urb-list
KeSynchronizeExecution(uhci->pdev_ext->uhci_int, uhci_sync_remove_urb_finished, &sync_param);
//release resources( tds, and qhs ) the urb occupied
while (IsListEmpty(&temp_list) == FALSE)
{
//not in any public queue, if do not access into dev, no race
//condition will occur
purb = (PURB) RemoveHeadList(&temp_list);
urb_status = purb->status;
//the only place we do not use this lock on non-pending-endp-list data ops
KeAcquireSpinLockAtDpcLevel(&uhci->pending_endp_list_lock);
while (IsListEmpty(&purb->trasac_list) == FALSE)
{
pthis = RemoveHeadList(&purb->trasac_list);
if ((((PTD_EXTENSION) pthis)->flags & UHCI_ITEM_FLAG_TYPE) == UHCI_ITEM_FLAG_QH)
{
pqhe = (PQH_EXTENSION) pthis;
lock_qh_pool(&uhci->qh_pool, TRUE);
free_qh(&uhci->qh_pool, pqhe->pqh);
unlock_qh_pool(&uhci->qh_pool, TRUE);
}
else
{
//must be a td chain
InsertHeadList(&purb->trasac_list, pthis);
for(i = 0, purb->bytes_transfered = 0; i < purb->td_count; i++)
{
PUHCI_TD ptd;
// accumulate data transfered in tds
ptd = ((PTD_EXTENSION) pthis)->ptd;
if ((ptd->status & TD_CTRL_ACTIVE) == 0 && (ptd->status & TD_CTRL_ANY_ERROR) == 0)
{
j = ptd->status & 0x7ff;
purb->bytes_transfered += ((j == 0x7ff) ? 0 : (j + 1));
}
ListNext(&purb->trasac_list, pthis, pnext);
pthis = pnext;
}
if (urb_status & TD_CTRL_ANY_ERROR)
{
if (purb->last_finished_td != NULL && purb->last_finished_td != &purb->trasac_list)
toggle = (((PTD_EXTENSION) purb->last_finished_td)->ptd->info & (1 << 19));
}
//trick, remove trasac_list
ListFirst(&purb->trasac_list, pthis);
RemoveEntryList(&purb->trasac_list);
lock_td_pool(&uhci->td_pool, TRUE);
free_tds(&uhci->td_pool, ((PTD_EXTENSION) pthis)->ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
//termination condition
InitializeListHead(&purb->trasac_list);
purb->last_finished_td = NULL;
}
}
if (endp_type(purb->pendp) == USB_ENDPOINT_XFER_ISOC
|| endp_type(purb->pendp) == USB_ENDPOINT_XFER_INT)
uhci_claim_bandwidth(uhci, purb, FALSE); //release band-width
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
uhci_set_error_code(purb, urb_status);
finished = TRUE;
//since the ref_count for the urb is not released, we can safely have one
//pointer to dev
pdev = dev_from_endp(purb->pendp);
pendp = purb->pendp;
if (purb->status == USB_STATUS_BABBLE_DETECTED)
{
usb_dbg_print(DBGLVL_MEDIUM,
("uhci_dpc_callback(): alert!!!, babble detected, severe error, reset the whole bus\n"));
uhci_reset(uhci);
uhci_start(&uhci->hcd_interf);
}
//this will let the new request in uhci_generic_urb_completion to this endp
//be processed rather than queued in the pending_endp_list
lock_dev(pdev, TRUE);
usb_endp_busy_count_dec(pendp);
unlock_dev(pdev, TRUE);
if (usb_success(purb->status) == FALSE)
{
// set error code and complete the urb and purb is invalid from this point
uhci_generic_urb_completion(purb, purb->context);
}
else
{
if ((purb->pipe & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
{
purb->rest_bytes -= purb->bytes_transfered;
if (purb->rest_bytes)
{
finished = FALSE;
}
else
{
uhci_generic_urb_completion(purb, purb->context);
}
}
else
{
uhci_generic_urb_completion(purb, purb->context);
//purb is now invalid
}
}
KeAcquireSpinLockAtDpcLevel(&uhci->pending_endp_list_lock);
lock_dev(pdev, TRUE);
if (finished)
pdev->ref_count--;
if (urb_status & TD_CTRL_ANY_ERROR && endp_type(pendp) != USB_ENDPOINT_XFER_CONTROL)
{
pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK;
pendp->flags |= USB_ENDP_FLAG_STALL;
}
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
if (finished == FALSE)
{
purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
uhci_generic_urb_completion(purb, purb->context);
lock_dev(pdev, TRUE);
pdev->ref_count--;
unlock_dev(pdev, TRUE);
}
continue;
}
if (finished && IsListEmpty(&pendp->urb_list) == TRUE)
{
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
continue;
}
else if (finished == TRUE)
{
//has urb in the endp's urb-list
if (usb_endp_busy_count(pendp) > 0)
{
//the urbs still have chance to be sheduled but not this time
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
continue;
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?