reactos/drivers/usb/nt4compat/usbdriver/uhci.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

3898 lines
110 KiB
C

/**
* uhci.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"
//----------------------------------------------------------
// uhci routines
//#define DEMO
#ifdef INCLUDE_EHCI
#define rh_port1_status rh_port_status[ 1 ]
#define rh_port2_status rh_port_status[ 2 ]
extern PDEVICE_OBJECT ehci_probe(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, PUSB_DEV_MANAGER dev_mgr);
#endif
#define DEFAULT_ENDP( enDP ) \
( enDP->flags & USB_ENDP_FLAG_DEFAULT_ENDP )
#define dev_from_endp( enDP ) \
( DEFAULT_ENDP( enDP )\
? ( ( PUSB_DEV )( enDP )->pusb_if )\
: ( ( enDP )->pusb_if->pusb_config->pusb_dev ) )
#define endp_state( enDP ) ( ( enDP )->flags & USB_ENDP_FLAG_STAT_MASK )
#define endp_num( enDP ) \
( DEFAULT_ENDP( enDP )\
? 0 \
: ( ( enDP )->pusb_endp_desc->bEndpointAddress & 0x0f ) )
#define endp_dir( enDP ) \
( DEFAULT_ENDP( enDP )\
? 0L\
: ( ( enDP )->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) )
#define dev_set_state( pdEV, staTE ) \
( pdEV->flags = ( ( pdEV )->flags & ( ~USB_DEV_STATE_MASK ) ) | ( staTE ) )
#define endp_max_packet_size( enDP ) \
( DEFAULT_ENDP( enDP )\
? ( ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc ? \
( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc->bMaxPacketSize0\
: 8 )\
: enDP->pusb_endp_desc->wMaxPacketSize )
#define release_adapter( padapTER ) HalPutDmaAdapter(padapTER)
#define get_int_idx( _urb, _idx ) \
{\
UCHAR interVAL;\
interVAL = ( UCHAR )( ( _urb )->pipe >> 24 );\
for( _idx = 1; _idx < 9; _idx++ )\
{\
interVAL >>= 1;\
if( !interVAL )\
break;\
}\
_idx --;\
}
#define uhci_insert_urb_to_schedule( uHCI, pURB, rET ) \
{\
SYNC_PARAM sync_param;\
sync_param.uhci = uHCI;\
sync_param.context = pURB;\
\
rET = KeSynchronizeExecution( uHCI->pdev_ext->uhci_int, uhci_sync_insert_urb_schedule, &sync_param );\
}
//declarations
typedef struct
{
PUHCI_DEV uhci;
PVOID context;
ULONG ret;
} SYNC_PARAM, *PSYNC_PARAM;
PDEVICE_OBJECT
uhci_alloc(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, ULONG bus_addr, PUSB_DEV_MANAGER dev_mgr);
BOOLEAN uhci_init_schedule(PUHCI_DEV uhci, PADAPTER_OBJECT padapter);
BOOLEAN uhci_release(PDEVICE_OBJECT pdev, PUSB_DEV_MANAGER dev_mgr);
static VOID uhci_stop(PUHCI_DEV uhci);
BOOLEAN uhci_destroy_schedule(PUHCI_DEV uhci);
BOOLEAN NTAPI uhci_sync_insert_urb_schedule(PVOID context);
VOID uhci_init_hcd_interface(PUHCI_DEV uhci);
NTSTATUS uhci_rh_submit_urb(PUSB_DEV rh, PURB purb);
NTSTATUS uhci_dispatch_irp(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp);
extern VOID rh_timer_svc_reset_port_completion(PUSB_DEV dev, PVOID context);
extern VOID rh_timer_svc_int_completion(PUSB_DEV dev, PVOID context);
ULONG debug_level = DBGLVL_MINIMUM;//DBGLVL_MAXIMUM;
PDRIVER_OBJECT usb_driver_obj = NULL;
extern USB_DEV_MANAGER g_dev_mgr;
//pending endpoint pool funcs
VOID
uhci_wait_ms(PUHCI_DEV uhci, LONG ms)
{
LARGE_INTEGER lms;
if (ms <= 0)
return;
lms.QuadPart = -10 * ms;
KeSetTimer(&uhci->reset_timer, lms, NULL);
KeWaitForSingleObject(&uhci->reset_timer, Executive, KernelMode, FALSE, NULL);
return;
}
BOOLEAN
init_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool)
{
int i;
if (pool == NULL)
return FALSE;
pool->pending_endp_array =
usb_alloc_mem(NonPagedPool, sizeof(UHCI_PENDING_ENDP) * UHCI_MAX_PENDING_ENDPS);
InitializeListHead(&pool->free_que);
pool->free_count = 0;
pool->total_count = UHCI_MAX_PENDING_ENDPS;
KeInitializeSpinLock(&pool->pool_lock);
for(i = 0; i < MAX_TIMER_SVCS; i++)
{
free_pending_endp(pool, &pool->pending_endp_array[i]);
}
return TRUE;
}
BOOLEAN
free_pending_endp(PUHCI_PENDING_ENDP_POOL pool, PUHCI_PENDING_ENDP pending_endp)
{
if (pool == NULL || pending_endp == NULL)
{
return FALSE;
}
RtlZeroMemory(pending_endp, sizeof(UHCI_PENDING_ENDP));
InsertTailList(&pool->free_que, &pending_endp->endp_link);
pool->free_count++;
return TRUE;
}
PUHCI_PENDING_ENDP
alloc_pending_endp(PUHCI_PENDING_ENDP_POOL pool, LONG count)
{
PUHCI_PENDING_ENDP new_endp;
if (pool == NULL || count != 1)
return NULL;
if (pool->free_count <= 0)
return NULL;
new_endp = (PUHCI_PENDING_ENDP) RemoveHeadList(&pool->free_que);
pool->free_count--;
return new_endp;
}
BOOLEAN
destroy_pending_endp_pool(PUHCI_PENDING_ENDP_POOL pool)
{
if (pool == NULL)
return FALSE;
InitializeListHead(&pool->free_que);
pool->free_count = pool->total_count = 0;
usb_free_mem(pool->pending_endp_array);
pool->pending_endp_array = NULL;
return TRUE;
}
//end of pending endpoint pool funcs
static void
uhci_fill_td(PUHCI_TD td, ULONG status, ULONG info, ULONG buffer)
{
td->status = status;
td->info = info;
td->buffer = buffer;
}
BOOLEAN
uhci_insert_td_fl(PUHCI_TD prev_td, PUHCI_TD ptd)
{
PLIST_ENTRY temp_entry;
if (prev_td == NULL || ptd == NULL)
return FALSE;
temp_entry = &prev_td->ptde->hori_link;
ptd->link = (struct_ptr(temp_entry, TD_EXTENSION, hori_link))->ptd->phy_addr;
prev_td->link = ptd->phy_addr;
InsertHeadList(&prev_td->ptde->hori_link, &ptd->ptde->hori_link);
return TRUE;
}
BOOLEAN
uhci_remove_td_fl(PUHCI_TD ptd)
{
PUHCI_TD prev_td;
if (ptd == NULL)
return FALSE;
prev_td = (struct_ptr(ptd->ptde->hori_link.Blink, TD_EXTENSION, hori_link))->ptd;
prev_td->link = ptd->link;
ptd->link = UHCI_PTR_TERM;
RemoveEntryList(&ptd->ptde->hori_link);
return FALSE;
}
BOOLEAN
uhci_insert_qh_fl(PVOID prev_item, PUHCI_QH pqh)
{
//only horizontal link allowed
PUHCI_QH pprev_qh;
PUHCI_TD pprev_td;
PLIST_ENTRY temp_entry;
if (prev_item == NULL || pqh == NULL)
return FALSE;
if ((((PUHCI_TD) prev_item)->ptde->flags & UHCI_ITEM_FLAG_TYPE) == UHCI_ITEM_FLAG_QH)
{
pprev_qh = (PUHCI_QH) prev_item;
temp_entry = pprev_qh->pqhe->hori_link.Flink;
pqh->link = (struct_ptr(temp_entry, TD_EXTENSION, hori_link))->ptd->phy_addr;
pprev_qh->link = pqh->phy_addr;
InsertHeadList(&pprev_qh->pqhe->hori_link, &pqh->pqhe->hori_link);
}
else
{
pprev_td = ((PUHCI_TD) prev_item);
temp_entry = pprev_td->ptde->hori_link.Flink;
pprev_td->link = pqh->phy_addr;
pqh->link = (struct_ptr(temp_entry, TD_EXTENSION, hori_link))->ptd->phy_addr;
InsertHeadList(&pprev_td->ptde->hori_link, &pqh->pqhe->hori_link);
}
return FALSE;
}
BOOLEAN
uhci_remove_qh_fl(PUHCI_QH pqh)
{
PVOID prev_item;
PUHCI_QH pprevqh;
PUHCI_TD pprevtd;
if (pqh == NULL)
return FALSE;
prev_item = (struct_ptr(pqh->pqhe->hori_link.Blink, TD_EXTENSION, hori_link))->ptd;
if ((((PUHCI_TD) prev_item)->ptde->flags & UHCI_ITEM_FLAG_TYPE) == UHCI_ITEM_FLAG_QH)
{
pprevqh = (PUHCI_QH) prev_item;
pprevqh->link = pqh->link;
}
else
{
pprevtd = ((PUHCI_TD) prev_item);
pprevtd->link = pqh->link;
}
RemoveEntryList(&pqh->pqhe->hori_link);
pqh->link = UHCI_PTR_TERM;
pqh->pqhe->hori_link.Flink = pqh->pqhe->hori_link.Blink = NULL;
return TRUE;
}
BOOLEAN
uhci_init_frame_list(PUHCI_DEV uhci, PADAPTER_OBJECT padapter)
{
int i;
if (uhci == NULL || padapter == NULL)
return FALSE;
//note: frame_list_lock will be connected to interrupt
KeInitializeSpinLock(&uhci->frame_list_lock);
uhci->io_buf = HalAllocateCommonBuffer(padapter, 4096, &uhci->io_buf_logic_addr, FALSE);
if (uhci->io_buf == NULL)
return FALSE;
uhci->frame_list =
HalAllocateCommonBuffer(padapter,
sizeof(ULONG) * UHCI_MAX_FRAMES, &uhci->frame_list_logic_addr, FALSE);
if (uhci->frame_list == NULL)
return FALSE;
RtlZeroMemory(uhci->frame_list, sizeof(ULONG) * UHCI_MAX_FRAMES);
uhci->frame_list_cpu = usb_alloc_mem(NonPagedPool, sizeof(FRAME_LIST_CPU_ENTRY) * UHCI_MAX_FRAMES);
if (uhci->frame_list_cpu == NULL)
return FALSE;
for(i = 0; i < UHCI_MAX_FRAMES; i++)
InitializeListHead(&uhci->frame_list_cpu[i].td_link);
uhci->frame_bw = usb_alloc_mem(NonPagedPool, sizeof(LONG) * UHCI_MAX_FRAMES);
if (uhci->frame_bw == NULL)
return FALSE;
for(i = 0; i < UHCI_MAX_FRAMES; i++)
{
uhci->frame_bw[i] = FRAME_TIME_MAX_USECS_ALLOC;
}
uhci->fsbr_cnt = 0;
return TRUE;
}
BOOLEAN
uhci_destroy_frame_list(PUHCI_DEV uhci)
{
if (uhci == NULL)
return FALSE;
if (uhci->frame_list)
HalFreeCommonBuffer(uhci->pdev_ext->padapter,
sizeof(ULONG) * UHCI_MAX_FRAMES,
uhci->frame_list_logic_addr, uhci->frame_list, FALSE);
uhci->frame_list = NULL;
uhci->frame_list_logic_addr.LowPart = 0;
uhci->frame_list_logic_addr.HighPart = 0;
if (uhci->frame_list_cpu)
usb_free_mem(uhci->frame_list_cpu);
uhci->frame_list_cpu = NULL;
if (uhci->frame_bw)
usb_free_mem(uhci->frame_bw);
uhci->frame_bw = NULL;
return TRUE;
}
PDEVICE_OBJECT
uhci_create_device(PDRIVER_OBJECT drvr_obj, PUSB_DEV_MANAGER dev_mgr)
{
NTSTATUS status;
PDEVICE_OBJECT pdev;
PDEVICE_EXTENSION pdev_ext;
UNICODE_STRING dev_name;
UNICODE_STRING symb_name;
STRING string, another_string;
CHAR str_dev_name[64], str_symb_name[64];
UCHAR hcd_id;
if (drvr_obj == NULL)
return NULL;
ASSERT(dev_mgr != NULL);
//note: hcd count wont increment till the hcd is registered in dev_mgr
sprintf(str_dev_name, "%s%d", UHCI_DEVICE_NAME, dev_mgr->hcd_count);
sprintf(str_symb_name, "%s%d", DOS_DEVICE_NAME, dev_mgr->hcd_count);
RtlInitString(&string, str_dev_name);
RtlAnsiStringToUnicodeString(&dev_name, &string, TRUE);
pdev = NULL;
status = IoCreateDevice(drvr_obj,
sizeof(DEVICE_EXTENSION) + sizeof(UHCI_DEV),
&dev_name, FILE_UHCI_DEV_TYPE, 0, FALSE, &pdev);
if (status != STATUS_SUCCESS || pdev == NULL)
{
RtlFreeUnicodeString(&dev_name);
return NULL;
}
pdev_ext = pdev->DeviceExtension;
RtlZeroMemory(pdev_ext, sizeof(DEVICE_EXTENSION) + sizeof(UHCI_DEV));
pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_HCD;
pdev_ext->dev_ext_hdr.dispatch = uhci_dispatch_irp;
pdev_ext->dev_ext_hdr.start_io = NULL; //we do not support startio
pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr;
pdev_ext->pdev_obj = pdev;
pdev_ext->pdrvr_obj = drvr_obj;
pdev_ext->uhci = (PUHCI_DEV) & (pdev_ext[1]);
RtlInitString(&another_string, str_symb_name);
RtlAnsiStringToUnicodeString(&symb_name, &another_string, TRUE);
IoCreateSymbolicLink(&symb_name, &dev_name);
uhci_dbg_print(DBGLVL_MAXIMUM,
("uhci_create_device(): dev=0x%x\n, pdev_ext= 0x%x, uhci=0x%x, dev_mgr=0x%x\n", pdev,
pdev_ext, pdev_ext->uhci, dev_mgr));
RtlFreeUnicodeString(&dev_name);
RtlFreeUnicodeString(&symb_name);
//register with dev_mgr though it is not initilized
uhci_init_hcd_interface(pdev_ext->uhci);
hcd_id = dev_mgr_register_hcd(dev_mgr, &pdev_ext->uhci->hcd_interf);
pdev_ext->uhci->hcd_interf.hcd_set_id(&pdev_ext->uhci->hcd_interf, hcd_id);
pdev_ext->uhci->hcd_interf.hcd_set_dev_mgr(&pdev_ext->uhci->hcd_interf, dev_mgr);
return pdev;
}
BOOLEAN
uhci_delete_device(PDEVICE_OBJECT pdev, PUSB_DEV_MANAGER dev_mgr)
{
STRING string;
UNICODE_STRING symb_name;
PDEVICE_EXTENSION pdev_ext;
CHAR str_symb_name[64];
if (pdev == NULL)
return FALSE;
pdev_ext = pdev->DeviceExtension;
sprintf(str_symb_name,
"%s%d", DOS_DEVICE_NAME, pdev_ext->uhci->hcd_interf.hcd_get_id(&pdev_ext->uhci->hcd_interf));
RtlInitString(&string, str_symb_name);
RtlAnsiStringToUnicodeString(&symb_name, &string, TRUE);
IoDeleteSymbolicLink(&symb_name);
RtlFreeUnicodeString(&symb_name);
dev_mgr_deregister_hcd(dev_mgr, pdev_ext->uhci->hcd_interf.hcd_get_id(&pdev_ext->uhci->hcd_interf));
if (pdev_ext->res_list)
ExFreePool(pdev_ext->res_list); // not allocated by usb_alloc_mem
IoDeleteDevice(pdev);
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_delete_device(): device deleted\n"));
return TRUE;
}
// we can not use endp here for it is within the dev scope, and
// we can not acquire the dev-lock, fortunately we saved some
// info in urb->pipe in uhci_internal_submit_XXX.
BOOLEAN NTAPI
uhci_isr(PKINTERRUPT interrupt, PVOID context)
{
PUHCI_DEV uhci;
USHORT status;
PLIST_ENTRY pthis, pnext;
PURB purb;
UNREFERENCED_PARAMETER(interrupt);
UNREFERENCED_PARAMETER(context);
uhci_dbg_print(DBGLVL_ULTRA, ("uhci_isr(): context=0x%x\n", context));
/*
* Read the interrupt status, and write it back to clear the
* interrupt cause
*/
uhci = (PUHCI_DEV) context;
if (uhci == NULL)
return FALSE;
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + USBSTS));
if (!status) /* shared interrupt, not mine */
return FALSE;
if (status != 1)
{
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_isr(): current uhci status=0x%x\n", status));
}
else
{
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_isr(): congratulations, no error occurs\n"));
}
/* clear it */
WRITE_PORT_USHORT((PUSHORT) (uhci->port_base + USBSTS), status);
if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD))
{
if (status & USBSTS_HSE)
{
DbgPrint("uhci_isr(): host system error, PCI problems?\n");
//for( ; ; );
}
if (status & USBSTS_HCPE)
{
DbgPrint("uhci_isr(): host controller process error. something bad happened\n");
//for( ; ; );
//for( ; ; );
}
if ((status & USBSTS_HCH)) //&& !uhci->is_suspended
{
DbgPrint("uhci_isr(): host controller halted. very bad\n");
/* FIXME: Reset the controller, fix the offending TD */
}
}
// don't no how to handle it yet
//if (status & USBSTS_RD)
//{
//uhci_wakeup(uhci);
//}*/
//let's remove those force-cancel urbs from the schedule first
ListFirst(&uhci->urb_list, pthis);
while (pthis)
{
purb = (PURB) pthis;
if (purb->flags & URB_FLAG_FORCE_CANCEL)
{
uhci_remove_urb_from_schedule(uhci, purb);
}
ListNext(&uhci->urb_list, pthis, pnext);
pthis = pnext;
}
//clear the interrupt if the urb is force canceled
uhci->skel_term_td->status &= ~TD_CTRL_IOC;
//next we need to find if anything fininshed
ListFirst(&uhci->urb_list, pthis);
while (pthis)
{
purb = (PURB) pthis;
if (purb->flags & URB_FLAG_IN_SCHEDULE)
{
if (uhci_is_xfer_finished(purb))
uhci_remove_urb_from_schedule(uhci, purb);
}
ListNext(&uhci->urb_list, pthis, pnext);
pthis = pnext;
}
KeInsertQueueDpc(&uhci->pdev_ext->uhci_dpc, uhci, 0);
return TRUE;
}
BOOLEAN NTAPI
uhci_cal_cpu_freq(PVOID context)
{
UNREFERENCED_PARAMETER(context);
usb_cal_cpu_freq();
return TRUE;
}
PDEVICE_OBJECT
uhci_probe(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, PUSB_DEV_MANAGER dev_mgr)
{
LONG bus, i, j, ret = 0;
PCI_SLOT_NUMBER slot_num;
PPCI_COMMON_CONFIG pci_config;
PDEVICE_OBJECT pdev;
BYTE buffer[sizeof(PCI_COMMON_CONFIG)];
LONG count;
PDEVICE_EXTENSION pdev_ext;
slot_num.u.AsULONG = 0;
pci_config = (PPCI_COMMON_CONFIG) buffer;
count = 0;
pdev = NULL;
//scan the PCI buses to find uhci controller
for (bus = 0; bus <= PCI_MAX_BRIDGE_NUMBER; bus++)
{
for(i = 0; i <= PCI_MAX_DEVICES; i++)
{
slot_num.u.bits.DeviceNumber = i;
for(j = 0; j <= PCI_MAX_FUNCTION; j++)
{
slot_num.u.bits.FunctionNumber = j;
ret = HalGetBusData(PCIConfiguration,
bus, slot_num.u.AsULONG, pci_config, PCI_COMMON_HDR_LENGTH);
if (ret == 0) /*no this bus */
break;
if (ret == 2) /*no device on the slot */
break;
if (pci_config->BaseClass == 0x0c && pci_config->SubClass == 0x03 &&
pci_config->ProgIf == 0x00)
{
// well, we find our usb host controller, create device
pdev = uhci_alloc(drvr_obj, reg_path, ((bus << 8) | (i << 3) | j), dev_mgr);
if (pdev)
#ifdef _MULTI_UHCI
count++;
#else
goto LBL_LOOPOUT;
#endif
}
}
if (ret == 0)
break;
}
}
#ifndef _MULTI_UHCI
LBL_LOOPOUT:
#endif
DbgPrint("Found %d UHCI controllers\n", count);
if (pdev)
{
pdev_ext = pdev->DeviceExtension;
if (pdev_ext)
{
// acquire higher irql to eliminate pre-empty
KeSynchronizeExecution(pdev_ext->uhci_int, uhci_cal_cpu_freq, NULL);
}
}
return pdev;
}
PDEVICE_OBJECT
uhci_alloc(PDRIVER_OBJECT drvr_obj, PUNICODE_STRING reg_path, ULONG bus_addr, PUSB_DEV_MANAGER dev_mgr)
{
LONG frd_num, prd_num;
PDEVICE_OBJECT pdev;
PDEVICE_EXTENSION pdev_ext;
ULONG vector, addr_space;
LONG bus;
KIRQL irql;
KAFFINITY affinity;
DEVICE_DESCRIPTION dev_desc;
CM_PARTIAL_RESOURCE_DESCRIPTOR *pprd;
PCI_SLOT_NUMBER slot_num;
NTSTATUS status;
pdev = uhci_create_device(drvr_obj, dev_mgr);
if (pdev == NULL)
return pdev;
pdev_ext = pdev->DeviceExtension;
pdev_ext->pci_addr = bus_addr;
bus = (bus_addr >> 8);
slot_num.u.AsULONG = 0;
slot_num.u.bits.DeviceNumber = ((bus_addr & 0xff) >> 3);
slot_num.u.bits.FunctionNumber = (bus_addr & 0x07);
//now create adapter object
RtlZeroMemory(&dev_desc, sizeof(dev_desc));
dev_desc.Version = DEVICE_DESCRIPTION_VERSION;
dev_desc.Master = TRUE;
dev_desc.ScatterGather = TRUE;
dev_desc.Dma32BitAddresses = TRUE;
dev_desc.BusNumber = bus;
dev_desc.InterfaceType = PCIBus;
dev_desc.MaximumLength =
UHCI_MAX_POOL_TDS * sizeof(UHCI_TD) * UHCI_MAX_TD_POOLS
+ sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS + sizeof(ULONG) * UHCI_MAX_FRAMES;
pdev_ext->map_regs = 2; // UHCI_MAX_TD_POOLS +
//+ BYTES_TO_PAGES( ( UHCI_MAX_POOL_TDS * 64 ) * UHCI_MAX_TD_POOLS ) ;
pdev_ext->padapter = HalGetAdapter(&dev_desc, &pdev_ext->map_regs);
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_alloc(): padapter=0x%x\n", pdev_ext->padapter));
if (pdev_ext->padapter == NULL)
{
//fatal error
uhci_delete_device(pdev, dev_mgr);
return NULL;
}
DbgPrint("uhci_alloc(): reg_path=%p, \n \
uhci_alloc(): PCIBus=0x%x, bus=0x%x, bus_addr=0x%x \n \
uhci_alloc(): slot_num=0x%x, &res_list=%p \n", reg_path, (DWORD) PCIBus, (DWORD) bus,
(DWORD) bus_addr, (DWORD) slot_num.u.AsULONG, & pdev_ext->res_list);
//let's allocate resources for this device
DbgPrint("uhci_alloc(): about to assign slot res\n");
if ((status = HalAssignSlotResources(reg_path, NULL, //no class name yet
drvr_obj, NULL, //no support of another uhci controller
PCIBus,
bus, slot_num.u.AsULONG, &pdev_ext->res_list)) != STATUS_SUCCESS)
{
DbgPrint("uhci_alloc(): error assign slot res, 0x%x\n", status);
release_adapter(pdev_ext->padapter);
pdev_ext->padapter = NULL;
uhci_delete_device(pdev, dev_mgr);
return NULL;
}
//parse the resource list
for(frd_num = 0; frd_num < (LONG) pdev_ext->res_list->Count; frd_num++)
{
for(prd_num = 0; prd_num < (LONG) pdev_ext->res_list->List[frd_num].PartialResourceList.Count;
prd_num++)
{
pprd = &pdev_ext->res_list->List[frd_num].PartialResourceList.PartialDescriptors[prd_num];
if (pprd->Type == CmResourceTypePort)
{
RtlCopyMemory(&pdev_ext->res_port, &pprd->u.Port, sizeof(pprd->u.Port));
}
else if (pprd->Type == CmResourceTypeInterrupt)
{
RtlCopyMemory(&pdev_ext->res_interrupt, &pprd->u.Interrupt, sizeof(pprd->u.Interrupt));
}
}
}
//for port, translate them to system address
addr_space = 1;
if (HalTranslateBusAddress(PCIBus, bus, pdev_ext->res_port.Start, &addr_space, //io space
&pdev_ext->uhci->uhci_reg_base) != (BOOLEAN) TRUE)
{
DbgPrint("uhci_alloc(): error, can not translate bus address\n");
release_adapter(pdev_ext->padapter);
pdev_ext->padapter = NULL;
uhci_delete_device(pdev, dev_mgr);
return NULL;
}
DbgPrint("uhci_alloc(): address space=0x%x\n, reg_base=0x%x\n",
addr_space, pdev_ext->uhci->uhci_reg_base.u.LowPart);
if (addr_space == 0)
{
//port has been mapped to memory space
pdev_ext->uhci->port_mapped = TRUE;
pdev_ext->uhci->port_base = (PBYTE) MmMapIoSpace(pdev_ext->uhci->uhci_reg_base,
pdev_ext->res_port.Length, FALSE);
//fatal error can not map the registers
if (pdev_ext->uhci->port_base == NULL)
{
release_adapter(pdev_ext->padapter);
pdev_ext->padapter = NULL;
uhci_delete_device(pdev, dev_mgr);
return NULL;
}
}
else
{
//io space
pdev_ext->uhci->port_mapped = FALSE;
pdev_ext->uhci->port_base = (PBYTE) pdev_ext->uhci->uhci_reg_base.LowPart;
}
//before we connect the interrupt, we have to init uhci
pdev_ext->uhci->fsbr_cnt = 0;
pdev_ext->uhci->pdev_ext = pdev_ext;
if (uhci_init_schedule(pdev_ext->uhci, pdev_ext->padapter) == FALSE)
{
release_adapter(pdev_ext->padapter);
pdev_ext->padapter = NULL;
uhci_delete_device(pdev, dev_mgr);
return NULL;
}
InitializeListHead(&pdev_ext->uhci->urb_list);
KeInitializeSpinLock(&pdev_ext->uhci->pending_endp_list_lock);
InitializeListHead(&pdev_ext->uhci->pending_endp_list);
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_alloc(): pending_endp_list=0x%x\n",
&pdev_ext->uhci->pending_endp_list));
init_pending_endp_pool(&pdev_ext->uhci->pending_endp_pool);
KeInitializeTimer(&pdev_ext->uhci->reset_timer);
vector = HalGetInterruptVector(PCIBus,
bus,
pdev_ext->res_interrupt.level,
pdev_ext->res_interrupt.vector,
&irql,
&affinity);
KeInitializeDpc(&pdev_ext->uhci_dpc, uhci_dpc_callback, (PVOID) pdev_ext->uhci);
//connect the interrupt
DbgPrint("uhci_alloc(): the int=0x%x\n", vector);
if (IoConnectInterrupt(&pdev_ext->uhci_int,
uhci_isr,
pdev_ext->uhci,
NULL, //&pdev_ext->uhci->frame_list_lock,
vector,
irql,
irql,
LevelSensitive,
TRUE, //share the vector
affinity,
FALSE) //No float save
!= STATUS_SUCCESS)
{
uhci_release(pdev, dev_mgr);
return NULL;
}
return pdev;
}
BOOLEAN
uhci_release(PDEVICE_OBJECT pdev, PUSB_DEV_MANAGER dev_mgr)
{
PDEVICE_EXTENSION pdev_ext;
PUHCI_DEV uhci;
if (pdev == NULL)
return FALSE;
pdev_ext = pdev->DeviceExtension;
if (pdev_ext == NULL)
return FALSE;
uhci = pdev_ext->uhci;
if (uhci == NULL)
return FALSE;
uhci_stop(uhci);
//pdev_ext->uhci->conn_count = 0;
pdev_ext->uhci->fsbr_cnt = 0;
if (pdev_ext->uhci_int)
{
IoDisconnectInterrupt(pdev_ext->uhci_int);
pdev_ext->uhci_int = NULL;
}
else
TRAP();
destroy_pending_endp_pool(&pdev_ext->uhci->pending_endp_pool);
//pdev_ext->uhci->pending_endp_pool = NULL;
uhci_destroy_schedule(uhci);
release_adapter(pdev_ext->padapter);
pdev_ext->padapter = NULL;
uhci_delete_device(pdev, dev_mgr);
return FALSE;
}
// send cmds to start the uhc
// shamelessly copied from linux's uhci.c (reset_hc(), configure_hc() routines)
BOOLEAN
uhci_start(PHCD hcd)
{
PUHCI_DEV uhci;
PBYTE io_addr;
USHORT pirq;
PCI_SLOT_NUMBER SlotNum;
int timeout = 10000;
uhci = uhci_from_hcd(hcd);
io_addr = uhci->port_base;
/*
* Reset the HC - this will force us to get a
* new notification of any already connected
* ports due to the virtual disconnect that it
* implies.
*/
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBCMD), USBCMD_HCRESET);
while (READ_PORT_USHORT((PUSHORT) (io_addr + USBCMD)) & USBCMD_HCRESET)
{
if (!--timeout)
{
break;
}
}
/* Turn on all interrupts */
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBINTR),
USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP);
/* Start at frame 0 */
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBFRNUM), 0);
WRITE_PORT_ULONG((PULONG) (io_addr + USBFLBASEADD), uhci->frame_list_logic_addr.LowPart);
/* Run and mark it configured with a 64-byte max packet */
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBCMD), USBCMD_RS | USBCMD_CF | USBCMD_MAXP);
DbgPrint("uhci_start(): current uhci status=0x%x\n", uhci_status(uhci));
/* Enable PIRQ */
pirq = USBLEGSUP_DEFAULT;
SlotNum.u.AsULONG = 0;
SlotNum.u.bits.DeviceNumber = ((uhci->pdev_ext->pci_addr & 0xff) >> 3);
SlotNum.u.bits.FunctionNumber = (uhci->pdev_ext->pci_addr & 0x07);
DbgPrint("uhci_start(): set bus %d data at slot 0x%x\n", (uhci->pdev_ext->pci_addr >> 8),
SlotNum.u.AsULONG);
HalSetBusDataByOffset(PCIConfiguration, (uhci->pdev_ext->pci_addr >> 8), SlotNum.u.AsULONG,
&pirq, USBLEGSUP, sizeof(pirq));
return TRUE;
}
VOID
uhci_stop(PUHCI_DEV uhci)
{
PBYTE io_addr = uhci->port_base;
// turn off all the interrupt
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBINTR), 0);
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBCMD), 0);
}
static VOID
uhci_reset(PUHCI_DEV uhci)
{
PBYTE io_addr = uhci->port_base;
uhci_stop(uhci);
/* Global reset for 50ms */
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBCMD), USBCMD_GRESET);
//uhci_wait_ms( uhci, 50 );
usb_wait_ms_dpc(50);
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBCMD), 0);
//uhci_wait_ms( uhci, 10 );
usb_wait_ms_dpc(10);
}
VOID
uhci_suspend(PUHCI_DEV uhci)
{
PBYTE io_addr = uhci->port_base;
//uhci->is_suspended = 1;
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBCMD), USBCMD_EGSM);
}
VOID
uhci_wakeup(PUHCI_DEV uhci)
{
PBYTE io_addr;
unsigned int status;
io_addr = uhci->port_base;
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBCMD), 0);
/* wait for EOP to be sent */
status = READ_PORT_USHORT((PUSHORT) (io_addr + USBCMD));
while (status & USBCMD_FGR)
status = READ_PORT_USHORT((PUSHORT) (io_addr + USBCMD));
//uhci->is_suspended = 0;
/* Run and mark it configured with a 64-byte max packet */
WRITE_PORT_USHORT((PUSHORT) (io_addr + USBCMD), USBCMD_RS | USBCMD_CF | USBCMD_MAXP);
}
BOOLEAN
uhci_init_schedule(PUHCI_DEV uhci, PADAPTER_OBJECT padapter)
{
int i, irq;
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_init_schedule(): entering..., uhci=0x%x\n", uhci));
if (uhci == NULL || padapter == NULL)
return FALSE;
if (init_td_pool_list(&uhci->td_pool, padapter) == FALSE)
{
return FALSE;
}
if (init_qh_pool(&uhci->qh_pool, padapter) == FALSE)
{
return FALSE;
}
//since uhci is not started we can freely access all resources.
for(i = 0; i < UHCI_MAX_SKELTDS; i++)
{
uhci->skel_td[i] = alloc_td(&uhci->td_pool);
uhci_fill_td(uhci->skel_td[i], 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
if (i > 0)
{
uhci->skel_td[i]->link = uhci->skel_td[i - 1]->phy_addr;
}
}
/*for( i = UHCI_MAX_SKELTDS - 3; i >= 0; i-- )
{
InsertTailList( &uhci->skel_int256_td->ptde->hori_link,
&uhci->skel_td[ i ]->ptde->hori_link );
} */
for(i = 0; i < UHCI_MAX_SKELQHS; i++)
{
uhci->skel_qh[i] = alloc_qh(&uhci->qh_pool);
if (i > 0)
{
uhci->skel_qh[i - 1]->link = uhci->skel_qh[i]->phy_addr;
}
uhci->skel_qh[i]->element = UHCI_PTR_TERM;
}
uhci->skel_int1_td->link = uhci->skel_ls_control_qh->phy_addr;
// Hack for PIIX
uhci_fill_td(uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
uhci->skel_term_td->link = uhci->skel_term_td->phy_addr;
uhci->skel_term_qh->link = UHCI_PTR_TERM;
uhci->skel_term_qh->element = uhci->skel_term_td->phy_addr;
InsertTailList(&uhci->skel_term_qh->pqhe->vert_link, &uhci->skel_term_td->ptde->vert_link);
/*for( i = 0; i < UHCI_MAX_SKELQHS; i++ )
{
InsertTailList( &uhci->skel_int256_td->ptde->hori_link,
&uhci->skel_qh[ i ]->pqhe->hori_link );
} */
if (uhci_init_frame_list(uhci, uhci->pdev_ext->padapter) == FALSE)
uhci_destroy_frame_list(uhci);
//well all have been chained, now scatter the int tds to frame-list
//shamelessly pasted from linux's uhci.c :-)
for(i = 0; i < UHCI_MAX_FRAMES; i++)
{
irq = 0;
if (i & 1)
{
irq++;
if (i & 2)
{
irq++;
if (i & 4)
{
irq++;
if (i & 8)
{
irq++;
if (i & 16)
{
irq++;
if (i & 32)
{
irq++;
if (i & 64)
irq++;
}
}
}
}
}
}
/* Only place we don't use the frame list routines */
uhci->frame_list[i] = uhci->skel_td[irq]->phy_addr;
}
return TRUE;
}
BOOLEAN
uhci_destroy_schedule(PUHCI_DEV uhci)
{
BOOLEAN ret;
ret = uhci_destroy_frame_list(uhci);
ret = destroy_qh_pool(&uhci->qh_pool);
ret = destroy_td_pool_list(&uhci->td_pool);
return ret;
}
VOID NTAPI
uhci_cancel_pending_endp_urb(IN PVOID Parameter)
{
PLIST_ENTRY abort_list;
PUSB_DEV pdev;
PURB purb;
USE_BASIC_NON_PENDING_IRQL;
abort_list = (PLIST_ENTRY) Parameter;
if (abort_list == NULL)
return;
while (IsListEmpty(abort_list) == FALSE)
{
//these devs are protected by urb's ref-count
purb = (PURB) RemoveHeadList(abort_list);
pdev = purb->pdev;
// purb->status is set when they are added to abort_list
uhci_generic_urb_completion(purb, purb->context);
lock_dev(pdev, FALSE);
pdev->ref_count--;
unlock_dev(pdev, FALSE);
}
usb_free_mem(abort_list);
return;
}
BOOLEAN
uhci_process_pending_endp(PUHCI_DEV uhci)
{
PUSB_DEV pdev;
LIST_ENTRY temp_list, abort_list;
PLIST_ENTRY pthis;
PURB purb;
PUSB_ENDPOINT pendp;
NTSTATUS can_submit = STATUS_UNSUCCESSFUL;
PWORK_QUEUE_ITEM pwork_item;
PLIST_ENTRY cancel_list;
USE_BASIC_IRQL;
if (uhci == NULL)
return FALSE;
InitializeListHead(&temp_list);
InitializeListHead(&abort_list);
purb = NULL;
uhci_dbg_print(DBGLVL_MEDIUM, ("uhci_process_pending_endp(): entering..., uhci=0x%x\n", uhci));
lock_pending_endp_list(&uhci->pending_endp_list_lock);
while (IsListEmpty(&uhci->pending_endp_list) == FALSE)
{
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_process_pending_endp(): pending_endp_list=0x%x\n",
&uhci->pending_endp_list));
pthis = RemoveHeadList(&uhci->pending_endp_list);
pendp = ((PUHCI_PENDING_ENDP) pthis)->pendp;
pdev = dev_from_endp(pendp);
lock_dev(pdev, TRUE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, TRUE);
free_pending_endp(&uhci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
//delegate to uhci_remove_device for removing the urb queue on the endpoint
continue;
}
if (endp_state(pendp) == USB_ENDP_FLAG_STALL)
{
while (IsListEmpty(&pendp->urb_list) == FALSE)
{
purb = (PURB) RemoveHeadList(&pendp->urb_list);
purb->status = USB_STATUS_ENDPOINT_HALTED;
InsertTailList(&abort_list, (LIST_ENTRY *) purb);
}
InitializeListHead(&pendp->urb_list);
unlock_dev(pdev, TRUE);
free_pending_endp(&uhci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
continue;
}
if (IsListEmpty(&pendp->urb_list) == FALSE)
{
purb = (PURB) RemoveHeadList(&pendp->urb_list);
ASSERT(purb);
}
else
{
InitializeListHead(&pendp->urb_list);
unlock_dev(pdev, TRUE);
free_pending_endp(&uhci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
continue;
}
// if can_submit is STATUS_SUCCESS, the purb is inserted into the schedule
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_process_pending_endp(): endp_type=0x%x\n",
endp_type(pendp)));
switch (endp_type(pendp))
{
case USB_ENDPOINT_XFER_BULK:
{
#ifdef DEMO
can_submit = STATUS_UNSUCCESSFUL;
#else
can_submit = uhci_internal_submit_bulk(uhci, purb);
#endif
break;
}
case USB_ENDPOINT_XFER_CONTROL:
{
can_submit = uhci_internal_submit_ctrl(uhci, purb);
break;
}
case USB_ENDPOINT_XFER_INT:
{
can_submit = uhci_internal_submit_int(uhci, purb);
break;
}
case USB_ENDPOINT_XFER_ISOC:
{
can_submit = uhci_internal_submit_iso(uhci, purb);
break;
}
}
if (can_submit == STATUS_NO_MORE_ENTRIES)
{
//no enough bandwidth or tds
InsertHeadList(&pendp->urb_list, &purb->urb_link);
InsertTailList(&temp_list, pthis);
}
else
{
// other error or success
free_pending_endp(&uhci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
if (can_submit != STATUS_SUCCESS)
{
//abort these URBs
InsertTailList(&abort_list, (LIST_ENTRY *) purb);
uhci_dbg_print(DBGLVL_MEDIUM, ("uhci_process_pending_endp(): unable to submit urb 0x%x, "
"with status=0x%x\n", purb, can_submit));
purb->status = can_submit;
}
}
unlock_dev(pdev, TRUE);
}
if (IsListEmpty(&temp_list) == FALSE)
{
//re-append them to the pending_endp_list
ListFirst(&temp_list, pthis);
RemoveEntryList(&temp_list);
MergeList(&uhci->pending_endp_list, pthis);
}
unlock_pending_endp_list(&uhci->pending_endp_list_lock);
if (IsListEmpty(&abort_list) == FALSE)
{
PLIST_ENTRY pthis;
cancel_list = (PLIST_ENTRY) usb_alloc_mem(NonPagedPool, sizeof(WORK_QUEUE_ITEM) + sizeof(LIST_ENTRY));
ASSERT(cancel_list);
ListFirst(&abort_list, pthis);
RemoveEntryList(&abort_list);
InsertTailList(pthis, cancel_list);
pwork_item = (PWORK_QUEUE_ITEM) (cancel_list + 1);
// we do not need to worry the uhci_cancel_pending_endp_urb running when the
// driver is unloading since it will prevent the dev_mgr to quit till all the
// reference count to the dev drop to zero.
ExInitializeWorkItem(pwork_item, uhci_cancel_pending_endp_urb, (PVOID) cancel_list);
ExQueueWorkItem(pwork_item, DelayedWorkQueue);
}
return TRUE;
}
NTSTATUS
uhci_submit_urb(PUHCI_DEV uhci, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
{
int i;
PUHCI_PENDING_ENDP pending_endp;
NTSTATUS status;
USE_BASIC_IRQL;
if (uhci == NULL || pdev == NULL || pendp == NULL || purb == NULL)
{
uhci_dbg_print(DBGLVL_MEDIUM,
("uhci_submit_urb(): uhci=0x%x, pdev=0x%x, pendp=0x%x, purb=0x%x "
"called with invalid param!\n", uhci, pdev, pendp, purb));
return STATUS_INVALID_PARAMETER;
}
lock_pending_endp_list(&uhci->pending_endp_list_lock);
lock_dev(pdev, TRUE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
status = purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
goto LBL_OUT;
}
if (dev_class(pdev) == USB_DEV_CLASS_ROOT_HUB)
{
unlock_dev(pdev, TRUE);
unlock_pending_endp_list(&uhci->pending_endp_list_lock);
status = uhci_rh_submit_urb(pdev, purb);
return status;
}
if (pendp)
purb->pendp = pendp;
else
purb->pendp = &pdev->default_endp;
if (dev_from_endp(purb->pendp) != pdev)
{
uhci_dbg_print(DBGLVL_MEDIUM,
("uhci_submit_urb(): dev_from_endp=0x%x\n, pdev=0x%x, pendp=0x%x "
"devices mismatch!\n", dev_from_endp(purb->pendp), pdev, pendp));
status = purb->status = STATUS_INVALID_PARAMETER;
goto LBL_OUT;
}
if (endp_state(purb->pendp) == USB_ENDP_FLAG_STALL)
{
status = purb->status = USB_STATUS_ENDPOINT_HALTED;
goto LBL_OUT;
}
purb->pdev = pdev;
purb->rest_bytes = purb->data_length;
if (endp_type(purb->pendp) == USB_ENDPOINT_XFER_BULK)
purb->bytes_to_transfer = (purb->data_length > purb->pendp->pusb_endp_desc->wMaxPacketSize * UHCI_MAX_TDS_PER_TRANSFER ? purb->pendp->pusb_endp_desc->wMaxPacketSize * UHCI_MAX_TDS_PER_TRANSFER : purb->data_length); //multiple transfer for large data block
else
purb->bytes_to_transfer = purb->data_length;
uhci_dbg_print(DBGLVL_MEDIUM, ("uhci_submit_urb(): bytes_to_transfer=0x%x\n", purb->bytes_to_transfer));
purb->bytes_transfered = 0;
InitializeListHead(&purb->trasac_list);
purb->last_finished_td = &purb->trasac_list;
purb->flags &= ~(URB_FLAG_STATE_MASK | URB_FLAG_IN_SCHEDULE | URB_FLAG_FORCE_CANCEL);
purb->flags |= URB_FLAG_STATE_PENDING;
i = IsListEmpty(&pendp->urb_list);
InsertTailList(&pendp->urb_list, &purb->urb_link);
pdev->ref_count++; //for urb reference
if (i == FALSE)
{
//there is urb pending, simply queue it and return
status = purb->status = STATUS_PENDING;
goto LBL_OUT;
}
else if (usb_endp_busy_count(purb->pendp) && endp_type(purb->pendp) != USB_ENDPOINT_XFER_ISOC)
{
//
//No urb waiting but urb overlap not allowed,
//so leave it in queue and return, will be scheduled
//later
//
status = purb->status = STATUS_PENDING;
goto LBL_OUT;
}
pending_endp = alloc_pending_endp(&uhci->pending_endp_pool, 1);
if (pending_endp == NULL)
{
//panic
status = purb->status = STATUS_UNSUCCESSFUL;
goto LBL_OUT2;
}
pending_endp->pendp = purb->pendp;
InsertTailList(&uhci->pending_endp_list, &pending_endp->endp_link );
unlock_dev(pdev, TRUE);
unlock_pending_endp_list(&uhci->pending_endp_list_lock);
uhci_process_pending_endp(uhci);
return STATUS_PENDING;
LBL_OUT2:
pdev->ref_count--;
RemoveEntryList(&purb->urb_link);
LBL_OUT:
unlock_dev(pdev, TRUE);
unlock_pending_endp_list(&uhci->pending_endp_list_lock);
return status;
}
NTSTATUS
uhci_set_error_code(PURB urb, ULONG raw_status)
{
if ((raw_status & TD_CTRL_ANY_ERROR) == 0)
{
//test if the urb is canceled
if (urb->flags & URB_FLAG_FORCE_CANCEL)
urb->status = STATUS_CANCELLED;
else
urb->status = STATUS_SUCCESS;
}
else if (raw_status & TD_CTRL_BABBLE)
urb->status = USB_STATUS_DATA_OVERRUN;
else if (raw_status & TD_CTRL_STALLED)
urb->status = USB_STATUS_STALL_PID;
else if (raw_status & TD_CTRL_DBUFERR)
urb->status = USB_STATUS_BUFFER_OVERRUN;
else if (raw_status & TD_CTRL_CRCTIMEO)
urb->status = USB_STATUS_CRC;
else if (raw_status & TD_CTRL_BITSTUFF)
urb->status = USB_STATUS_BTSTUFF;
else
urb->status = STATUS_UNSUCCESSFUL;
return urb->status;
}
BOOLEAN NTAPI
uhci_sync_remove_urb_finished(PVOID context)
{
PUHCI_DEV uhci;
PLIST_ENTRY pthis, pnext, ptemp;
PURB purb;
PSYNC_PARAM pparam;
pparam = (PSYNC_PARAM) context;
uhci = pparam->uhci;
ptemp = (PLIST_ENTRY) pparam->context;
if (uhci == NULL)
{
return (UCHAR) (pparam->ret = FALSE);
}
ListFirst(&uhci->urb_list, pthis);
while (pthis)
{
//remove urbs not in the schedule
ListNext(&uhci->urb_list, pthis, pnext);
purb = (PURB) pthis;
if ((purb->flags & URB_FLAG_IN_SCHEDULE) == 0)
{
//finished or canceled( not apply for split bulk ).
purb->flags &= ~URB_FLAG_STATE_MASK;
purb->flags |= URB_FLAG_STATE_FINISHED;
RemoveEntryList(pthis);
InsertTailList(ptemp, pthis);
}
pthis = pnext;
}
pparam->ret = TRUE;
return (UCHAR) TRUE;
}
BOOLEAN
uhci_drop_fsbr(PUHCI_DEV uhci)
{
if (uhci == NULL)
return (UCHAR) FALSE;
uhci->fsbr_cnt--;
if (uhci->fsbr_cnt <= 0)
{
uhci->skel_term_qh->link = UHCI_PTR_TERM;
uhci->fsbr_cnt = 0;
}
return (UCHAR) TRUE;
}
VOID NTAPI
uhci_dpc_callback(PKDPC dpc, PVOID context, PVOID sysarg1, PVOID sysarg2)
{
PUHCI_DEV uhci;
LIST_HEAD temp_list;
PLIST_ENTRY pthis, pnext;
PURB purb;
PQH_EXTENSION pqhe;
PUHCI_PENDING_ENDP pending_endp;
PUSB_DEV pdev;
PUSB_ENDPOINT pendp;
BOOLEAN finished;
LONG i, j;
ULONG uhci_status, urb_status, toggle = 0;
SYNC_PARAM sync_param;
USE_BASIC_NON_PENDING_IRQL;
UNREFERENCED_PARAMETER(dpc);
UNREFERENCED_PARAMETER(sysarg2);
uhci = (PUHCI_DEV) context;
if (uhci == NULL)
return;
uhci_status = (ULONG) sysarg1;
InitializeListHead(&temp_list);
sync_param.uhci = uhci;
sync_param.context = (PVOID) & temp_list;
uhci_dbg_print(DBGLVL_MAXIMUM, ("uhci_dpc_callback(): entering..., uhci=0x%x\n", uhci));
//remove finished urb from uhci's urb-list
KeSynchronizeExecution(uhci->pdev_ext->uhci_int, uhci_sync_remove_urb_finished, &sync_param);
//release resources( tds, and qhs ) the urb occupied
while (IsListEmpty(&temp_list) == FALSE)
{
//not in any public queue, if do not access into dev, no race
//condition will occur
purb = (PURB) RemoveHeadList(&temp_list);
urb_status = purb->status;
//the only place we do not use this lock on non-pending-endp-list data ops
KeAcquireSpinLockAtDpcLevel(&uhci->pending_endp_list_lock);
while (IsListEmpty(&purb->trasac_list) == FALSE)
{
pthis = RemoveHeadList(&purb->trasac_list);
if ((((PTD_EXTENSION) pthis)->flags & UHCI_ITEM_FLAG_TYPE) == UHCI_ITEM_FLAG_QH)
{
pqhe = (PQH_EXTENSION) pthis;
lock_qh_pool(&uhci->qh_pool, TRUE);
free_qh(&uhci->qh_pool, pqhe->pqh);
unlock_qh_pool(&uhci->qh_pool, TRUE);
}
else
{
//must be a td chain
InsertHeadList(&purb->trasac_list, pthis);
for(i = 0, purb->bytes_transfered = 0; i < purb->td_count; i++)
{
PUHCI_TD ptd;
// accumulate data transfered in tds
ptd = ((PTD_EXTENSION) pthis)->ptd;
if ((ptd->status & TD_CTRL_ACTIVE) == 0 && (ptd->status & TD_CTRL_ANY_ERROR) == 0)
{
j = ptd->status & 0x7ff;
purb->bytes_transfered += ((j == 0x7ff) ? 0 : (j + 1));
}
ListNext(&purb->trasac_list, pthis, pnext);
pthis = pnext;
}
if (urb_status & TD_CTRL_ANY_ERROR)
{
if (purb->last_finished_td != NULL && purb->last_finished_td != &purb->trasac_list)
toggle = (((PTD_EXTENSION) purb->last_finished_td)->ptd->info & (1 << 19));
}
//trick, remove trasac_list
ListFirst(&purb->trasac_list, pthis);
RemoveEntryList(&purb->trasac_list);
lock_td_pool(&uhci->td_pool, TRUE);
free_tds(&uhci->td_pool, ((PTD_EXTENSION) pthis)->ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
//termination condition
InitializeListHead(&purb->trasac_list);
purb->last_finished_td = NULL;
}
}
if (endp_type(purb->pendp) == USB_ENDPOINT_XFER_ISOC
|| endp_type(purb->pendp) == USB_ENDPOINT_XFER_INT)
uhci_claim_bandwidth(uhci, purb, FALSE); //release band-width
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
uhci_set_error_code(purb, urb_status);
finished = TRUE;
//since the ref_count for the urb is not released, we can safely have one
//pointer to dev
pdev = dev_from_endp(purb->pendp);
pendp = purb->pendp;
if (purb->status == USB_STATUS_BABBLE_DETECTED)
{
usb_dbg_print(DBGLVL_MEDIUM,
("uhci_dpc_callback(): alert!!!, babble detected, severe error, reset the whole bus\n"));
uhci_reset(uhci);
uhci_start(&uhci->hcd_interf);
}
//this will let the new request in uhci_generic_urb_completion to this endp
//be processed rather than queued in the pending_endp_list
lock_dev(pdev, TRUE);
usb_endp_busy_count_dec(pendp);
unlock_dev(pdev, TRUE);
if (usb_success(purb->status) == FALSE)
{
// set error code and complete the urb and purb is invalid from this point
uhci_generic_urb_completion(purb, purb->context);
}
else
{
if ((purb->pipe & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
{
purb->rest_bytes -= purb->bytes_transfered;
if (purb->rest_bytes)
{
finished = FALSE;
}
else
{
uhci_generic_urb_completion(purb, purb->context);
}
}
else
{
uhci_generic_urb_completion(purb, purb->context);
//purb is now invalid
}
}
KeAcquireSpinLockAtDpcLevel(&uhci->pending_endp_list_lock);
lock_dev(pdev, TRUE);
if (finished)
pdev->ref_count--;
if (urb_status & TD_CTRL_ANY_ERROR && endp_type(pendp) != USB_ENDPOINT_XFER_CONTROL)
{
pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK;
pendp->flags |= USB_ENDP_FLAG_STALL;
}
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
if (finished == FALSE)
{
purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
uhci_generic_urb_completion(purb, purb->context);
lock_dev(pdev, TRUE);
pdev->ref_count--;
unlock_dev(pdev, TRUE);
}
continue;
}
if (finished && IsListEmpty(&pendp->urb_list) == TRUE)
{
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
continue;
}
else if (finished == TRUE)
{
//has urb in the endp's urb-list
if (usb_endp_busy_count(pendp) > 0)
{
//the urbs still have chance to be sheduled but not this time
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
continue;
}
}
if (finished == FALSE)
{
//a split bulk transfer
purb->bytes_transfered = 0;
purb->bytes_to_transfer =
UHCI_MAX_TDS_PER_TRANSFER * purb->pendp->pusb_endp_desc->wMaxPacketSize
> purb->rest_bytes
? purb->rest_bytes : UHCI_MAX_TDS_PER_TRANSFER * purb->pendp->pusb_endp_desc->wMaxPacketSize;
//the urb is not finished
purb->flags &= ~URB_FLAG_STATE_MASK;
purb->flags |= URB_FLAG_STATE_PENDING;
InsertHeadList(&pendp->urb_list, &purb->urb_link);
}
pending_endp = alloc_pending_endp(&uhci->pending_endp_pool, 1);
if (!pending_endp)
{
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
return;
}
pending_endp->pendp = pendp;
InsertTailList(&uhci->pending_endp_list, &pending_endp->endp_link);
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&uhci->pending_endp_list_lock);
}
//ah...exhausted, let's find some in the pending_endp_list to rock
uhci_process_pending_endp(uhci);
return;
}
BOOLEAN
uhci_add_device(PUHCI_DEV uhci, PUSB_DEV dev)
{
if (dev == NULL || uhci == NULL)
return FALSE;
return TRUE;
}
BOOLEAN NTAPI
uhci_sync_cancel_urbs_dev(PVOID context)
{
//cancel all the urbs on one dev
PUHCI_DEV uhci;
PUSB_DEV pdev, dest_dev;
PSYNC_PARAM sync_param;
PLIST_ENTRY pthis, pnext;
LONG count;
sync_param = (PSYNC_PARAM) context;
dest_dev = (PUSB_DEV) sync_param->context;
uhci = sync_param->uhci;
if (uhci == NULL || dest_dev == NULL)
{
return (UCHAR) (sync_param->ret = FALSE);
}
count = 0;
ListFirst(&uhci->urb_list, pthis);
while (pthis)
{
pdev = dev_from_endp(((PURB) pthis)->pendp);
if (pdev == dest_dev)
{
((PURB) pthis)->flags |= URB_FLAG_FORCE_CANCEL;
}
ListNext(&uhci->urb_list, pthis, pnext);
pthis = pnext;
count++;
}
if (count)
uhci->skel_term_td->status |= TD_CTRL_IOC;
return (UCHAR) (sync_param->ret = TRUE);
}
BOOLEAN
uhci_remove_device(PUHCI_DEV uhci, PUSB_DEV dev)
{
PUHCI_PENDING_ENDP ppending_endp;
PLIST_ENTRY pthis, pnext;
PURB purb;
LIST_HEAD temp_list;
int i, j, k;
SYNC_PARAM sync_param;
USE_BASIC_IRQL;
if (uhci == NULL || dev == NULL)
return FALSE;
InitializeListHead(&temp_list);
//free pending endp that has urb queued from pending endp list
lock_pending_endp_list(&uhci->pending_endp_list_lock);
ListFirst(&uhci->pending_endp_list, pthis);
while (pthis)
{
ppending_endp = (PUHCI_PENDING_ENDP) pthis;
ListNext(&uhci->pending_endp_list, pthis, pnext);
if (dev_from_endp(ppending_endp->pendp) == dev)
{
RemoveEntryList(pthis);
free_pending_endp(&uhci->pending_endp_pool, struct_ptr(pthis, UHCI_PENDING_ENDP, endp_link));
}
pthis = pnext;
}
unlock_pending_endp_list(&uhci->pending_endp_list_lock);
//cancel all the urbs in the urb-list
sync_param.uhci = uhci;
sync_param.context = (PVOID) dev;
KeSynchronizeExecution(uhci->pdev_ext->uhci_int, uhci_sync_cancel_urbs_dev, &sync_param);
//cancel all the urb in the endp's urb-list
k = 0;
lock_dev(dev, FALSE);
if (dev->usb_config)
{
//only for configed dev
for(i = 0; i < dev->usb_config->if_count; i++)
{
for(j = 0; j < dev->usb_config->interf[i].endp_count; j++)
{
ListFirst(&dev->usb_config->interf[i].endp[j].urb_list, pthis);
while (pthis)
{
ListNext(&dev->usb_config->interf[i].endp[j].urb_list, pthis, pnext);
RemoveEntryList(pthis);
InsertHeadList(&temp_list, pthis);
pthis = pnext;
k++;
}
}
}
}
ListFirst(&dev->default_endp.urb_list, pthis);
while (pthis)
{
ListNext(&dev->default_endp.urb_list, pthis, pnext);
RemoveEntryList(pthis);
InsertHeadList(&temp_list, pthis);
pthis = pnext;
k++;
}
unlock_dev(dev, FALSE);
if (IsListEmpty(&temp_list) == FALSE)
{
for(i = 0; i < k; i++)
{
//complete those urbs with error
pthis = RemoveHeadList(&temp_list);
purb = (PURB) pthis;
purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
{
uhci_generic_urb_completion(purb, purb->context);
}
}
}
lock_dev(dev, FALSE) dev->ref_count -= k;
unlock_dev(dev, FALSE);
return TRUE;
}
//
// assume that the urb has its rest_bytes and bytes_to_transfer set
// and bytes_transfered is zeroed.
// dev_lock must be acquired outside
// urb comes from dev's endpoint urb-list. it is already removed from
// the endpoint urb-list.
//
NTSTATUS
uhci_internal_submit_bulk(PUHCI_DEV uhci, PURB urb)
{
LONG max_packet_size, td_count, offset, bytes_to_transfer, data_load;
PBYTE start_addr;
PUHCI_TD ptd;
PUHCI_QH pqh;
LIST_ENTRY td_list, *pthis, *pnext;
BOOLEAN old_toggle, toggle, ret;
UCHAR pid;
if (uhci == NULL || urb == NULL)
return STATUS_INVALID_PARAMETER;
max_packet_size = endp_max_packet_size(urb->pendp);
if (urb->bytes_to_transfer == 0)
{
return STATUS_INVALID_PARAMETER;
}
td_count = (urb->bytes_to_transfer + max_packet_size - 1) / max_packet_size;
lock_td_pool(&uhci->td_pool, TRUE);
if (can_transfer(&uhci->td_pool, td_count) == FALSE)
{
unlock_td_pool(&uhci->td_pool, TRUE);
return STATUS_NO_MORE_ENTRIES;
}
ptd = alloc_tds(&uhci->td_pool, td_count);
unlock_td_pool(&uhci->td_pool, TRUE);
if (ptd == NULL)
{
return STATUS_UNSUCCESSFUL;
}
InitializeListHead(&td_list);
InsertTailList(&ptd->ptde->vert_link, &td_list);
ListFirst(&td_list, pthis);
ListNext(&td_list, pthis, pnext);
start_addr = &urb->data_buffer[urb->data_length - urb->rest_bytes];
offset = 0;
old_toggle = toggle = urb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ? TRUE : FALSE;
bytes_to_transfer = urb->bytes_to_transfer;
urb->pipe = ((max_packet_size - 1) << 21)
| ((ULONG) endp_num(urb->pendp) << 15)
| (dev_from_endp(urb->pendp)->dev_addr << 8)
| ((ULONG) endp_dir(urb->pendp)) | USB_ENDPOINT_XFER_BULK;
pid = (((ULONG) urb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT);
while (pthis)
{
ptd = ((PTD_EXTENSION) pthis)->ptd;
data_load = max_packet_size < bytes_to_transfer ? max_packet_size : bytes_to_transfer;
ptd->purb = urb;
uhci_fill_td(ptd,
(3 << TD_CTRL_C_ERR_SHIFT)
| (TD_CTRL_ACTIVE),
((data_load - 1) << 21)
| (toggle << 19)
| ((ULONG) endp_num(urb->pendp) << 15)
| (dev_from_endp(urb->pendp)->dev_addr << 8)
| pid, MmGetPhysicalAddress(start_addr + offset).LowPart);
bytes_to_transfer -= data_load;
offset += data_load;
if (pnext)
{
ptd->link = ((PTD_EXTENSION) pnext)->ptd->phy_addr;
}
else
{
//Last one, enable ioc and short packet detect if necessary
ptd->link = UHCI_PTR_TERM;
ptd->status |= TD_CTRL_IOC;
if (bytes_to_transfer < max_packet_size && (pid == USB_PID_IN))
{
//ptd->status |= TD_CTRL_SPD;
}
}
pthis = pnext;
toggle ^= 1;
if (pthis)
ListNext(&td_list, pthis, pnext);
}
ListFirst(&td_list, pthis);
RemoveEntryList(&td_list);
lock_qh_pool(&uhci->qh_pool, TRUE);
pqh = alloc_qh(&uhci->qh_pool);
unlock_qh_pool(&uhci->qh_pool, TRUE);
if (pqh == NULL)
{
lock_td_pool(&uhci->td_pool, TRUE);
if (pthis)
free_tds(&uhci->td_pool, ((PTD_EXTENSION) pthis)->ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
return STATUS_NO_MORE_ENTRIES;
}
urb->td_count = td_count;
uhci_insert_tds_qh(pqh, ((PTD_EXTENSION) pthis)->ptd);
uhci_insert_qh_urb(urb, pqh);
urb->pendp->flags =
(urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE) | (toggle ? USB_ENDP_FLAG_DATATOGGLE : 0);
usb_endp_busy_count_inc(urb->pendp);
uhci_insert_urb_to_schedule(uhci, urb, ret);
if (ret == FALSE)
{
// undo all we have done
RemoveEntryList(&pqh->pqhe->vert_link); //remove qh from td_chain
RemoveEntryList(&urb->trasac_list);
lock_td_pool(&uhci->td_pool, TRUE);
if (pthis)
free_tds(&uhci->td_pool, ((PTD_EXTENSION) pthis)->ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
lock_qh_pool(&uhci->qh_pool, TRUE);
if (pqh)
free_qh(&uhci->qh_pool, pqh);
unlock_qh_pool(&uhci->qh_pool, TRUE);
InitializeListHead(&urb->trasac_list);
usb_endp_busy_count_dec(urb->pendp);
urb->pendp->flags =
(urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE) | (old_toggle ? USB_ENDP_FLAG_DATATOGGLE : 0);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
NTSTATUS
uhci_internal_submit_ctrl(PUHCI_DEV uhci, PURB urb)
{
LIST_ENTRY td_list, *pthis, *pnext;
LONG i, td_count;
LONG toggle;
LONG max_packet_size, bytes_to_transfer, bytes_rest, start_idx;
PUHCI_TD ptd;
PUHCI_QH pqh;
ULONG dev_addr;
PUSB_DEV pdev;
BOOLEAN ret;
if (uhci == NULL || urb == NULL)
return STATUS_INVALID_PARAMETER;
toggle = 0;
bytes_rest = urb->rest_bytes;
bytes_to_transfer = urb->bytes_to_transfer;
max_packet_size = endp_max_packet_size(urb->pendp);
start_idx = urb->data_length - urb->rest_bytes;
td_count = 2 + (urb->bytes_to_transfer + max_packet_size - 1) / max_packet_size;
lock_td_pool(&uhci->td_pool, TRUE);
if (can_transfer(&uhci->td_pool, td_count) == FALSE)
{
unlock_td_pool(&uhci->td_pool, TRUE);
return STATUS_NO_MORE_ENTRIES;
}
ptd = alloc_tds(&uhci->td_pool, td_count);
unlock_td_pool(&uhci->td_pool, TRUE);
if (ptd == NULL)
{
return STATUS_UNSUCCESSFUL;
}
InsertTailList(&ptd->ptde->vert_link, &td_list);
ListFirst(&td_list, pthis);
ListNext(&td_list, pthis, pnext);
ptd = ((PTD_EXTENSION) pthis)->ptd;
pdev = dev_from_endp(urb->pendp);
dev_addr = pdev->dev_addr;
if (dev_state(pdev) <= USB_DEV_STATE_RESET)
dev_addr = 0;
usb_dbg_print(DBGLVL_MAXIMUM, ("uhci_internal_submit_ctrl(): dev_addr =0x%x\n", dev_addr));
RtlCopyMemory(uhci->io_buf, urb->setup_packet, 8);
if ((urb->setup_packet[0] & USB_DIR_IN) == 0) //out
RtlCopyMemory(&uhci->io_buf[8], urb->data_buffer, bytes_to_transfer);
else
RtlZeroMemory(&uhci->io_buf[8], bytes_to_transfer);
uhci_fill_td(ptd,
(3 << TD_CTRL_C_ERR_SHIFT) | (TD_CTRL_ACTIVE),
(7 << 21) | (((ULONG) endp_num(urb->pendp)) << 15) | (dev_addr << 8) | (USB_PID_SETUP),
//uhci->io_buf_logic_addr.LowPart);
MmGetPhysicalAddress(urb->setup_packet).LowPart);
ptd->link = ((PTD_EXTENSION) pnext)->ptd->phy_addr;
pthis = pnext;
ListNext(&td_list, pthis, pnext);
urb->pipe = ((max_packet_size - 1) << 21)
| ((ULONG) endp_num(urb->pendp) << 15)
| (dev_addr << 8) | (pdev->flags & USB_DEV_FLAG_LOW_SPEED) | USB_ENDPOINT_XFER_CONTROL;
for(i = 0, toggle = 1; ((i < td_count - 2) && pthis); i++, toggle ^= 1)
{
//construct tds for DATA packets of data stage.
ptd = ((PTD_EXTENSION) pthis)->ptd;
uhci_fill_td(ptd,
(3 << TD_CTRL_C_ERR_SHIFT)
| (TD_CTRL_ACTIVE),
((bytes_to_transfer >
max_packet_size ? max_packet_size - 1 : bytes_to_transfer -
1) << 21) | (toggle << 19) | (((ULONG) endp_num(urb->
pendp)) << 15) | (dev_addr << 8) |
((urb->setup_packet[0] & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT),
//uhci->io_buf_logic_addr.LowPart + 8 + i * max_packet_size );
MmGetPhysicalAddress(&urb->data_buffer[start_idx + i * max_packet_size]).LowPart);
if (pnext)
ptd->link = ((PTD_EXTENSION) pnext)->ptd->phy_addr;
if (i < td_count - 3)
{
bytes_to_transfer -= max_packet_size;
}
else
{
if (bytes_to_transfer > 0)
{
if (bytes_to_transfer < max_packet_size && (urb->setup_packet[0] & USB_DIR_IN))
ptd->status |= TD_CTRL_SPD;
}
}
pthis = pnext;
if (pthis)
ListNext(&td_list, pthis, pnext);
}
if (pnext)
ptd->link = ((PTD_EXTENSION) pnext)->ptd->phy_addr;
ListFirstPrev(&td_list, pthis);
ptd = ((PTD_EXTENSION) pthis)->ptd;
//the last is an IN transaction
uhci_fill_td(ptd,
(3 << TD_CTRL_C_ERR_SHIFT)
| (TD_CTRL_ACTIVE | TD_CTRL_IOC),
(UHCI_NULL_DATA_SIZE << 21)
| (1 << 19)
| (((ULONG) endp_num(urb->pendp)) << 15)
| (dev_addr << 8)
| ((td_count > 2)
? ((urb->setup_packet[0] & USB_DIR_IN) ? USB_PID_OUT : USB_PID_IN) : USB_PID_IN), 0);
ptd->link = UHCI_PTR_TERM;
ListFirst(&td_list, pthis);
RemoveEntryList(&td_list);
lock_qh_pool(&uhci->qh_pool, TRUE);
pqh = alloc_qh(&uhci->qh_pool);
unlock_qh_pool(&uhci->qh_pool, TRUE);
if (pqh == NULL)
{
lock_td_pool(&uhci->td_pool, TRUE);
if (pthis)
free_tds(&uhci->td_pool, ((PTD_EXTENSION) pthis)->ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
return STATUS_NO_MORE_ENTRIES;
}
urb->td_count = td_count;
uhci_insert_tds_qh(pqh, ((PTD_EXTENSION) pthis)->ptd);
uhci_insert_qh_urb(urb, pqh);
usb_endp_busy_count_inc(urb->pendp);
uhci_insert_urb_to_schedule(uhci, urb, ret);
if (ret == FALSE)
{
RemoveEntryList(&pqh->pqhe->vert_link);
RemoveEntryList(&urb->trasac_list);
lock_td_pool(&uhci->td_pool, TRUE);
if (pthis)
free_tds(&uhci->td_pool, ((PTD_EXTENSION) pthis)->ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
lock_qh_pool(&uhci->qh_pool, TRUE);
if (pqh)
free_qh(&uhci->qh_pool, pqh);
unlock_qh_pool(&uhci->qh_pool, TRUE);
InitializeListHead(&urb->trasac_list);
usb_endp_busy_count_dec(urb->pendp);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
NTSTATUS
uhci_internal_submit_int(PUHCI_DEV uhci, PURB urb)
{
LONG i;
LONG toggle = 0;
LONG max_packet_size;
PUHCI_TD ptd;
BOOLEAN ret;
if (uhci == NULL || urb == NULL)
{
uhci_dbg_print(DBGLVL_MEDIUM,
("uhci_internal_submit_int(): uhci=0x%x, urb=0x%x "
"returning STATUS_INVALID_PARAMETER!\n", uhci, urb));
return STATUS_INVALID_PARAMETER;
}
toggle = (urb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE) ? TRUE : FALSE;
max_packet_size = endp_max_packet_size(urb->pendp);
if (max_packet_size < urb->data_length || max_packet_size == 0 || max_packet_size > 64)
{
uhci_dbg_print(DBGLVL_MEDIUM,
("uhci_internal_submit_int(): max_packet_size=%d, urb->data_length=%d "
"returning STATUS_INVALID_PARAMETER!\n", max_packet_size, urb->data_length));
return STATUS_INVALID_PARAMETER;
}
lock_td_pool(&uhci->td_pool, TRUE);
ptd = alloc_td(&uhci->td_pool);
unlock_td_pool(&uhci->td_pool, TRUE);
if (ptd == NULL)
return STATUS_NO_MORE_ENTRIES;
for(i = 1; i <= 7; i++)
{
if (((ULONG) max_packet_size) >> i)
continue;
else
break;
}
i--;
i &= 7;
urb->pipe = (((ULONG) urb->pendp->pusb_endp_desc->bInterval) << 24)
| (i << 21)
| (toggle << 19)
| ((ULONG) endp_num(urb->pendp) << 15)
| (((ULONG) dev_from_endp(urb->pendp)->dev_addr) << 8)
| USB_DIR_IN | (dev_from_endp(urb->pendp)->flags & USB_DEV_FLAG_LOW_SPEED) | USB_ENDPOINT_XFER_INT;
uhci_fill_td(ptd,
(3 << TD_CTRL_C_ERR_SHIFT)
| (TD_CTRL_ACTIVE)
| ((urb->data_length < max_packet_size ? TD_CTRL_SPD : 0))
| (TD_CTRL_IOC),
(((ULONG) max_packet_size - 1) << 21)
| (toggle << 19)
| ((ULONG) endp_num(urb->pendp) << 15)
| (((ULONG) dev_from_endp(urb->pendp)->dev_addr & 0x7f) << 8)
| USB_PID_IN, MmGetPhysicalAddress(urb->data_buffer).LowPart);
toggle ^= 1;
urb->td_count = 1;
InitializeListHead(&urb->trasac_list);
InsertTailList(&urb->trasac_list, &ptd->ptde->vert_link);
//indirectly guarded by pending_endp_list_lock
if (uhci_claim_bandwidth(uhci, urb, TRUE) == FALSE)
{
InitializeListHead(&urb->trasac_list);
lock_td_pool(&uhci->td_pool, TRUE);
free_td(&uhci->td_pool, ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
return STATUS_NO_MORE_ENTRIES;
}
urb->pendp->flags = (urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE) | (toggle << 31);
usb_endp_busy_count_inc(urb->pendp);
uhci_insert_urb_to_schedule(uhci, urb, ret);
if (ret == FALSE)
{
lock_td_pool(&uhci->td_pool, TRUE);
if (ptd)
free_td(&uhci->td_pool, ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
InitializeListHead(&urb->trasac_list);
usb_endp_busy_count_dec(urb->pendp);
urb->pendp->flags = (urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE) | ((toggle ^ 1) << 31);
uhci_claim_bandwidth(uhci, urb, FALSE);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
NTSTATUS
uhci_internal_submit_iso(PUHCI_DEV uhci, PURB urb)
{
PUHCI_TD ptd;
LIST_ENTRY td_list, *pthis, *pnext;
int i;
BOOLEAN toggle = FALSE, ret;
if (uhci == NULL || urb == NULL)
return STATUS_INVALID_PARAMETER;
if (urb->iso_frame_count == 0)
return STATUS_INVALID_PARAMETER;
lock_td_pool(&uhci->td_pool, TRUE);
if (can_transfer(&uhci->td_pool, urb->iso_frame_count) == FALSE)
{
unlock_td_pool(&uhci->td_pool, TRUE);
return STATUS_NO_MORE_ENTRIES;
}
ptd = alloc_tds(&uhci->td_pool, urb->iso_frame_count);
unlock_td_pool(&uhci->td_pool, TRUE);
if (ptd == NULL)
{
return STATUS_UNSUCCESSFUL;
}
InsertTailList(&ptd->ptde->vert_link, &td_list);
ListFirst(&td_list, pthis);
urb->td_count = urb->iso_frame_count;
urb->pipe = (((ULONG) urb->iso_packet_desc[0].length) << 21)
| ((ULONG) endp_num(urb->pendp) << 15)
| (((ULONG) dev_from_endp(urb->pendp)->dev_addr) << 8)
| ((ULONG) endp_dir(urb->pendp)) | USB_ENDPOINT_XFER_ISOC;
for(i = 0; i < urb->iso_frame_count && pthis; i++)
{
ptd = ((PTD_EXTENSION) pthis)->ptd;
uhci_fill_td(ptd,
(3 << TD_CTRL_C_ERR_SHIFT)
| (TD_CTRL_ACTIVE)
| (TD_CTRL_IOS),
(((ULONG) urb->iso_packet_desc[i].length - 1) << 21)
| (0 << 19)
| ((ULONG) endp_num(urb->pendp) << 15)
| (((ULONG) dev_from_endp(urb->pendp)->dev_addr) << 8)
| ((urb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN)
? USB_PID_OUT : USB_PID_IN),
MmGetPhysicalAddress(&urb->data_buffer[urb->iso_packet_desc[i].offset]).LowPart);
toggle ^= 1;
ListNext(&td_list, pthis, pnext);
pthis = pnext;
}
ptd->status |= TD_CTRL_IOC; //need interrupt
ListFirst(&td_list, pthis);
RemoveEntryList(&td_list);
InsertTailList(pthis, &urb->trasac_list);
//indirectly guarded by pending_endp_list_lock
if (uhci_claim_bandwidth(uhci, urb, TRUE) == FALSE)
{
//bad news: we can not allocate the enough bandwidth for the urb
RemoveEntryList(&urb->trasac_list);
InitializeListHead(&urb->trasac_list);
lock_td_pool(&uhci->td_pool, TRUE);
free_tds(&uhci->td_pool, ((PTD_EXTENSION) pthis)->ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
return STATUS_NO_MORE_ENTRIES;
}
usb_endp_busy_count_inc(urb->pendp);
uhci_insert_urb_to_schedule(uhci, urb, ret);
if (ret == FALSE)
{
usb_endp_busy_count_dec(urb->pendp);
RemoveEntryList(&urb->trasac_list);
lock_td_pool(&uhci->td_pool, TRUE);
free_tds(&uhci->td_pool, ((PTD_EXTENSION) pthis)->ptd);
unlock_td_pool(&uhci->td_pool, TRUE);
uhci_claim_bandwidth(uhci, urb, FALSE);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
// runs in uhci_isr
BOOLEAN
uhci_is_xfer_finished(PURB urb)
{
PLIST_ENTRY pthis, pnext;
PUHCI_TD ptd;
BOOLEAN ret = TRUE;
PTD_EXTENSION ptde;
if (urb->last_finished_td == NULL)
{
urb->last_finished_td = &urb->trasac_list;
}
if (&urb->trasac_list == urb->last_finished_td)
ListFirst(&urb->trasac_list, pthis)
else
ListNext(&urb->trasac_list, urb->last_finished_td, pthis);
while (pthis)
{
if ((((PTD_EXTENSION) pthis)->flags & UHCI_ITEM_FLAG_TYPE) != UHCI_ITEM_FLAG_TD)
{
ListNext(&urb->trasac_list, pthis, pnext);
pthis = pnext;
continue;
}
else
{
ptde = (PTD_EXTENSION) pthis;
ptd = ptde->ptd;
ASSERT(ptd != NULL);
if (ptd->status & TD_CTRL_ACTIVE)
{
//still active
ret = FALSE;
break;
}
//let's see whether error occured
if ((ptd->status & TD_CTRL_ANY_ERROR) == 0)
{
urb->last_finished_td = pthis;
ListNext(&urb->trasac_list, pthis, pnext);
pthis = pnext;
continue;
}
else
{
urb->status = ptd->status;
pthis = NULL;
continue;
}
}
}
if (pthis == NULL)
ret = TRUE;
return ret;
}
// executed in isr, and have frame_list_lock acquired, so
// never try to acquire any spin-lock
// remove the bulk urb from schedule, and mark it not in
// the schedule
BOOLEAN
uhci_remove_urb_from_schedule(PUHCI_DEV uhci, PURB urb)
{
BOOLEAN ret = FALSE;
{
switch (urb->pipe & USB_ENDPOINT_XFERTYPE_MASK)
{
case USB_ENDPOINT_XFER_BULK:
{
ret = uhci_remove_bulk_from_schedule(uhci, urb);
break;
}
case USB_ENDPOINT_XFER_CONTROL:
{
ret = uhci_remove_ctrl_from_schedule(uhci, urb);
break;
}
case USB_ENDPOINT_XFER_INT:
{
ret = uhci_remove_int_from_schedule(uhci, urb);
break;
}
case USB_ENDPOINT_XFER_ISOC:
{
ret = uhci_remove_iso_from_schedule(uhci, urb);
break;
}
}
}
return ret;
}
// executed in isr, and have frame_list_lock acquired, so
// never try to acquire any spin-lock
// remove the bulk urb from schedule, and mark it not in
// the schedule
BOOLEAN
uhci_remove_bulk_from_schedule(PUHCI_DEV uhci, PURB urb)
{
PUHCI_QH pqh, pnext_qh, pprev_qh;
PLIST_ENTRY pthis, pnext, pprev;
LONG i;
if (uhci == NULL || urb == NULL)
return FALSE;
ListFirst(&urb->trasac_list, pthis);
pqh = ((PQH_EXTENSION) pthis)->pqh;
ListFirst(&pqh->pqhe->hori_link, pnext);
ListFirstPrev(&pqh->pqhe->hori_link, pprev);
if (pprev == NULL || pnext == NULL)
return FALSE;
pnext_qh = struct_ptr(pnext, QH_EXTENSION, hori_link)->pqh;
pprev_qh = struct_ptr(pprev, QH_EXTENSION, hori_link)->pqh;
if (pprev != pnext)
{
//not the last one
pprev_qh->link = pnext_qh->phy_addr;
}
else
{
//only two qhs in the list
for(i = 0; i < UHCI_MAX_SKELQHS; i++)
{
if (pprev_qh == uhci->skel_qh[i])
{
break;
}
}
ASSERT(i < UHCI_MAX_SKELQHS - 1);
pprev_qh->link = uhci->skel_qh[i + 1]->phy_addr;
}
RemoveEntryList(&pqh->pqhe->hori_link);
urb->flags &= ~URB_FLAG_IN_SCHEDULE;
if ((urb->pipe & USB_DEV_FLAG_LOW_SPEED) == 0)
uhci_drop_fsbr(uhci);
return TRUE;
}
BOOLEAN
uhci_remove_iso_from_schedule(PUHCI_DEV uhci, PURB urb)
{
PUHCI_TD ptd, pprev_td;
PLIST_ENTRY pthis, pnext, pprev;
int i, idx;
if (uhci == NULL || urb == NULL)
return FALSE;
ListFirst(&urb->trasac_list, pthis);
for(i = 0; i < urb->iso_frame_count && pthis; i++)
{
ptd = ((PTD_EXTENSION) pthis)->ptd;
idx = (urb->iso_start_frame + i) & (UHCI_MAX_FRAMES - 1);
ListFirstPrev(&ptd->ptde->hori_link, pprev);
if (pprev == NULL)
return FALSE;
if (pprev == &uhci->frame_list_cpu[idx].td_link)
{
uhci->frame_list[idx] = ptd->link;
}
else
{
pprev_td = struct_ptr(pprev, TD_EXTENSION, hori_link)->ptd;
pprev_td->link = ptd->link;
}
RemoveEntryList(&ptd->ptde->hori_link);
ListNext(&urb->trasac_list, pthis, pnext);
pthis = pnext;
}
urb->flags &= ~URB_FLAG_IN_SCHEDULE;
return TRUE;
}
BOOLEAN
uhci_remove_int_from_schedule(PUHCI_DEV uhci, PURB urb)
{
PUHCI_TD ptd, pnext_td, pprev_td;
PLIST_ENTRY pthis, pnext, pprev;
LONG i;
if (uhci == NULL || urb == NULL)
return FALSE;
ListFirst(&urb->trasac_list, pthis);
ptd = ((PTD_EXTENSION) pthis)->ptd;
ListFirst(&ptd->ptde->hori_link, pnext);
ListFirstPrev(&ptd->ptde->hori_link, pprev);
if (pprev == NULL || pnext == NULL)
return FALSE;
pnext_td = struct_ptr(pnext, TD_EXTENSION, hori_link)->ptd;
pprev_td = struct_ptr(pprev, TD_EXTENSION, hori_link)->ptd;
if (pprev_td != pnext_td)
pprev_td->link = pnext_td->phy_addr;
else
{
//the last one
for(i = UHCI_MAX_SKELTDS - 2; i >= 0; i--)
{
//UHCI_MAX_SKELTDS -1 skel tds for int transfer
if (pprev_td == uhci->skel_td[i])
break;
}
ASSERT(i >= 0);
if (i == 0)
{
pprev_td->link = uhci->skel_qh[0]->phy_addr;
}
else
{
pprev_td->link = uhci->skel_td[i - 1]->phy_addr;
}
}
RemoveEntryList(&ptd->ptde->hori_link);
urb->flags &= ~URB_FLAG_IN_SCHEDULE;
return TRUE;
}
BOOLEAN
uhci_insert_tds_qh(PUHCI_QH pqh, PUHCI_TD td_chain)
{
if (pqh == NULL || td_chain == NULL)
return FALSE;
InsertTailList(&td_chain->ptde->vert_link, &pqh->pqhe->vert_link);
pqh->element = td_chain->phy_addr;
return TRUE;
}
BOOLEAN
uhci_insert_qh_urb(PURB urb, PUHCI_QH qh_chain)
{
if (urb == NULL || qh_chain == NULL)
return FALSE;
InsertTailList(&qh_chain->pqhe->vert_link, &urb->trasac_list);
qh_chain->pqhe->purb = urb;
return TRUE;
}
// must have dev_lock and frame_list_lock acquired
BOOLEAN
uhci_insert_urb_schedule(PUHCI_DEV uhci, PURB urb)
{
PUHCI_QH pqh, pskel_qh, pnext_qh;
PUHCI_TD ptd, plast_td;
PLIST_ENTRY pthis, pnext;
int i;
if (uhci == NULL || urb == NULL)
return FALSE;
ListFirst(&urb->trasac_list, pthis);
if (pthis == NULL)
return FALSE;
InsertTailList(&uhci->urb_list, &urb->urb_link);
urb->flags &= ~URB_FLAG_STATE_MASK;
urb->flags |= URB_FLAG_STATE_IN_PROCESS | URB_FLAG_IN_SCHEDULE;
switch (endp_type(urb->pendp))
{
case USB_ENDPOINT_XFER_CONTROL:
{
pqh = ((PQH_EXTENSION) pthis)->pqh;
if ((dev_from_endp(urb->pendp)->flags & USB_DEV_FLAG_LOW_SPEED) == 0)
{
pskel_qh = uhci->skel_hs_control_qh;
pnext_qh = uhci->skel_bulk_qh;
}
else
{
pskel_qh = uhci->skel_ls_control_qh;
pnext_qh = uhci->skel_hs_control_qh;
}
ListFirstPrev(&pskel_qh->pqhe->hori_link, pthis);
if (pthis == NULL)
pthis = &pskel_qh->pqhe->hori_link;
InsertTailList(&pskel_qh->pqhe->hori_link, &pqh->pqhe->hori_link);
pqh->link = pnext_qh->phy_addr;
struct_ptr(pthis, QH_EXTENSION, hori_link)->pqh->link = pqh->phy_addr;
//full speed band reclaimation
if ((urb->pipe & USB_DEV_FLAG_LOW_SPEED) == 0)
{
uhci->fsbr_cnt++;
if (uhci->fsbr_cnt == 1)
{
uhci->skel_term_qh->link = uhci->skel_hs_control_qh->phy_addr;
}
}
return TRUE;
}
case USB_ENDPOINT_XFER_BULK:
{
pqh = ((PQH_EXTENSION) pthis)->pqh;
ListFirstPrev(&uhci->skel_bulk_qh->pqhe->hori_link, pthis);
if (pthis == NULL)
pthis = &uhci->skel_bulk_qh->pqhe->hori_link;
InsertTailList(&uhci->skel_bulk_qh->pqhe->hori_link, &pqh->pqhe->hori_link);
pqh->link = uhci->skel_term_qh->phy_addr;
struct_ptr(pthis, QH_EXTENSION, hori_link)->pqh->link = pqh->phy_addr;
//full speed band reclaimation
uhci->fsbr_cnt++;
if (uhci->fsbr_cnt == 1)
{
uhci->skel_term_qh->link = uhci->skel_hs_control_qh->phy_addr;
}
return TRUE;
}
case USB_ENDPOINT_XFER_INT:
{
//bandwidth claim is done outside
ptd = ((PTD_EXTENSION) pthis)->ptd;
get_int_idx(urb, i);
ListFirstPrev(&uhci->skel_td[i]->ptde->hori_link, pthis);
if (pthis == NULL)
pthis = &uhci->skel_td[i]->ptde->hori_link;
InsertTailList(&uhci->skel_td[i]->ptde->hori_link, &ptd->ptde->hori_link);
if (i > 0)
{
ptd->link = uhci->skel_td[i - 1]->phy_addr;
}
else if (i == 0)
{
ptd->link = uhci->skel_qh[0]->phy_addr;
}
//finally link the previous td to this td
struct_ptr(pthis, TD_EXTENSION, hori_link)->ptd->link = ptd->phy_addr;
return TRUE;
}
case USB_ENDPOINT_XFER_ISOC:
{
for(i = 0; i < urb->iso_frame_count; i++)
{
ptd = ((PTD_EXTENSION) pthis)->ptd;
InsertTailList(&uhci->frame_list_cpu[(urb->iso_start_frame + i) & 0x3ff].td_link,
&ptd->ptde->hori_link);
if (IsListEmpty(&uhci->frame_list_cpu[(urb->iso_start_frame + i) & 0x3ff].td_link) == TRUE)
{
ptd->link = uhci->frame_list[(urb->iso_start_frame + i) & 0x3ff];
uhci->frame_list[i] = ptd->phy_addr;
}
else
{
ListFirstPrev(&uhci->frame_list_cpu[(urb->iso_start_frame + i) & 0x3ff].td_link, pnext);
plast_td = struct_ptr(pnext, TD_EXTENSION, hori_link)->ptd;
ptd->link = plast_td->link;
plast_td->link = ptd->phy_addr;
}
ListNext(&urb->trasac_list, pthis, pnext);
pthis = pnext;
}
return TRUE;
}
}
return FALSE;
}
//this function used as the KeSynchronizeExecution param to delegate control to uhci_insert_urb_schedule
BOOLEAN NTAPI
uhci_sync_insert_urb_schedule(PVOID context)
{
PSYNC_PARAM sync_param;
PUHCI_DEV uhci;
PURB purb;
sync_param = (PSYNC_PARAM) context;
if (sync_param == NULL)
return FALSE;
uhci = sync_param->uhci;
purb = (PURB) sync_param->context;
if (uhci == NULL || purb == NULL)
return (UCHAR) (sync_param->ret = FALSE);
return (UCHAR) (sync_param->ret = uhci_insert_urb_schedule(uhci, purb));
}
// be sure pending_endp_list_lock acquired
BOOLEAN
uhci_claim_bandwidth(PUHCI_DEV uhci,
PURB urb,
BOOLEAN claim_bw //true to claim bandwidth, false to free bandwidth
)
{
UCHAR type;
BOOLEAN ls, can_alloc;
LONG bus_time, us;
LONG i, idx, j, start_frame, interval;
if (urb == NULL)
return FALSE;
can_alloc = TRUE;
type = (UCHAR) (urb->pipe & USB_ENDPOINT_XFERTYPE_MASK);
if (type == USB_ENDPOINT_XFER_BULK || type == USB_ENDPOINT_XFER_CONTROL)
{
return FALSE;
}
ls = (urb->pipe & USB_DEV_FLAG_LOW_SPEED) ? TRUE : FALSE;
if (type == USB_ENDPOINT_XFER_INT)
{
start_frame = 0;
i = urb->data_length;
bus_time = usb_calc_bus_time(ls, FALSE, FALSE, i);
us = ns_to_us(bus_time);
i = (urb->pipe >> 24); //polling interval
for(interval = 0, j = 0; j < 8; j++)
{
if (i & (1 << j))
{
interval = j;
}
}
interval = 1 << interval;
start_frame = interval - 1;
if (claim_bw)
{
for(idx = 0; idx < UHCI_MAX_FRAMES; idx += interval)
{
if (uhci->frame_bw[idx] < us)
{
can_alloc = FALSE;
break;
}
}
if (!can_alloc)
{
return FALSE;
}
for(idx = start_frame; idx < UHCI_MAX_FRAMES; idx += interval)
{
uhci->frame_bw[idx] -= us;
}
}
else
{
for(idx = start_frame; idx < UHCI_MAX_FRAMES; idx += interval)
{
uhci->frame_bw[idx] += us;
}
}
}
else if (type == USB_ENDPOINT_XFER_ISOC)
{
if (claim_bw)
{
for(i = 0; i < urb->iso_frame_count; i++)
{
bus_time = usb_calc_bus_time(FALSE,
(urb->pipe & USB_DIR_IN)
? TRUE : FALSE, TRUE, urb->iso_packet_desc[i].length);
urb->iso_packet_desc[i].bus_time = ns_to_us(bus_time);
}
for(i = 0; i < urb->iso_frame_count; i++)
{
if (uhci->frame_bw[(urb->iso_start_frame + i) & 0x3ff] < urb->iso_packet_desc[i].bus_time)
{
can_alloc = FALSE;
break;
}
}
if (!can_alloc)
{
return FALSE;
}
for(i = 0; i < urb->iso_frame_count; i++)
{
uhci->frame_bw[(urb->iso_start_frame + i) & 0x3ff] -= urb->iso_packet_desc[i].bus_time;
}
}
else
{
for(i = 0; i < urb->iso_frame_count; i++)
{
uhci->frame_bw[(urb->iso_start_frame + i) & 0x3ff] += urb->iso_packet_desc[i].bus_time;
}
}
}
return TRUE;
}
//cancel a single urb
BOOLEAN NTAPI
uhci_sync_cancel_urb(PVOID context)
{
PUHCI_DEV uhci;
PSYNC_PARAM sync_param;
PURB purb2, dest_urb;
PLIST_ENTRY pthis, pnext;
BOOLEAN found = FALSE;
if (context == NULL)
return FALSE;
sync_param = (PSYNC_PARAM) context;
uhci = sync_param->uhci;
dest_urb = (PURB) sync_param->context;
if (uhci == NULL || dest_urb == NULL)
return (UCHAR) (sync_param->ret = FALSE);
ListFirst(&uhci->urb_list, pthis);
while (pthis)
{
purb2 = (PURB) pthis;
if (purb2 == dest_urb)
{
found = TRUE;
purb2->flags |= URB_FLAG_FORCE_CANCEL;
break;
}
ListNext(&uhci->urb_list, pthis, pnext);
pthis = pnext;
}
if (found)
uhci->skel_term_td->status |= TD_CTRL_IOC;
return (UCHAR) (sync_param->ret = found);
}
//note any fields of the purb can not be referenced unless it is found in some queue
NTSTATUS
uhci_cancel_urb(PUHCI_DEV uhci, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
{
PLIST_ENTRY pthis, pnext;
BOOLEAN found;
PURB purb2;
SYNC_PARAM sync_param;
USE_BASIC_NON_PENDING_IRQL;
if (uhci == NULL || purb == NULL || pdev == NULL || pendp == NULL)
return STATUS_INVALID_PARAMETER;
lock_dev(pdev, FALSE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, FALSE);
//delegate to remove device for this job
return STATUS_DEVICE_DOES_NOT_EXIST;
}
if (dev_from_endp(pendp) != pdev)
{
unlock_dev(pdev, FALSE);
return STATUS_INVALID_PARAMETER;
}
if (endp_state(pendp) == USB_ENDP_FLAG_STALL)
{
//it will be canceled in uhci_process_pending_endp
unlock_dev(pdev, FALSE);
return USB_STATUS_ENDPOINT_HALTED;
}
found = FALSE;
ListFirst(&pendp->urb_list, pthis);
while (pthis)
{
purb2 = (PURB) pthis;
if (purb2 == purb)
{
found = TRUE;
RemoveEntryList(pthis);
InitializeListHead(pthis);
break;
}
ListNext(&pendp->urb_list, pthis, pnext);
pthis = pnext;
}
unlock_dev(pdev, FALSE);
if (found)
{
purb->status = STATUS_CANCELLED;
uhci_generic_urb_completion(purb, purb->context);
lock_dev(pdev, FALSE);
pdev->ref_count--;
unlock_dev(pdev, FALSE);
return STATUS_SUCCESS;
}
// search the urb in the urb-list and try to cancel
sync_param.uhci = uhci;
sync_param.context = purb;
KeSynchronizeExecution(uhci->pdev_ext->uhci_int, uhci_sync_cancel_urb, &sync_param);
found = (BOOLEAN) sync_param.ret;
if (found)
return USB_STATUS_CANCELING;
return STATUS_INVALID_PARAMETER;
}
VOID
uhci_generic_urb_completion(PURB purb, PVOID context)
{
PUSB_DEV pdev;
USE_NON_PENDING_IRQL;
old_irql = KeGetCurrentIrql();
if (old_irql > DISPATCH_LEVEL)
TRAP();
if (old_irql < DISPATCH_LEVEL)
KeRaiseIrql(DISPATCH_LEVEL, &old_irql);
if (purb == NULL)
return;
pdev = purb->pdev;
if (pdev == NULL)
return;
lock_dev(pdev, TRUE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, TRUE);
goto LBL_CLIENT_PROCESS;
}
if (usb_error(purb->status))
{
pdev->error_count++;
}
if (purb->pendp == &pdev->default_endp)
{
if (usb_halted(purb->status))
{
pdev->time_out_count++;
if (pdev->time_out_count > 3)
{
dev_set_state(pdev, USB_DEV_STATE_ZOMB);
uhci_dbg_print(DBGLVL_MAXIMUM,
("uhci_generic_urb_completion(): contiguous error 3 times, dev 0x%x is deactivated\n",
pdev));
}
}
else
pdev->time_out_count = 0;
}
unlock_dev(pdev, TRUE);
LBL_CLIENT_PROCESS:
if (purb->completion)
purb->completion(purb, context);
if (old_irql < DISPATCH_LEVEL)
KeLowerIrql(old_irql);
return;
}
NTSTATUS
uhci_rh_submit_urb(PUSB_DEV pdev, PURB purb)
{
PUSB_DEV_MANAGER dev_mgr;
PTIMER_SVC ptimer;
PUSB_CTRL_SETUP_PACKET psetup;
PUHCI_DEV uhci;
NTSTATUS status;
USHORT port_status;
#ifndef INCLUDE_EHCI
PHUB_EXTENSION hub_ext;
#else
PHUB2_EXTENSION hub_ext;
#endif
PUSB_PORT_STATUS ps, psret;
LONG i;
USE_NON_PENDING_IRQL;
if (pdev == NULL || purb == NULL)
return STATUS_INVALID_PARAMETER;
dev_mgr = dev_mgr_from_dev(pdev);
KeAcquireSpinLock(&dev_mgr->timer_svc_list_lock, &old_irql);
lock_dev(pdev, FALSE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
{
unlock_dev(pdev, FALSE);
KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql);
return STATUS_DEVICE_DOES_NOT_EXIST;
}
uhci = uhci_from_hcd(pdev->hcd);
psetup = (PUSB_CTRL_SETUP_PACKET) purb->setup_packet;
#ifndef INCLUDE_EHCI
hub_ext = ((PHUB_EXTENSION) pdev->dev_ext);
#else
hub_ext = ((PHUB2_EXTENSION) pdev->dev_ext);
#endif
switch (endp_type(purb->pendp))
{
case USB_ENDPOINT_XFER_CONTROL:
{
if (psetup->bmRequestType == 0xa3 && psetup->bRequest == USB_REQ_GET_STATUS)
{
//get-port-status
if (psetup->wIndex == 0 || psetup->wIndex > 2 || psetup->wLength < 4)
{
purb->status = STATUS_INVALID_PARAMETER;
break;
}
if (psetup->wIndex == 1)
{
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + USBPORTSC1));
ps = &hub_ext->rh_port1_status;
}
else
{
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + USBPORTSC2));
ps = &hub_ext->rh_port2_status;
}
psret = (PUSB_PORT_STATUS) purb->data_buffer;
ps->wPortStatus = 0;
if (status & USBPORTSC_CCS)
{
ps->wPortStatus |= USB_PORT_STAT_CONNECTION;
}
if (status & USBPORTSC_PE)
{
ps->wPortStatus |= USB_PORT_STAT_ENABLE;
}
if (status & USBPORTSC_PR)
{
ps->wPortStatus |= USB_PORT_STAT_RESET;
}
if (status & USBPORTSC_SUSP)
{
ps->wPortStatus |= USB_PORT_STAT_SUSPEND;
}
if (status & USBPORTSC_LSDA)
{
ps->wPortStatus |= USB_PORT_STAT_LOW_SPEED;
}
//always power on
ps->wPortStatus |= USB_PORT_STAT_POWER;
//now set change field
if (status & USBPORTSC_CSC)
{
ps->wPortChange |= USB_PORT_STAT_C_CONNECTION;
}
if (status & USBPORTSC_PEC)
{
ps->wPortChange |= USB_PORT_STAT_C_ENABLE;
}
//don't touch other fields, will be filled by
//other function
usb_dbg_print(DBGLVL_MAXIMUM,
("uhci_rh_submit_urb(): get port status, wPortStatus=0x%x, wPortChange=0x%x, address=0x%x\n",
ps->wPortStatus, ps->wPortChange, ps));
psret->wPortChange = ps->wPortChange;
psret->wPortStatus = ps->wPortStatus;
purb->status = STATUS_SUCCESS;
break;
}
else if (psetup->bmRequestType == 0x23 && psetup->bRequest == USB_REQ_CLEAR_FEATURE)
{
//clear-port-feature
if (psetup->wIndex == 0 || psetup->wIndex > 2)
{
purb->status = STATUS_INVALID_PARAMETER;
break;
}
if (psetup->wIndex == 1)
{
i = USBPORTSC1;
ps = &hub_ext->rh_port1_status;
}
else
{
i = USBPORTSC2;
ps = &hub_ext->rh_port2_status;
}
purb->status = STATUS_SUCCESS;
switch (psetup->wValue)
{
case USB_PORT_FEAT_C_CONNECTION:
{
ps->wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
SET_RH_PORTSTAT(i, USBPORTSC_CSC);
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + i));
usb_dbg_print(DBGLVL_MAXIMUM,
("uhci_rh_submit_urb(): clear csc, port%d=0x%x\n", psetup->wIndex,
status));
break;
}
case USB_PORT_FEAT_C_ENABLE:
{
ps->wPortChange &= ~USB_PORT_STAT_C_ENABLE;
SET_RH_PORTSTAT(i, USBPORTSC_PEC);
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + i));
usb_dbg_print(DBGLVL_MAXIMUM,
("uhci_rh_submit_urb(): clear pec, port%d=0x%x\n", psetup->wIndex,
status));
break;
}
case USB_PORT_FEAT_C_RESET:
{
ps->wPortChange &= ~USB_PORT_STAT_C_RESET;
//the reset signal is down in rh_timer_svc_reset_port_completion
//so enable the port here
status = READ_PORT_USHORT((PUSHORT)(uhci->port_base + i));
usb_dbg_print(DBGLVL_MAXIMUM,
("uhci_rh_submit_urb(): clear pr, enable pe, port%d=0x%x\n",
psetup->wIndex, status));
break;
}
case USB_PORT_FEAT_ENABLE:
{
ps->wPortStatus &= ~USB_PORT_STAT_ENABLE;
CLR_RH_PORTSTAT(i, USBPORTSC_PE);
status = READ_PORT_USHORT((PUSHORT)(uhci->port_base + i));
usb_dbg_print(DBGLVL_MAXIMUM,
("uhci_rh_submit_urb(): clear pe, port%d=0x%x\n", psetup->wIndex,
status));
break;
}
default:
purb->status = STATUS_UNSUCCESSFUL;
}
break;
}
else if (psetup->bmRequestType == 0xd3 && psetup->bRequest == HUB_REQ_GET_STATE)
{
// get bus state
if (psetup->wIndex == 0 || psetup->wIndex > 2 || psetup->wLength == 0)
{
purb->status = STATUS_INVALID_PARAMETER;
break;
}
if (psetup->wIndex == 1)
{
i = USBPORTSC1;
}
else
{
i = USBPORTSC2;
}
port_status = READ_PORT_USHORT((PUSHORT)(uhci->port_base + i));
purb->data_buffer[0] = (port_status & USBPORTSC_LS);
// reverse the order
purb->data_buffer[0] ^= 0x3;
purb->status = STATUS_SUCCESS;
break;
}
else if (psetup->bmRequestType == 0x23 && psetup->bRequest == USB_REQ_SET_FEATURE)
{
//reset port
if (psetup->wValue != USB_PORT_FEAT_RESET)
{
purb->status = STATUS_INVALID_PARAMETER;
uhci_dbg_print(DBGLVL_MAXIMUM,
("uhci_rh_submit_urb(): set feature with wValue=0x%x\n", psetup->wValue));
break;
}
if (psetup->wIndex == 1)
{
i = USBPORTSC1;
}
else
{
i = USBPORTSC2;
}
ptimer = alloc_timer_svc(&dev_mgr->timer_svc_pool, 1);
if (!ptimer)
{
purb->status = STATUS_NO_MEMORY;
break;
}
ptimer->threshold = 0; // within [ 50ms, 60ms ], one tick is 10 ms
ptimer->context = (ULONG) purb;
ptimer->pdev = pdev;
ptimer->func = rh_timer_svc_reset_port_completion;
//start the timer
pdev->ref_count += 2; //one for timer and one for urb
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + i));
usb_dbg_print(DBGLVL_MAXIMUM,
("uhci_rh_submit_urb(): reset port, port%d=0x%x\n", psetup->wIndex, status));
InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link);
purb->status = STATUS_PENDING;
}
else
{
purb->status = STATUS_INVALID_PARAMETER;
}
break;
}
case USB_ENDPOINT_XFER_INT:
{
ptimer = alloc_timer_svc(&dev_mgr->timer_svc_pool, 1);
if (!ptimer)
{
purb->status = STATUS_NO_MEMORY;
break;
}
ptimer->threshold = RH_INTERVAL;
ptimer->context = (ULONG) purb;
ptimer->pdev = pdev;
ptimer->func = rh_timer_svc_int_completion;
//start the timer
InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link);
usb_dbg_print(DBGLVL_ULTRA,
("uhci_rh_submit_urb(): current rh's ref_count=0x%x\n", pdev->ref_count));
pdev->ref_count += 2; //one for timer and one for urb
purb->status = STATUS_PENDING;
break;
}
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_ISOC:
default:
{
purb->status = STATUS_INVALID_PARAMETER;
break;
}
}
unlock_dev(pdev, FALSE);
KeReleaseSpinLock(&dev_mgr->timer_svc_list_lock, old_irql);
return purb->status;
}
//must have rh dev_lock acquired
BOOLEAN
uhci_rh_reset_port(PHCD hcd, UCHAR port_idx)
{
LONG i;
PUHCI_DEV uhci;
ULONG status;
if (port_idx != 1 && port_idx != 2)
return FALSE;
if (hcd == NULL)
return FALSE;
if (port_idx == 1)
{
i = USBPORTSC1;
}
else
{
i = USBPORTSC2;
}
uhci = uhci_from_hcd(hcd);
//assert the reset signal,(implicitly disable the port)
SET_RH_PORTSTAT(i, USBPORTSC_PR);
usb_wait_ms_dpc(50);
//clear the reset signal, delay port enable till clearing port feature
CLR_RH_PORTSTAT(i, USBPORTSC_PR);
usb_wait_us_dpc(10);
SET_RH_PORTSTAT(i, USBPORTSC_PE);
//recovery time 10ms
usb_wait_ms_dpc(10);
SET_RH_PORTSTAT(i, 0x0a);
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + i));
usb_dbg_print(DBGLVL_MAXIMUM, ("uhci_rh_reset_port(): status after written=0x%x\n", status));
return TRUE;
}
NTSTATUS
uhci_dispatch_irp(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp)
{
PDEVICE_EXTENSION pdev_ext;
PUSB_DEV_MANAGER dev_mgr;
PUHCI_DEV uhci;
pdev_ext = DeviceObject->DeviceExtension;
uhci = pdev_ext->uhci;
dev_mgr = uhci->hcd_interf.hcd_get_dev_mgr(&uhci->hcd_interf);
return dev_mgr_dispatch(dev_mgr, irp);
}
VOID NTAPI
uhci_unload(IN PDRIVER_OBJECT DriverObject)
{
PDEVICE_OBJECT pdev;
PDEVICE_EXTENSION pdev_ext;
PUSB_DEV_MANAGER dev_mgr;
pdev = DriverObject->DeviceObject;
if (pdev == NULL)
return;
pdev_ext = pdev->DeviceExtension;
if (pdev_ext == NULL)
return;
dev_mgr = &g_dev_mgr;
if (dev_mgr == NULL)
return;
//
// set the termination flag
//
dev_mgr->term_flag = TRUE;
//
// wake up the thread if it is
//
KeSetEvent(&dev_mgr->wake_up_event, 0, FALSE);
KeWaitForSingleObject(dev_mgr->pthread, Executive, KernelMode, TRUE, NULL);
ObDereferenceObject(dev_mgr->pthread);
dev_mgr->pthread = NULL;
// for( i = 0; i < dev_mgr->hcd_count; i++ )
// dev_mgr->hcd_array[ i ]->hcd_release( dev_mgr->hcd_array[ i ]);
dev_mgr_release_hcd(dev_mgr);
return;
}
//the following are for hcd interface methods
VOID
uhci_set_dev_mgr(struct _HCD * hcd, PUSB_DEV_MANAGER dev_mgr)
{
hcd->dev_mgr = dev_mgr;
}
PUSB_DEV_MANAGER
uhci_get_dev_mgr(struct _HCD *hcd)
{
return hcd->dev_mgr;
}
ULONG
uhci_get_type(struct _HCD * hcd)
{
return (hcd->flags & HCD_TYPE_MASK);
}
VOID
uhci_set_id(struct _HCD * hcd, UCHAR id)
{
hcd->flags &= ~HCD_ID_MASK;
hcd->flags |= (HCD_ID_MASK & id);
}
UCHAR
uhci_get_id(struct _HCD *hcd)
{
return (UCHAR) (hcd->flags & HCD_ID_MASK);
}
UCHAR
uhci_alloc_addr(struct _HCD * hcd)
{
LONG i;
if (hcd == NULL)
return 0;
for(i = 1; i < MAX_DEVS; i++)
{
if (hcd->dev_addr_map[i >> 3] & (1 << (i & 7)))
{
continue;
}
else
{
break;
}
}
if (i >= MAX_DEVS)
return 0xff;
hcd->dev_addr_map[i >> 3] |= (1 << (i & 7));
hcd->conn_count++;
return (BYTE) i;
}
VOID
uhci_free_addr(struct _HCD * hcd, UCHAR addr)
{
if (addr & 0x80)
return;
if (hcd == NULL)
return;
hcd->dev_addr_map[addr >> 3] &= ~(1 << (addr & 7));
return;
}
NTSTATUS
uhci_submit_urb2(struct _HCD * hcd, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
{
return uhci_submit_urb(uhci_from_hcd(hcd), pdev, pendp, purb);
}
PUSB_DEV
uhci_get_root_hub(struct _HCD * hcd)
{
return uhci_from_hcd(hcd)->root_hub;
}
VOID
uhci_set_root_hub(struct _HCD * hcd, PUSB_DEV root_hub)
{
if (hcd == NULL || root_hub == NULL)
return;
uhci_from_hcd(hcd)->root_hub = root_hub;
return;
}
BOOLEAN
uhci_remove_device2(struct _HCD * hcd, PUSB_DEV pdev)
{
if (hcd == NULL || pdev == NULL)
return FALSE;
return uhci_remove_device(uhci_from_hcd(hcd), pdev);
}
BOOLEAN
uhci_hcd_release(struct _HCD * hcd)
{
PUHCI_DEV uhci;
PDEVICE_EXTENSION pdev_ext;
if (hcd == NULL)
return FALSE;
uhci = uhci_from_hcd(hcd);
pdev_ext = uhci->pdev_ext;
return uhci_release(pdev_ext->pdev_obj, hcd->dev_mgr);
}
NTSTATUS
uhci_cancel_urb2(struct _HCD * hcd, PUSB_DEV pdev, PUSB_ENDPOINT pendp, PURB purb)
{
PUHCI_DEV uhci;
if (hcd == NULL)
return STATUS_INVALID_PARAMETER;
uhci = uhci_from_hcd(hcd);
return uhci_cancel_urb(uhci, pdev, pendp, purb);
}
BOOLEAN
uhci_rh_get_dev_change(PHCD hcd, PBYTE buf)
{
PUHCI_DEV uhci;
ULONG status;
if (hcd == NULL || buf == NULL)
return FALSE;
uhci = uhci_from_hcd(hcd);
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + USBPORTSC1));
usb_dbg_print(DBGLVL_ULTRA, ("uhci_rh_get_dev_change(): rh port1 status=0x%x\n", status));
if ((status & USBPORTSC_PEC) || (status & USBPORTSC_CSC))
{
buf[0] |= (1 << 1);
}
status = READ_PORT_USHORT((PUSHORT) (uhci->port_base + USBPORTSC2));
usb_dbg_print(DBGLVL_ULTRA, ("uhci_rh_get_dev_change(): rh port2 status=0x%x\n", status));
if ((status & USBPORTSC_PEC) || (status & USBPORTSC_CSC))
{
buf[0] |= (1 << 2);
}
return TRUE;
}
NTSTATUS
uhci_dispatch(PHCD hcd, LONG disp_code, PVOID param) // locking depends on type of code
{
if (hcd == NULL)
return FALSE;
switch (disp_code)
{
case HCD_DISP_READ_PORT_COUNT:
{
if (param == NULL)
return STATUS_INVALID_PARAMETER;
*((PUCHAR) param) = 2;
return STATUS_SUCCESS;
}
case HCD_DISP_READ_RH_DEV_CHANGE:
{
if (uhci_rh_get_dev_change(hcd, param) == FALSE)
return STATUS_INVALID_PARAMETER;
return STATUS_SUCCESS;
}
}
return STATUS_NOT_IMPLEMENTED;
}
VOID
uhci_init_hcd_interface(PUHCI_DEV uhci)
{
uhci->hcd_interf.hcd_set_dev_mgr = uhci_set_dev_mgr;
uhci->hcd_interf.hcd_get_dev_mgr = uhci_get_dev_mgr;
uhci->hcd_interf.hcd_get_type = uhci_get_type;
uhci->hcd_interf.hcd_set_id = uhci_set_id;
uhci->hcd_interf.hcd_get_id = uhci_get_id;
uhci->hcd_interf.hcd_alloc_addr = uhci_alloc_addr;
uhci->hcd_interf.hcd_free_addr = uhci_free_addr;
uhci->hcd_interf.hcd_submit_urb = uhci_submit_urb2;
uhci->hcd_interf.hcd_generic_urb_completion = uhci_generic_urb_completion;
uhci->hcd_interf.hcd_get_root_hub = uhci_get_root_hub;
uhci->hcd_interf.hcd_set_root_hub = uhci_set_root_hub;
uhci->hcd_interf.hcd_remove_device = uhci_remove_device2;
uhci->hcd_interf.hcd_rh_reset_port = uhci_rh_reset_port;
uhci->hcd_interf.hcd_release = uhci_hcd_release;
uhci->hcd_interf.hcd_cancel_urb = uhci_cancel_urb2;
uhci->hcd_interf.hcd_start = uhci_start;
uhci->hcd_interf.hcd_dispatch = uhci_dispatch;
uhci->hcd_interf.flags = HCD_TYPE_UHCI; //hcd types | hcd id
}
NTSTATUS NTAPI
generic_dispatch_irp(IN PDEVICE_OBJECT dev_obj, IN PIRP irp)
{
PDEVEXT_HEADER dev_ext;
dev_ext = (PDEVEXT_HEADER) dev_obj->DeviceExtension;
if (dev_ext && dev_ext->dispatch)
return dev_ext->dispatch(dev_obj, irp);
irp->IoStatus.Information = 0;
EXIT_DISPATCH(STATUS_UNSUCCESSFUL, irp);
}
VOID NTAPI
generic_start_io(IN PDEVICE_OBJECT dev_obj, IN PIRP irp)
{
PDEVEXT_HEADER dev_ext;
KIRQL old_irql;
IoAcquireCancelSpinLock(&old_irql);
if (irp != dev_obj->CurrentIrp || irp->Cancel)
{
IoReleaseCancelSpinLock(old_irql);
return;
}
else
{
(void)IoSetCancelRoutine(irp, NULL);
IoReleaseCancelSpinLock(old_irql);
}
dev_ext = (PDEVEXT_HEADER) dev_obj->DeviceExtension;
if (dev_ext && dev_ext->start_io)
{
dev_ext->start_io(dev_obj, irp);
return;
}
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoStartNextPacket(dev_obj, FALSE);
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
NTSTATUS
NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
#if DBG
// should be done before any debug output is done.
// read our debug verbosity level from the registry
//NetacOD_GetRegistryDword( NetacOD_REGISTRY_PARAMETERS_PATH, //absolute registry path
// L"DebugLevel", // REG_DWORD ValueName
// &gDebugLevel ); // Value receiver
// debug_level = DBGLVL_MAXIMUM;
#endif
uhci_dbg_print_cond(DBGLVL_MINIMUM, DEBUG_UHCI,
("Entering DriverEntry(), RegistryPath=\n %ws\n", RegistryPath->Buffer));
// Remember our driver object, for when we create our child PDO
usb_driver_obj = DriverObject;
//
// Create dispatch points for create, close, unload
DriverObject->MajorFunction[IRP_MJ_CREATE] = generic_dispatch_irp;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = generic_dispatch_irp;
DriverObject->DriverUnload = uhci_unload;
// User mode DeviceIoControl() calls will be routed here
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = generic_dispatch_irp;
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = generic_dispatch_irp;
// User mode ReadFile()/WriteFile() calls will be routed here
DriverObject->MajorFunction[IRP_MJ_WRITE] = generic_dispatch_irp;
DriverObject->MajorFunction[IRP_MJ_READ] = generic_dispatch_irp;
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = generic_dispatch_irp;
DriverObject->MajorFunction[IRP_MJ_SCSI] = generic_dispatch_irp;
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = generic_dispatch_irp;
DriverObject->DriverStartIo = generic_start_io;
// routines for handling system PNP and power management requests
//DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = generic_dispatch_irp;
// The Functional Device Object (FDO) will not be created for PNP devices until
// this routine is called upon device plug-in.
RtlZeroMemory(&g_dev_mgr, sizeof(USB_DEV_MANAGER));
g_dev_mgr.usb_driver_obj = DriverObject;
#ifdef INCLUDE_EHCI
ehci_probe(DriverObject, RegistryPath, &g_dev_mgr);
#endif
uhci_probe(DriverObject, RegistryPath, &g_dev_mgr);
if (dev_mgr_strobe(&g_dev_mgr) == FALSE)
{
dev_mgr_release_hcd(&g_dev_mgr);
return STATUS_UNSUCCESSFUL;
}
dev_mgr_start_hcd(&g_dev_mgr);
/* Wait till all drivers are initialized */
ntStatus = KeWaitForSingleObject(&g_dev_mgr.drivers_inited, Executive, KernelMode, TRUE, NULL);
uhci_dbg_print_cond(DBGLVL_DEFAULT, DEBUG_UHCI, ("DriverEntry(): exiting... (%x)\n", ntStatus));
return STATUS_SUCCESS;
}
//note: the initialization will be in the following order
// uhci_probe
// dev_mgr_strobe
// uhci_start
// to kill dev_mgr_thread:
// dev_mgr->term_flag = TRUE;
// KeSetEvent( &dev_mgr->wake_up_event );
// this piece of code must run at passive-level