mirror of
https://github.com/reactos/reactos.git
synced 2025-01-11 16:51:06 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
1357 lines
36 KiB
C
1357 lines
36 KiB
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)
|
|
{
|
|
for(i = 0; i < if_count; i++)
|
|
{
|
|
ret = usb_skip_if_and_altif((PUCHAR *) & pif_desc2);
|
|
if (ret == FALSE)
|
|
break;
|
|
}
|
|
if (ret)
|
|
*pconfig_desc_BUF = (PUCHAR) pif_desc2;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
PUSB_CONFIGURATION_DESC
|
|
usb_find_config_desc_by_idx(PUCHAR pbuf, LONG idx, LONG cfg_count)
|
|
{
|
|
LONG i;
|
|
BOOLEAN ret;
|
|
PUSB_CONFIGURATION_DESC pcfg_desc = (PUSB_CONFIGURATION_DESC) pbuf;
|
|
if (pcfg_desc == NULL)
|
|
return NULL;
|
|
|
|
if (cfg_count > MAX_CONFIGS_PER_DEV)
|
|
return NULL;
|
|
|
|
if (idx > cfg_count)
|
|
return NULL;
|
|
|
|
if (idx == 0)
|
|
return pcfg_desc;
|
|
|
|
for(i = 0; i < idx - 1; i++)
|
|
{
|
|
ret = usb_skip_one_config((PBYTE *) & pcfg_desc);
|
|
if (ret == FALSE)
|
|
return NULL;
|
|
}
|
|
return pcfg_desc;
|
|
}
|
|
|
|
PUSB_CONFIGURATION_DESC
|
|
usb_find_config_desc_by_val(PBYTE pbuf, LONG val, LONG cfg_count)
|
|
{
|
|
LONG i;
|
|
BOOLEAN ret;
|
|
PUSB_CONFIGURATION_DESC pcfg_desc = (PUSB_CONFIGURATION_DESC) pbuf;
|
|
if (pcfg_desc == NULL)
|
|
return NULL;
|
|
|
|
if (cfg_count > MAX_CONFIGS_PER_DEV)
|
|
return NULL;
|
|
|
|
for(i = 0; i < cfg_count; i++)
|
|
{
|
|
if (pcfg_desc->bConfigurationValue == val)
|
|
return pcfg_desc;
|
|
|
|
ret = usb_skip_one_config((PBYTE *) & pcfg_desc);
|
|
if (ret == FALSE)
|
|
return NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define if_from_handle( handle ) ( ( handle & 0xff00 ) >> 8 )
|
|
|
|
NTSTATUS
|
|
usb_submit_config_urb(PURB purb)
|
|
{
|
|
PUSB_DEV pdev;
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PUSB_ENDPOINT pendp;
|
|
PURB purb1;
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
NTSTATUS status;
|
|
PHCD hcd;
|
|
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (purb == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
pdev = purb->pdev;
|
|
pendp = purb->pendp;
|
|
|
|
lock_dev(pdev, FALSE);
|
|
|
|
dev_mgr = dev_mgr_from_dev(pdev);
|
|
hcd = pdev->hcd;
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_FIRST_CONFIG || dev_state(pdev) == USB_DEV_STATE_RECONFIG)
|
|
{
|
|
//outstanding request of set configuration exists in process
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_CONFIGURED
|
|
&& pdev->usb_config->pusb_config_desc->bConfigurationValue == (BYTE) psetup->wValue)
|
|
{
|
|
//already the current config
|
|
status = STATUS_SUCCESS;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_CONFIGURED)
|
|
{
|
|
// not support re-configuration yet
|
|
status = STATUS_NOT_SUPPORTED;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
purb1 = usb_alloc_mem(NonPagedPool, sizeof(URB));
|
|
if (purb1 == NULL)
|
|
{
|
|
status = STATUS_NO_MEMORY;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
UsbBuildSelectConfigurationRequest(purb1,
|
|
usb_make_handle(pdev->dev_id, 0, 0) | 0xffff,
|
|
psetup->wValue, usb_config_dev_completion, 0, ((ULONG) purb));
|
|
purb1->pdev = pdev;
|
|
purb1->pendp = pendp;
|
|
|
|
//change the dev state
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_FIRST_CONFIG;
|
|
|
|
unlock_dev(pdev, FALSE);
|
|
|
|
status = hcd->hcd_submit_urb(hcd, pdev, pendp, purb1);
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
usb_free_mem(purb1);
|
|
purb1 = NULL;
|
|
}
|
|
return status;
|
|
|
|
LBL_OUT:
|
|
unlock_dev(pdev, FALSE);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
usb_submit_urb(PUSB_DEV_MANAGER dev_mgr, PURB purb)
|
|
{
|
|
NTSTATUS status;
|
|
PUSB_DEV pdev;
|
|
LONG if_idx, endp_idx;
|
|
DEV_HANDLE endp_handle;
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
PUSB_ENDPOINT pendp;
|
|
|
|
PHCD hcd;
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (purb == NULL || dev_mgr == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
endp_handle = purb->endp_handle;
|
|
|
|
if (endp_handle == 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
status = usb_query_and_lock_dev(dev_mgr, endp_handle, &pdev);
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
if_idx = if_idx_from_handle(endp_handle);
|
|
endp_idx = endp_idx_from_handle(endp_handle);
|
|
|
|
lock_dev(pdev, FALSE);
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
if (dev_state(pdev) < USB_DEV_STATE_ADDRESSED)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
status = STATUS_DEVICE_NOT_READY;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
dev_mgr = dev_mgr_from_dev(pdev);
|
|
hcd = pdev->hcd;
|
|
|
|
if (default_endp_handle(endp_handle))
|
|
{
|
|
//default endp
|
|
pendp = &pdev->default_endp;
|
|
}
|
|
else if (if_idx >= MAX_INTERFACES_PER_CONFIG || endp_idx >= MAX_ENDPS_PER_IF)
|
|
{
|
|
status = STATUS_INVALID_PARAMETER;
|
|
unlock_dev(pdev, FALSE);
|
|
goto LBL_OUT;
|
|
}
|
|
else
|
|
{
|
|
if (dev_state(pdev) < USB_DEV_STATE_CONFIGURED)
|
|
{
|
|
status = STATUS_DEVICE_NOT_READY;
|
|
unlock_dev(pdev, FALSE);
|
|
goto LBL_OUT;
|
|
}
|
|
pendp = &pdev->usb_config->interf[if_idx].endp[endp_idx];
|
|
|
|
}
|
|
|
|
purb->pdev = pdev;
|
|
purb->pendp = pendp;
|
|
|
|
//for default endpoint we have some special process
|
|
if (default_endp_handle(endp_handle))
|
|
{
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
if (psetup->bmRequestType == 0 && psetup->bRequest == USB_REQ_SET_CONFIGURATION)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
status = usb_submit_config_urb(purb);
|
|
goto LBL_OUT;
|
|
}
|
|
else if (psetup->bmRequestType == 1 && psetup->bRequest == USB_REQ_SET_INTERFACE)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
// status = STATUS_NOT_SUPPORTED;
|
|
status = usb_set_interface(purb);
|
|
goto LBL_OUT;
|
|
}
|
|
else if (psetup->bmRequestType == 0x80 && psetup->bRequest == USB_REQ_GET_DESCRIPTOR)
|
|
{
|
|
if ((psetup->wValue >> 8) == USB_DT_CONFIG || (psetup->wValue >> 8) == USB_DT_DEVICE)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
status = usb_get_descriptor(pdev, purb);
|
|
goto LBL_OUT;
|
|
|
|
//get the descriptor directly
|
|
//status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
|
|
//goto LBL_OUT;
|
|
}
|
|
}
|
|
else if (psetup->bmRequestType == 0x02 && psetup->bRequest == USB_REQ_CLEAR_FEATURE && psetup->wValue == 0) //reset pipe
|
|
{
|
|
ULONG endp_addr;
|
|
BOOLEAN found;
|
|
endp_addr = psetup->wIndex;
|
|
if ((endp_addr & 0xf) == 0)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
// search for the endp by the endp addr in the wIndex
|
|
found = FALSE;
|
|
for(if_idx = 0; if_idx < pdev->usb_config->if_count; if_idx++)
|
|
{
|
|
for(endp_idx = 0; endp_idx < pdev->usb_config->interf[if_idx].endp_count; endp_idx++)
|
|
{
|
|
pendp = &pdev->usb_config->interf[if_idx].endp[endp_idx];
|
|
if (pendp->pusb_endp_desc->bEndpointAddress == endp_addr)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (found == TRUE)
|
|
break;
|
|
}
|
|
if (found)
|
|
endp_handle = usb_make_handle(pdev->dev_id, if_idx, endp_idx);
|
|
else
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto LBL_OUT;
|
|
}
|
|
unlock_dev(pdev, FALSE);
|
|
status = usb_reset_pipe_ex(dev_mgr, endp_handle, usb_reset_pipe_from_dispatch_completion, purb);
|
|
|
|
goto LBL_OUT;
|
|
}
|
|
}
|
|
|
|
unlock_dev(pdev, FALSE);
|
|
status = hcd->hcd_submit_urb(hcd, pdev, purb->pendp, purb);
|
|
|
|
LBL_OUT:
|
|
usb_unlock_dev(pdev);
|
|
return status;
|
|
}
|
|
|
|
void
|
|
usb_config_dev_completion(PURB purb, PVOID context)
|
|
{
|
|
PURB puser_urb;
|
|
PUSB_DEV pdev;
|
|
PUSB_ENDPOINT pendp;
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
ULONG config_val;
|
|
NTSTATUS status;
|
|
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
UNREFERENCED_PARAMETER(context);
|
|
|
|
if (purb == NULL)
|
|
{
|
|
return;
|
|
}
|
|
pdev = purb->pdev;
|
|
pendp = purb->pendp;
|
|
|
|
if (pdev == NULL)
|
|
return;
|
|
|
|
if (purb->reference != 0)
|
|
puser_urb = (PURB) purb->reference;
|
|
else
|
|
puser_urb = NULL;
|
|
|
|
lock_dev(pdev, TRUE);
|
|
|
|
if (puser_urb)
|
|
puser_urb->status = purb->status;
|
|
|
|
if (purb->status != STATUS_SUCCESS)
|
|
{
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_FIRST_CONFIG)
|
|
{
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_ADDRESSED;
|
|
}
|
|
else if (dev_state(pdev) == USB_DEV_STATE_RECONFIG)
|
|
{
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_CONFIGURED;
|
|
|
|
}
|
|
goto LBL_OUT;
|
|
}
|
|
// now let's construct usb_config
|
|
if (!pdev->usb_config)
|
|
{
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
config_val = psetup->wValue;
|
|
status = dev_mgr_build_usb_config(pdev,
|
|
&pdev->desc_buf[sizeof(USB_DEVICE_DESC)],
|
|
config_val, pdev->pusb_dev_desc->bNumConfigurations);
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_ADDRESSED;
|
|
goto LBL_OUT;
|
|
}
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_CONFIGURED;
|
|
//this usb dev represents physical dev
|
|
if (pdev->pusb_dev_desc->bDeviceClass == USB_CLASS_HUB && pdev->pusb_dev_desc->bDeviceSubClass == 0)
|
|
{
|
|
pdev->flags &= ~USB_DEV_CLASS_MASK;
|
|
pdev->flags |= USB_DEV_CLASS_HUB;
|
|
}
|
|
else if (pdev->pusb_dev_desc->bDeviceClass == USB_CLASS_MASS_STORAGE
|
|
&& pdev->pusb_dev_desc->bDeviceSubClass == 0)
|
|
{
|
|
pdev->flags &= ~USB_DEV_CLASS_MASK;
|
|
pdev->flags |= USB_DEV_CLASS_MASSSTOR;
|
|
}
|
|
else
|
|
{
|
|
pdev->flags &= ~USB_DEV_CLASS_MASK;
|
|
pdev->flags |= USB_DEV_CLASS_SCANNER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//not supported
|
|
puser_urb->status = STATUS_NOT_SUPPORTED;
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_CONFIGURED;
|
|
}
|
|
|
|
LBL_OUT:
|
|
unlock_dev(pdev, TRUE);
|
|
usb_free_mem(purb);
|
|
if (puser_urb && puser_urb->completion)
|
|
puser_urb->completion(puser_urb, puser_urb->context);
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
usb_get_descriptor(PUSB_DEV pdev, PURB purb)
|
|
{
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
LONG idx, size, count, i;
|
|
PBYTE buf;
|
|
PUSB_CONFIGURATION_DESC pcfg_desc1;
|
|
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (pdev == NULL || purb == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (purb->data_buffer == NULL || purb->data_length == 0)
|
|
{
|
|
return purb->status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
lock_dev(pdev, FALSE);
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
if (pdev->desc_buf == NULL)
|
|
{
|
|
purb->status = STATUS_DEVICE_NOT_READY;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
if ((psetup->wValue >> 8) == USB_DT_CONFIG)
|
|
{
|
|
idx = (psetup->wValue & 0xff);
|
|
|
|
count = pdev->pusb_dev_desc->bNumConfigurations;
|
|
|
|
if (idx >= count)
|
|
{
|
|
purb->status = STATUS_INVALID_PARAMETER;
|
|
goto LBL_OUT;
|
|
}
|
|
buf = &pdev->desc_buf[sizeof(USB_DEVICE_DESC)];
|
|
pcfg_desc1 = usb_find_config_desc_by_idx(buf, idx, count);
|
|
if (pcfg_desc1 == NULL)
|
|
{
|
|
purb->status = STATUS_UNSUCCESSFUL;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
size = pcfg_desc1->wTotalLength;
|
|
size = size > purb->data_length ? purb->data_length : size;
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
purb->data_buffer[i] = ((PBYTE) pcfg_desc1)[i];
|
|
}
|
|
purb->status = STATUS_SUCCESS;
|
|
goto LBL_OUT;
|
|
|
|
}
|
|
else if ((psetup->wValue >> 8) == USB_DT_DEVICE)
|
|
{
|
|
size = purb->data_length > sizeof(USB_DEVICE_DESC) ? sizeof(USB_DEVICE_DESC) : purb->data_length;
|
|
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
purb->data_buffer[i] = ((PBYTE) pdev->pusb_dev_desc)[i];
|
|
}
|
|
purb->status = STATUS_SUCCESS;
|
|
}
|
|
|
|
LBL_OUT:
|
|
unlock_dev(pdev, FALSE);
|
|
return purb->status;
|
|
}
|
|
|
|
LONG
|
|
usb_count_list(PLIST_HEAD list_head)
|
|
{
|
|
LONG count;
|
|
PLIST_ENTRY pthis, pnext;
|
|
|
|
if (list_head == NULL)
|
|
return 0;
|
|
|
|
count = 0;
|
|
ListFirst(list_head, pthis);
|
|
|
|
while (pthis)
|
|
{
|
|
ListNext(list_head, pthis, pnext);
|
|
pthis = pnext;
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// checks if processor supports Time Stamp Counter
|
|
__inline BOOLEAN
|
|
usb_query_clicks(PLARGE_INTEGER clicks)
|
|
{
|
|
BOOLEAN ret_val;
|
|
//so we have to use intel's cpu???
|
|
ret_val = FALSE;
|
|
|
|
#ifdef _MSC_VER
|
|
__asm
|
|
{
|
|
push ebx;
|
|
push eax;
|
|
mov eax, 1; //read version
|
|
cpuid;
|
|
test edx, 0x10; //timer stamp
|
|
jz LBL_OUT;
|
|
// cpuid //serialization
|
|
rdtsc;
|
|
mov ebx, dword ptr[clicks];
|
|
mov dword ptr[ebx], eax;
|
|
mov dword ptr[ebx + 4], edx;
|
|
mov dword ptr[ret_val], TRUE;
|
|
LBL_OUT:
|
|
pop eax;
|
|
pop ebx;
|
|
}
|
|
#else
|
|
ret_val = FALSE;
|
|
#endif
|
|
return ret_val;
|
|
}
|
|
|
|
VOID
|
|
usb_wait_ms_dpc(ULONG ms)
|
|
{
|
|
LARGE_INTEGER Interval;
|
|
if (ms <= 0)
|
|
return;
|
|
|
|
Interval.QuadPart = -ms * 10000;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
|
|
}
|
|
|
|
|
|
VOID
|
|
usb_wait_us_dpc(ULONG us)
|
|
{
|
|
LARGE_INTEGER Interval;
|
|
if (us <= 0)
|
|
return;
|
|
|
|
Interval.QuadPart = -us;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
|
|
}
|
|
|
|
VOID
|
|
usb_cal_cpu_freq()
|
|
{
|
|
LARGE_INTEGER tick1, tick2;
|
|
LONG i;
|
|
// interval.QuadPart = -40 * 1000 * 1000;
|
|
|
|
if (cpu_clock_freq >= 100 * 1000 * 1000) // assume it is valid
|
|
return;
|
|
|
|
if (usb_query_clicks(&tick1))
|
|
{
|
|
for(i = 0; i < 25; i++)
|
|
{
|
|
usb_query_clicks(&tick1);
|
|
KeStallExecutionProcessor(40 * 1000);
|
|
usb_query_clicks(&tick2);
|
|
cpu_clock_freq += (ULONG) (tick2.QuadPart - tick1.QuadPart);
|
|
}
|
|
// cpu_clock_freq *= 1000;
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("usb_cal_cpu_freq(): cpu frequency = %d Hz\n", cpu_clock_freq));
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
usb_set_interface(PURB purb)
|
|
{
|
|
ULONG u;
|
|
PURB purb1;
|
|
PCTRL_REQ_STACK pstack;
|
|
PUSB_DEV pdev;
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
PUSB_ENDPOINT pendp;
|
|
NTSTATUS status;
|
|
|
|
PHCD hcd;
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
purb1 = purb;
|
|
pdev = purb->pdev;
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
|
|
lock_dev(pdev, FALSE);
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
if (dev_state(pdev) < USB_DEV_STATE_CONFIGURED)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_DEVICE_NOT_READY;
|
|
}
|
|
|
|
hcd = pdev->hcd;
|
|
|
|
if (psetup->wIndex >= pdev->usb_config->if_count)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (psetup->wValue >= pdev->usb_config->interf[psetup->wIndex].altif_count + 1)
|
|
{
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (pdev->usb_config->interf[psetup->wIndex].pusb_if_desc->bAlternateSetting == psetup->wValue)
|
|
{
|
|
// already the current interface
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
// check to see if the endp is busy
|
|
for(u = 0; u < pdev->usb_config->interf[psetup->wIndex].endp_count; u++)
|
|
{
|
|
// This check is not adquate. Since we do not have mechanism to block the new coming
|
|
// request during this request. the caller must guarantee no active or pending
|
|
// usb request on these endpoint.
|
|
pendp = &pdev->usb_config->interf[psetup->wIndex].endp[u];
|
|
if (usb_endp_busy_count(pendp))
|
|
{
|
|
// active urb on that endp
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_DEVICE_NOT_READY;
|
|
}
|
|
if (IsListEmpty(&pendp->urb_list))
|
|
{
|
|
// pending urb on that endp
|
|
unlock_dev(pdev, FALSE);
|
|
return STATUS_DEVICE_NOT_READY;
|
|
}
|
|
}
|
|
unlock_dev(pdev, FALSE);
|
|
|
|
if (purb1->ctrl_req_context.ctrl_stack_count == 0)
|
|
{
|
|
// ok, we have one stack cell for our use
|
|
if (purb1->completion != NULL)
|
|
{
|
|
purb1->ctrl_req_context.ctrl_stack_count = 1;
|
|
purb1->ctrl_req_context.ctrl_cur_stack = 0;
|
|
}
|
|
else
|
|
{
|
|
// use urb's completion and context
|
|
purb1->completion = usb_set_interface_completion;
|
|
purb1->context = pdev;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (purb->ctrl_req_context.ctrl_cur_stack + 1 >= purb->ctrl_req_context.ctrl_stack_count)
|
|
{
|
|
// stack full, let's allocate one new urb, we need stack size one
|
|
purb1 = usb_alloc_mem(NonPagedPool, sizeof(URB));
|
|
if (purb1 == NULL)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
RtlCopyMemory(purb1, purb, sizeof(URB));
|
|
|
|
// we do not use stack
|
|
RtlZeroMemory(purb1->ctrl_req_stack, sizeof(CTRL_REQ_STACK));
|
|
purb1->context = pdev;
|
|
purb1->completion = usb_set_interface_completion;
|
|
purb1->ctrl_parent_urb = purb;
|
|
purb1->ctrl_req_context.ctrl_req_flags = CTRL_PARENT_URB_VALID;
|
|
|
|
goto LBL_SEND_URB;
|
|
}
|
|
else
|
|
purb->ctrl_req_context.ctrl_cur_stack++;
|
|
}
|
|
|
|
u = purb1->ctrl_req_context.ctrl_cur_stack;
|
|
RtlZeroMemory(&purb1->ctrl_req_stack[u], sizeof(CTRL_REQ_STACK));
|
|
pstack = &purb1->ctrl_req_stack[u];
|
|
pstack->context = pdev;
|
|
pstack->urb_completion = usb_set_interface_completion;
|
|
|
|
LBL_SEND_URB:
|
|
if (hcd == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
status = hcd->hcd_submit_urb(hcd, purb->pdev, purb->pendp, purb);
|
|
return status;
|
|
}
|
|
|
|
#define usb_complete_and_free_ctrl_urb( pURB ) \
|
|
{\
|
|
UCHAR i, j;\
|
|
i = pURB->ctrl_req_context.ctrl_cur_stack;\
|
|
j = pURB->ctrl_req_context.ctrl_stack_count;\
|
|
usb_call_ctrl_completion( pURB );\
|
|
if( i == 0xff || j == 0 )\
|
|
usb_free_mem( pURB );\
|
|
}
|
|
|
|
VOID
|
|
usb_set_interface_completion(PURB purb, PVOID context)
|
|
{
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
PUSB_INTERFACE pif, palt_if;
|
|
USB_INTERFACE temp_if;
|
|
UCHAR if_idx, if_alt_idx;
|
|
PUSB_DEV pdev;
|
|
PUSB_ENDPOINT pendp;
|
|
ULONG i;
|
|
PLIST_ENTRY pthis, pnext;
|
|
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
UNREFERENCED_PARAMETER(context);
|
|
|
|
if (purb == NULL)
|
|
return;
|
|
|
|
if (purb->status == STATUS_SUCCESS)
|
|
{
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
|
|
if_idx = (UCHAR) psetup->wIndex;
|
|
if_alt_idx = (UCHAR) psetup->wValue;
|
|
pdev = purb->pdev;
|
|
RtlZeroMemory(&temp_if, sizeof(USB_INTERFACE));
|
|
|
|
lock_dev(pdev, TRUE);
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
purb->status = STATUS_DEVICE_NOT_CONNECTED;
|
|
purb->data_length = 0;
|
|
}
|
|
else
|
|
{
|
|
// let's swap the interface
|
|
pif = &pdev->usb_config->interf[if_idx];
|
|
ListFirst(&pif->altif_list, pthis);
|
|
pnext = pthis;
|
|
do
|
|
{
|
|
palt_if = struct_ptr(pthis, USB_INTERFACE, altif_list);
|
|
if (palt_if->pusb_if_desc->bAlternateSetting == if_alt_idx)
|
|
{
|
|
break;
|
|
}
|
|
palt_if = NULL;
|
|
ListNext(&pif->altif_list, pthis, pnext);
|
|
pthis = pnext;
|
|
|
|
} while (pthis);
|
|
|
|
if (palt_if != NULL)
|
|
{
|
|
RtlCopyMemory(&temp_if, palt_if, sizeof(USB_INTERFACE));
|
|
|
|
palt_if->endp_count = pif->endp_count;
|
|
RtlCopyMemory(palt_if->endp, pif->endp, sizeof(pif->endp));
|
|
palt_if->pif_drv = pif->pif_drv;
|
|
palt_if->pusb_if_desc = pif->pusb_if_desc;
|
|
for(i = 0; i < palt_if->endp_count; i++)
|
|
{
|
|
pendp = &palt_if->endp[i];
|
|
InitializeListHead(&pendp->urb_list);
|
|
pendp->flags = 0;
|
|
}
|
|
|
|
RtlCopyMemory(pif->endp, temp_if.endp, sizeof(temp_if.endp));
|
|
pif->endp_count = temp_if.endp_count;
|
|
pif->pusb_if_desc = temp_if.pusb_if_desc;
|
|
for(i = 0; i < pif->endp_count; i++)
|
|
{
|
|
pendp = &pif->endp[i];
|
|
InitializeListHead(&pendp->urb_list);
|
|
pendp->flags = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRAP();
|
|
purb->status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
unlock_dev(pdev, TRUE);
|
|
}
|
|
|
|
// for recursive reason, we have to store the parameter ahead
|
|
usb_complete_and_free_ctrl_urb(purb);
|
|
}
|
|
|
|
// can only be called when current completion finished and called only in
|
|
// urb completion. And this func may be called recursively, if this routine
|
|
// is called, the urb must be treated as released.
|
|
VOID
|
|
usb_call_ctrl_completion(PURB purb)
|
|
{
|
|
PURB parent_urb;
|
|
PCTRL_REQ_STACK pstack;
|
|
ULONG i;
|
|
|
|
|
|
if (purb == NULL)
|
|
return;
|
|
|
|
if (purb->ctrl_req_context.ctrl_stack_count != 0)
|
|
{
|
|
i = purb->ctrl_req_context.ctrl_cur_stack;
|
|
if (i > 0 && i < 0x10)
|
|
{
|
|
i--;
|
|
purb->ctrl_req_context.ctrl_cur_stack = (UCHAR) i;
|
|
pstack = &purb->ctrl_req_stack[i];
|
|
if (pstack->urb_completion)
|
|
{
|
|
pstack->urb_completion(purb, pstack->context);
|
|
}
|
|
else
|
|
TRAP();
|
|
}
|
|
else if (i == 0)
|
|
{
|
|
i = purb->ctrl_req_context.ctrl_cur_stack = 0xff;
|
|
if (purb->completion)
|
|
{
|
|
purb->completion(purb, purb->context);
|
|
}
|
|
else
|
|
TRAP();
|
|
}
|
|
else if (i == 0xff)
|
|
{
|
|
// only parent urb's completion, if parent urb exists, can be called
|
|
if (purb->ctrl_req_context.ctrl_req_flags & CTRL_PARENT_URB_VALID)
|
|
{
|
|
parent_urb = purb->ctrl_parent_urb;
|
|
if (parent_urb)
|
|
{
|
|
pstack = &parent_urb->ctrl_req_stack[parent_urb->ctrl_req_context.ctrl_cur_stack];
|
|
pstack->urb_completion(parent_urb, pstack->context);
|
|
}
|
|
else
|
|
TRAP();
|
|
}
|
|
}
|
|
else
|
|
TRAP();
|
|
}
|
|
else if (purb->ctrl_req_context.ctrl_req_flags & CTRL_PARENT_URB_VALID)
|
|
{
|
|
// this is the case when the child urb won't use the stack
|
|
parent_urb = purb->ctrl_parent_urb;
|
|
if (parent_urb)
|
|
{
|
|
// pstack = &parent_urb->ctrl_req_stack[ parent_urb->ctrl_req_context.ctrl_cur_stack ];
|
|
// pstack->urb_completion( parent_urb, pstack->context );
|
|
usb_call_ctrl_completion(parent_urb);
|
|
}
|
|
else
|
|
TRAP();
|
|
}
|
|
else
|
|
return;
|
|
}
|