⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hub.c

📁 winNT技术操作系统,国外开放的原代码和LIUX一样
💻 C
📖 第 1 页 / 共 5 页
字号:
    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 + -