reactos/drivers/usb/nt4compat/usbdriver/usb.c
Amine Khaldi c424146e2c Create a branch for cmake bringup.
svn path=/branches/cmake-bringup/; revision=48236
2010-07-24 18:52:44 +00:00

1358 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;
}