📄 usb.c
字号:
/**
* 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 + -