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

📄 usb.c

📁 这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统
💻 C
📖 第 1 页 / 共 3 页
字号:
/**
 * usb.c - USB driver stack project for Windows NT 4.0
 *
 * Copyright (c) 2002-2004 Zhiming  mypublic99@yahoo.com
 *
 * This program/include file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program/include file is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program (in the main directory of the distribution, the file
 * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
 * Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "usbdriver.h"

LONG g_alloc_cnt = 0;
ULONG cpu_clock_freq = 0;

NTSTATUS usb_get_descriptor(PUSB_DEV pdev, PURB purb);
VOID usb_set_interface_completion(PURB purb, PVOID context);
NTSTATUS usb_set_interface(PURB purb);

PVOID
usb_alloc_mem(POOL_TYPE pool_type, LONG size)
{
    PVOID ret;
    g_alloc_cnt++;
    ret = ExAllocatePool(pool_type, size);
    usb_dbg_print(DBGLVL_ULTRA, ("usb_alloc_mem(): alloced=0x%x\n", g_alloc_cnt));
    return ret;
}

VOID
usb_free_mem(PVOID pbuf)
{
    g_alloc_cnt--;
    usb_dbg_print(DBGLVL_ULTRA, ("usb_free_mem(): alloced=0x%x\n", g_alloc_cnt));
    ExFreePool(pbuf);
}

VOID usb_config_dev_completion(PURB purb, PVOID context);

//shamelessly pasted from linux's usb.c
LONG
usb_calc_bus_time(LONG speed, LONG input_dir, LONG is_iso, LONG byte_count)
{
    LONG tmp;

    switch (speed & 0x3)        /* no isoc. here */
    {
        case USB_SPEED_LOW:
        {
            if (input_dir)
            {
                tmp = (67667L * (31L + 10L * bit_time(byte_count))) / 1000L;
                return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
            }
            else
            {
                tmp = (66700L * (31L + 10L * bit_time(byte_count))) / 1000L;
                return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
            }
            break;
        }
        /* for full-speed: */
        case USB_SPEED_FULL:
        {
            if (!is_iso)        /* Input or Output */
            {
                tmp = (8354L * (31L + 10L * bit_time(byte_count))) / 1000L;
                return (9107L + BW_HOST_DELAY + tmp);
            }                   /* end not Isoc */

            /* for isoc: */

            tmp = (8354L * (31L + 10L * bit_time(byte_count))) / 1000L;
            return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
        }
        case USB_SPEED_HIGH:
        {
            if (!is_iso)
            {
                tmp = (999 + 926520 + 2083 * ((LONG) ((19 + 7 * 8 * byte_count) / 6))) / 1000;
            }
            else
            {
                tmp = (999 + 633232 + 2083 * ((LONG) ((19 + 7 * 8 * byte_count) / 6))) / 1000;
            }
            return tmp + USB2_HOST_DELAY;
        }
        default:
        {
            break;
        }
    }
    return 125001;
}

//
// if the dev is not in the list, return value is not success and the pointer is nulled
// if the dev is in the list but zomb, return value is error code and the pointer is the dev( no ref_count guarded )
// if the dev is alive and in the list, return is success and the pointer is the dev.
// one must be aware of what his doing before he uses the ppdev
//
NTSTATUS
usb_query_and_lock_dev(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle, PUSB_DEV * ppdev)
{
    int i;
    PLIST_ENTRY pthis, pnext;
    PUSB_DEV pdev = NULL;
    BOOLEAN valid_dev;

    USE_NON_PENDING_IRQL;

    *ppdev = NULL;

    if (dev_mgr == NULL || dev_handle == 0)
        return STATUS_INVALID_PARAMETER;

    i = dev_id_from_handle(dev_handle);

    KeAcquireSpinLock(&dev_mgr->dev_list_lock, &old_irql);
    ListFirst(&dev_mgr->dev_list, pthis);

    while (pthis)
    {
        pdev = (PUSB_DEV) pthis;
        if (pdev->dev_id != (ULONG) i)
        {
            ListNext(&dev_mgr->dev_list, pthis, pnext);
            pthis = pnext;
            continue;
        }
        else
            break;
    }
    if (pthis == NULL)
    {
        //no such device
        KeReleaseSpinLock(&dev_mgr->dev_list_lock, old_irql);
        return STATUS_INVALID_PARAMETER;
    }

    valid_dev = TRUE;

    lock_dev(pdev, TRUE);

    if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
    {
        valid_dev = FALSE;
    }
    else
        pdev->ref_count++;      //guard the dev by increasing the ref count

    unlock_dev(pdev, TRUE);

    KeReleaseSpinLock(&dev_mgr->dev_list_lock, old_irql);

    *ppdev = pdev;

    if (!valid_dev)
        return STATUS_DEVICE_DOES_NOT_EXIST;

    return STATUS_SUCCESS;

}

NTSTATUS
usb_unlock_dev(PUSB_DEV dev)
{
    USE_BASIC_NON_PENDING_IRQL;

    if (dev == NULL)
        return STATUS_INVALID_PARAMETER;

    lock_dev(dev, FALSE);
    dev->ref_count--;
    if (dev->ref_count < 0)
        dev->ref_count = 0;
    unlock_dev(dev, FALSE);
    return STATUS_SUCCESS;
}

NTSTATUS
usb_reset_pipe_ex(PUSB_DEV_MANAGER dev_mgr,
                  DEV_HANDLE endp_handle,          //endp handle to reset
                  PURBCOMPLETION reset_completion, //note: this reset completion has no right to delete the urb, that is only for reference
                  PVOID param)
{
    NTSTATUS status;
    PUSB_DEV pdev;
    LONG if_idx, endp_idx;
    PUSB_ENDPOINT pendp;
    USE_BASIC_NON_PENDING_IRQL;

    if (dev_mgr == NULL)
        return STATUS_INVALID_PARAMETER;

    status = usb_query_and_lock_dev(dev_mgr, (endp_handle & 0xffff0000), &pdev);
    if (status != STATUS_SUCCESS)
        return STATUS_UNSUCCESSFUL;

    lock_dev(pdev, FALSE);
    if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
    {
        status = STATUS_UNSUCCESSFUL;
        goto LBL_OUT;
    }

    if_idx = if_idx_from_handle(endp_handle);
    endp_idx = endp_idx_from_handle(endp_handle);

    if (default_endp_handle(endp_handle))
    {
        status = STATUS_UNSUCCESSFUL;
        goto LBL_OUT;
    }

    if (dev_state(pdev) < USB_DEV_STATE_CONFIGURED)
    {
        status = STATUS_DEVICE_NOT_READY;
        goto LBL_OUT;
    }

    pendp = &pdev->usb_config->interf[if_idx].endp[endp_idx];
    unlock_dev(pdev, FALSE) status = usb_reset_pipe(pdev, pendp, reset_completion, param);
    usb_unlock_dev(pdev);
    return status;

LBL_OUT:
    unlock_dev(pdev, FALSE);
    usb_unlock_dev(pdev);

    return status;
}

// caller must guarantee the pdev exist before the routine exit
NTSTATUS
usb_reset_pipe(PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURBCOMPLETION client_reset_pipe_completion, PVOID param)
{

    PHCD hcd;
    PURB purb;
    BYTE endp_addr;
    NTSTATUS status;
    DEV_HANDLE dev_handle;

    USE_BASIC_NON_PENDING_IRQL;

    if (pdev == NULL || pendp == NULL)
        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;
    }

    hcd = pdev->hcd;
    endp_addr = pendp->pusb_endp_desc->bEndpointAddress;
    dev_handle = usb_make_handle(pdev->dev_id, 0, 0);
    unlock_dev(pdev, FALSE);

    purb = (PURB) usb_alloc_mem(NonPagedPool, sizeof(URB) + sizeof(PIRP));

    if (purb == NULL)
        return STATUS_NO_MEMORY;

    UsbBuildResetPipeRequest(purb,
                             dev_handle,
                             endp_addr,
                             usb_reset_pipe_completion,
                             pendp,
                             (LONG)client_reset_pipe_completion);

    *((PULONG)&purb[1]) = (ULONG)param;

    if ((status = hcd->hcd_submit_urb(hcd, pdev, &pdev->default_endp, purb)) != STATUS_PENDING)
    {
        usb_free_mem(purb);
        purb = NULL;
    }
    return status;
}

VOID
usb_reset_pipe_completion(PURB purb, PVOID context)
{
    PUSB_DEV pdev;
    PUSB_ENDPOINT pendp;

    USE_BASIC_NON_PENDING_IRQL;

    if (purb == NULL || context == NULL)
        return;

    pdev = purb->pdev;
    pendp = (PUSB_ENDPOINT) context;

    lock_dev(pdev, TRUE);
    if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
    {
        goto LBL_OUT;
    }

    if (usb_error(purb->status))
    {
        goto LBL_OUT;
    }
    //clear stall
    pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK;

    //reset toggle endp_type
    if ((pendp->pusb_endp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK ||
        (pendp->pusb_endp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
    {
        pendp->flags &= ~USB_ENDP_FLAG_DATATOGGLE;
    }

LBL_OUT:
    unlock_dev(pdev, TRUE);

    if (purb->reference)
        ((PURBCOMPLETION) purb->reference) (purb, (PVOID) (*((PULONG) & purb[1])));

    usb_free_mem(purb);
    purb = NULL;
    return;

}

void
usb_reset_pipe_from_dispatch_completion(PURB purb, PVOID param)
{
    PURB pclient_urb;
    if (purb == NULL || param == NULL)
        TRAP();
    pclient_urb = (PURB) param;
    pclient_urb->status = purb->status;

    if (pclient_urb->completion)
    {
        pclient_urb->completion(pclient_urb, pclient_urb->context);
    }
    // the urb can not be freed here because it is owned by the reset
    // pipe completion
    return;
}

//used to check descriptor validity
BOOLEAN
is_header_match(PUCHAR pbuf, ULONG type)
{
    BOOLEAN ret;
    PUSB_DESC_HEADER phdr;
    phdr = (PUSB_DESC_HEADER) pbuf;

    switch (type)
    {
        case USB_DT_DEVICE:
        {
            ret = (phdr->bLength == sizeof(USB_DEVICE_DESC) && phdr->bDescriptorType == USB_DT_DEVICE);
            break;
        }
        case USB_DT_CONFIG:
        {
            ret = (phdr->bLength == sizeof(USB_CONFIGURATION_DESC) && phdr->bDescriptorType == USB_DT_CONFIG);
            break;
        }
        case USB_DT_INTERFACE:
        {
            ret = (phdr->bLength == sizeof(USB_INTERFACE_DESC) && phdr->bDescriptorType == USB_DT_INTERFACE);
            break;
        }
        case USB_DT_ENDPOINT:
        {
            ret = (phdr->bLength == sizeof(USB_ENDPOINT_DESC) && phdr->bDescriptorType == USB_DT_ENDPOINT);
            break;
        }
        default:
            ret = FALSE;
    }
    return ret;
}

BOOLEAN
usb_skip_endp_desc(PBYTE * pbUF, LONG n)
{
    if (is_header_match(*pbUF, USB_DT_ENDPOINT))
    {
        (*pbUF) += sizeof(USB_ENDPOINT_DESC) * n;
        return TRUE;
    }
    return FALSE;
}

BOOLEAN
usb_skip_if_desc(PBYTE * pBUF)
{
    BOOLEAN ret;
    PUSB_INTERFACE_DESC pif_desc = (PUSB_INTERFACE_DESC) * pBUF;
    LONG endp_count;
    ret = is_header_match((PBYTE) * pBUF, USB_DT_INTERFACE);
    if (ret == TRUE)
    {
        endp_count = pif_desc->bNumEndpoints;
        if (endp_count < MAX_ENDPS_PER_IF)
        {
            pif_desc++;
            ret = usb_skip_endp_desc((PBYTE *) & pif_desc, endp_count);
            if (ret)
                *(pBUF) = (PBYTE) pif_desc;
        }
        else
            ret = FALSE;
    }
    return ret;
}

BOOLEAN
usb_skip_if_and_altif(PUCHAR * pdesc_BUF)
{
    BOOLEAN ret;
    PUSB_INTERFACE_DESC pif_desc1 = (PUSB_INTERFACE_DESC) * pdesc_BUF;
    ret = is_header_match(*pdesc_BUF, USB_DT_INTERFACE);
    if (ret == TRUE)
    {
        if (pif_desc1->bAlternateSetting == 0)
            ret = usb_skip_if_desc((PUCHAR *) & pif_desc1);
        else
            //no default interface
            ret = FALSE;

        while (ret && pif_desc1->bAlternateSetting != 0)
            ret = usb_skip_if_desc((PUCHAR *) & pif_desc1);
    }
    if (ret)
        *pdesc_BUF = (PUCHAR) pif_desc1;

    return ret;
}

BOOLEAN
usb_skip_one_config(PUCHAR *pconfig_desc_BUF)
{
    LONG if_count, i;
    BOOLEAN ret;
    PUSB_CONFIGURATION_DESC pcfg_DESC = (PUSB_CONFIGURATION_DESC) * pconfig_desc_BUF;
    PUSB_INTERFACE_DESC pif_desc2 = (PUSB_INTERFACE_DESC) & pcfg_DESC[1];

    ret = is_header_match((PUCHAR) pcfg_DESC, USB_DT_CONFIG);
    if (ret)
        *pconfig_desc_BUF = &((BYTE *) pcfg_DESC)[pcfg_DESC->wTotalLength];
    return ret;

    ret = is_header_match((PUCHAR) pcfg_DESC, USB_DT_CONFIG)
        && is_header_match((PUCHAR) pif_desc2, USB_DT_INTERFACE);

    if (ret)
    {
        if_count = pcfg_DESC->bNumInterfaces;
        if (if_count < MAX_INTERFACES_PER_CONFIG)
        {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -