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

1848 lines
56 KiB
C

/**
* gendrv.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
*/
//this driver is part of the dev manager responsible to manage non-driver device
#include "usbdriver.h"
#include "gendrv.h"
#define if_dev( dev_obj ) \
( ( ( ( PGENDRV_DEVICE_EXTENSION)dev_obj->DeviceExtension )->pdriver->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE ) != 0 )
#define GENDRV_EXIT_DISPATCH( dev_OBJ, staTUS, iRp ) \
{\
iRp->IoStatus.Status = staTUS;\
if( staTUS != STATUS_PENDING)\
{\
IoCompleteRequest( iRp, IO_NO_INCREMENT);\
return staTUS;\
}\
IoMarkIrpPending( iRp );\
IoStartPacket( dev_OBJ, iRp, NULL, gendrv_cancel_queued_irp ); \
return STATUS_PENDING;\
}
#define GENDRV_COMPLETE_START_IO( dev_OBJ, staTUS, iRP ) \
{\
iRP->IoStatus.Status = staTUS;\
if( staTUS != STATUS_PENDING )\
{\
IoStartNextPacket( dev_OBJ, TRUE );\
IoCompleteRequest( iRP, IO_NO_INCREMENT );\
}\
return;\
}
extern POBJECT_TYPE NTSYSAPI IoDriverObjectType;
extern VOID disp_urb_completion(PURB purb, PVOID context);
VOID disp_noio_urb_completion(PURB purb, PVOID context);
NTSYSAPI NTSTATUS NTAPI ZwLoadDriver(IN PUNICODE_STRING DriverServiceName);
NTSYSAPI NTSTATUS NTAPI ZwClose(IN HANDLE Handle);
NTSYSAPI
NTSTATUS
NTAPI
ObOpenObjectByName(IN POBJECT_ATTRIBUTES ObjectAttributes,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
IN OUT PACCESS_STATE AccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN OUT PVOID ParseContext OPTIONAL, OUT PHANDLE Handle);
BOOLEAN gendrv_if_driver_destroy(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver);
VOID gendrv_set_cfg_completion(PURB purb, PVOID context);
BOOLEAN gendrv_connect(PDEV_CONNECT_DATA param, DEV_HANDLE dev_handle);
BOOLEAN gendrv_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle);
BOOLEAN gendrv_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle);
VOID gendrv_startio(IN PDEVICE_OBJECT dev_obj, IN PIRP irp);
VOID NTAPI gendrv_cancel_queued_irp(PDEVICE_OBJECT pdev_obj, PIRP pirp);
VOID gendrv_release_ext_drvr_entry(PGENDRV_DRVR_EXTENSION pdrvr_ext, PGENDRV_EXT_DRVR_ENTRY pentry);
VOID gendrv_clean_up_queued_irps(PDEVICE_OBJECT dev_obj);
PGENDRV_EXT_DRVR_ENTRY gendrv_alloc_ext_drvr_entry(PGENDRV_DRVR_EXTENSION pdrvr_ext);
PDRIVER_OBJECT gendrv_open_ext_driver(PUNICODE_STRING unicode_string);
NTSTATUS
gendrv_get_key_value(IN HANDLE KeyHandle, IN PWSTR ValueName, OUT PKEY_VALUE_FULL_INFORMATION * Information);
NTSTATUS
gendrv_open_reg_key(OUT PHANDLE handle,
IN HANDLE base_handle OPTIONAL,
IN PUNICODE_STRING keyname, IN ACCESS_MASK desired_access, IN BOOLEAN create);
BOOLEAN gendrv_do_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE if_handle, BOOLEAN is_if);
BOOLEAN gendrv_do_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle, BOOLEAN is_if);
NTSTATUS gendrv_send_pnp_msg(ULONG msg, PDEVICE_OBJECT pdev_obj, PVOID pctx);
BOOLEAN gendrv_delete_device(PUSB_DEV_MANAGER dev_mgr, PDEVICE_OBJECT dev_obj);
PDEVICE_OBJECT gendrv_create_device(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER gen_drvr, DEV_HANDLE dev_handle);
PDRIVER_OBJECT gendrv_load_ext_drvr(PGENDRV_DRVR_EXTENSION pdrvr_ext, PUSB_DESC_HEADER pdesc);
PDRIVER_OBJECT gendrv_find_drvr_by_key(PGENDRV_DRVR_EXTENSION pdrvr_ext, ULONG key);
ULONG gendrv_make_key(PUSB_DESC_HEADER pdesc);
BOOLEAN
gendrv_driver_init(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver)
{
PGENDRV_DRVR_EXTENSION pdrvr_ext;
if (dev_mgr == NULL || pdriver == NULL)
return FALSE;
pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE;
pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID
pdriver->driver_desc.product_id = 0xffff; // USB Product ID.
pdriver->driver_desc.release_num = 0x100; // Release Number of Device
pdriver->driver_desc.config_val = 0; // Configuration Value
pdriver->driver_desc.if_num = 0; // Interface Number
pdriver->driver_desc.if_class = 0xff; // Interface Class
pdriver->driver_desc.if_sub_class = 0xff; // Interface SubClass
pdriver->driver_desc.if_protocol = 0xff; // Interface Protocol
pdriver->driver_desc.driver_name = "USB generic dev driver"; // Driver name for Name Registry
pdriver->driver_desc.dev_class = USB_CLASS_VENDOR_SPEC;
pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
pdriver->driver_ext = usb_alloc_mem(NonPagedPool, sizeof(GENDRV_DRVR_EXTENSION));
if (!pdriver->driver_ext)
{
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_driver_init(): memory allocation failed!\n"));
return FALSE;
}
pdriver->driver_ext_size = sizeof(GENDRV_DRVR_EXTENSION);
RtlZeroMemory(pdriver->driver_ext, pdriver->driver_ext_size);
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) pdriver->driver_ext;
// InitializeListHead( &pdrvr_ext->dev_list );
InitializeListHead(&pdrvr_ext->ext_drvr_list);
pdrvr_ext->ext_drvr_count = 0;
ExInitializeFastMutex(&pdrvr_ext->drvr_ext_mutex);
pdriver->disp_tbl.version = 1;
pdriver->disp_tbl.dev_connect = gendrv_connect;
pdriver->disp_tbl.dev_disconnect = gendrv_disconnect;
pdriver->disp_tbl.dev_stop = gendrv_stop;
pdriver->disp_tbl.dev_reserved = NULL;
return TRUE;
}
BOOLEAN
gendrv_driver_destroy(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver)
{
return gendrv_if_driver_destroy(dev_mgr, pdriver);
}
BOOLEAN
gendrv_connect(PDEV_CONNECT_DATA param, DEV_HANDLE dev_handle)
{
PURB purb;
PUSB_CTRL_SETUP_PACKET psetup;
NTSTATUS status;
PUCHAR buf;
LONG i;
PUSB_CONFIGURATION_DESC pconfig_desc;
PUSB_DEV_MANAGER dev_mgr;
if (param == NULL || dev_handle == 0)
return FALSE;
dev_mgr = param->dev_mgr;
// let's set the configuration
purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
if (purb == NULL)
return FALSE;
buf = usb_alloc_mem(NonPagedPool, 512);
if (buf == NULL)
{
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_connect(): can not alloc buf\n"));
usb_free_mem(purb);
return FALSE;
}
// before we set the configuration, let's search to find if there
// exist interfaces we supported
psetup = (PUSB_CTRL_SETUP_PACKET) (purb)->setup_packet;
urb_init((purb));
purb->endp_handle = dev_handle | 0xffff;
purb->data_buffer = buf;
purb->data_length = 512;
purb->completion = NULL; // this is an immediate request, no completion required
purb->context = NULL;
purb->reference = 0;
psetup->bmRequestType = 0x80;
psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
psetup->wValue = USB_DT_CONFIG << 8;
psetup->wIndex = 0;
psetup->wLength = 512;
status = usb_submit_urb(dev_mgr, purb);
if (status == STATUS_PENDING)
{
TRAP();
usb_free_mem(buf);
usb_free_mem(purb);
return FALSE;
}
// check the config desc valid
pconfig_desc = (PUSB_CONFIGURATION_DESC) buf;
if (pconfig_desc->wTotalLength > 512)
{
usb_free_mem(buf);
usb_free_mem(purb);
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_connect(): error, bad configuration desc\n"));
return FALSE;
}
i = pconfig_desc->bConfigurationValue;
usb_free_mem(buf);
buf = NULL;
//set the configuration
urb_init(purb);
purb->endp_handle = dev_handle | 0xffff;
purb->data_buffer = NULL;
purb->data_length = 0;
purb->completion = gendrv_set_cfg_completion;
purb->context = dev_mgr;
purb->reference = (ULONG) param->pdriver;
psetup->bmRequestType = 0;
psetup->bRequest = USB_REQ_SET_CONFIGURATION;
psetup->wValue = (USHORT) i;
psetup->wIndex = 0;
psetup->wLength = 0;
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_connect(): start config the device, cfgval=%d\n", i));
status = usb_submit_urb(dev_mgr, purb);
if (status != STATUS_PENDING)
{
usb_free_mem(purb);
if (status == STATUS_SUCCESS)
return TRUE;
return FALSE;
}
return TRUE;
}
BOOLEAN
gendrv_event_select_driver(PUSB_DEV pdev, //always null. we do not use this param
ULONG event, ULONG context, ULONG param)
{
//
// try to search the registry to find one driver.
// if found, create the PDO, load the driver.
// and call its AddDevice.
//
LONG i;
PUSB_DRIVER pdrvr;
PGENDRV_DRVR_EXTENSION pdrvr_ext;
PGENDRV_EXT_DRVR_ENTRY pentry;
PGENDRV_DEVICE_EXTENSION pdev_ext;
PUSB_CONFIGURATION_DESC pconfig_desc;
PUSB_DEV_MANAGER dev_mgr;
PDEVICE_OBJECT pdev_obj;
PDRIVER_OBJECT pdrvr_obj;
PLIST_ENTRY pthis, pnext;
USE_BASIC_NON_PENDING_IRQL;
UNREFERENCED_PARAMETER(context);
UNREFERENCED_PARAMETER(event);
if (pdev == NULL)
return FALSE;
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_event_select_driver(): entering...\n"));
pdrvr = (PUSB_DRIVER) param;
//original code: pconfig_desc = (PUSB_CONFIGURATION_DESC) pdev->desc_buf[sizeof(USB_DEVICE_DESC)];
pconfig_desc = (PUSB_CONFIGURATION_DESC) &pdev->desc_buf[sizeof(USB_DEVICE_DESC)];
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) pdrvr->driver_ext;
//
// well, let's do the hard work to see if there is a class driver
// for this device.
// in the event routine, we have no need to check if the device is zomb or
// not, it must be alive there.
//
i = gendrv_make_key((PUSB_DESC_HEADER) pdev->pusb_dev_desc);
if (i == -1)
{
return FALSE;
}
pdrvr_obj = gendrv_find_drvr_by_key(pdrvr_ext, (ULONG) i);
if (!pdrvr_obj)
{
if ((pdrvr_obj = gendrv_load_ext_drvr(pdrvr_ext, (PUSB_DESC_HEADER) pdev->pusb_dev_desc)) == NULL)
return FALSE;
}
dev_mgr = dev_mgr_from_dev(pdev);
pdev_obj = gendrv_create_device(dev_mgr, pdrvr, usb_make_handle(pdev->dev_id, 0, 0));
if (pdev_obj == NULL)
{
goto ERROR_OUT;
}
lock_dev(pdev, FALSE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB ||
dev_mgr_set_driver(dev_mgr, usb_make_handle(pdev->dev_id, 0, 0), pdrvr, pdev) == FALSE)
{
unlock_dev(pdev, FALSE);
gendrv_delete_device(dev_mgr, pdev_obj);
goto ERROR_OUT;
}
if (pdev->usb_config)
{
pdev->dev_obj = pdev_obj;
}
unlock_dev(pdev, FALSE);
pdev_ext = (PGENDRV_DEVICE_EXTENSION) pdev_obj->DeviceExtension;
pdev_ext->desc_buf = usb_alloc_mem(NonPagedPool, 512);
if (!pdev_ext->desc_buf)
{
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_event_select_driver(): memory allocation failed!\n"));
goto ERROR_OUT;
}
RtlCopyMemory(pdev_ext->desc_buf, pconfig_desc, 512);
// insert the device to the dev_list
ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
ListFirst(&pdrvr_ext->ext_drvr_list, pthis);
pentry = NULL;
while (pthis)
{
pentry = (PGENDRV_EXT_DRVR_ENTRY) pthis;
if (pentry->pext_drvr == pdrvr_obj)
break;
ListNext(&pdrvr_ext->ext_drvr_list, pthis, pnext);
pthis = pnext;
pentry = NULL;
}
ASSERT(pentry);
InsertTailList(&pentry->dev_list, &pdev_ext->dev_obj_link);
pdev_ext->ext_drvr_entry = pentry;
pentry->ref_count++;
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
// notify the class driver, some device comes
gendrv_send_pnp_msg(GENDRV_MSG_ADDDEVICE, pdev_obj, pdrvr_obj);
usb_unlock_dev(pdev);
return TRUE;
ERROR_OUT:
usb_unlock_dev(pdev);
return FALSE;
}
VOID
gendrv_set_cfg_completion(PURB purb, PVOID context)
{
DEV_HANDLE dev_handle;
PUSB_DEV_MANAGER dev_mgr;
PUSB_DRIVER pdriver;
NTSTATUS status;
PUSB_DEV pdev;
PUSB_EVENT pevent;
USE_BASIC_NON_PENDING_IRQL;
if (purb == NULL || context == NULL)
return;
dev_handle = purb->endp_handle & ~0xffff;
dev_mgr = (PUSB_DEV_MANAGER) context;
pdriver = (PUSB_DRIVER) purb->reference;
if (purb->status != STATUS_SUCCESS)
{
usb_free_mem(purb);
return;
}
usb_free_mem(purb);
purb = NULL;
// set the dev state
status = usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev);
if (status != STATUS_SUCCESS)
{
usb_unlock_dev(pdev);
return;
}
usb_unlock_dev(pdev); // safe to release the pdev ref since we are in urb completion
KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock);
lock_dev(pdev, TRUE);
if (dev_state(pdev) >= USB_DEV_STATE_BEFORE_ZOMB)
{
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock);
return;
}
if (dev_mgr_set_driver(dev_mgr, dev_handle, pdriver, pdev) == FALSE)
{
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock);
return;
}
//transit the state to configured
pdev->flags &= ~USB_DEV_STATE_MASK;
pdev->flags |= USB_DEV_STATE_CONFIGURED;
pevent = alloc_event(&dev_mgr->event_pool, 1);
if (pevent == NULL)
{
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock);
return;
}
pevent->flags = USB_EVENT_FLAG_ACTIVE;
pevent->event = USB_EVENT_DEFAULT;
pevent->pdev = pdev;
pevent->context = 0;
pevent->param = (ULONG) pdriver;
pevent->pnext = 0; //vertical queue for serialized operation
pevent->process_event = (PROCESS_EVENT) gendrv_event_select_driver;
pevent->process_queue = event_list_default_process_queue;
InsertTailList(&dev_mgr->event_list, &pevent->event_link);
KeSetEvent(&dev_mgr->wake_up_event, 0, FALSE);
unlock_dev(pdev, TRUE);
KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock);
return;
}
BOOLEAN
gendrv_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
{
if (dev_mgr == NULL)
return FALSE;
return gendrv_do_stop(dev_mgr, dev_handle, FALSE);
}
BOOLEAN
gendrv_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
{
if (dev_mgr == NULL)
return FALSE;
return gendrv_do_disconnect(dev_mgr, dev_handle, FALSE);
}
BOOLEAN
gendrv_build_reg_string(PUSB_DESC_HEADER pdesc, PUNICODE_STRING pus)
{
CHAR desc_str[128];
STRING atemp;
if (pdesc == NULL || pus == NULL)
return FALSE;
if (pdesc->bDescriptorType == USB_DT_DEVICE)
{
PUSB_DEVICE_DESC pdev_desc;
pdev_desc = (PUSB_DEVICE_DESC) pdesc;
sprintf(desc_str, "%sv_%04x&p_%04x",
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ehci\\device\\",
pdev_desc->idVendor, pdev_desc->idProduct);
}
else if (pdesc->bDescriptorType == USB_DT_INTERFACE)
{
PUSB_INTERFACE_DESC pif_desc;
pif_desc = (PUSB_INTERFACE_DESC) pdesc;
sprintf(desc_str, "%sc_%04x&s_%04x&p_%04x",
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ehci\\interface\\",
pif_desc->bInterfaceClass, pif_desc->bInterfaceSubClass, pif_desc->bInterfaceProtocol);
}
else
return FALSE;
RtlInitString(&atemp, desc_str);
RtlAnsiStringToUnicodeString(pus, &atemp, TRUE);
return TRUE;
}
ULONG
gendrv_make_key(PUSB_DESC_HEADER pdesc)
{
PUSB_DEVICE_DESC pdev_desc;
PUSB_INTERFACE_DESC pif_desc;
if (pdesc == NULL)
return (ULONG) - 1;
if (pdesc->bDescriptorType == USB_DT_DEVICE)
{
pdev_desc = (PUSB_DEVICE_DESC) pdesc;
return ((((ULONG) pdev_desc->idVendor) << 16) | pdev_desc->idProduct);
}
else if (pdesc->bDescriptorType == USB_DT_INTERFACE)
{
pif_desc = (PUSB_INTERFACE_DESC) pdesc;
return ((((ULONG) pif_desc->bInterfaceClass) << 16) |
(((ULONG) pif_desc->bInterfaceSubClass) << 8) | ((ULONG) pif_desc->bInterfaceProtocol));
}
return (ULONG) - 1;
}
PDRIVER_OBJECT
gendrv_find_drvr_by_key(PGENDRV_DRVR_EXTENSION pdrvr_ext, ULONG key)
{
PGENDRV_EXT_DRVR_ENTRY pentry;
PLIST_ENTRY pthis, pnext;
if (pdrvr_ext == NULL || key == (ULONG) - 1)
return NULL;
ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
ListFirst(&pdrvr_ext->ext_drvr_list, pthis);
while (pthis)
{
pentry = (PGENDRV_EXT_DRVR_ENTRY) pthis;
if (pentry->drvr_key == key)
{
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
return pentry->pext_drvr;
}
ListNext(&pdrvr_ext->ext_drvr_list, pthis, pnext);
pthis = pnext;
}
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
return NULL;
}
PDRIVER_OBJECT
gendrv_load_ext_drvr(PGENDRV_DRVR_EXTENSION pdrvr_ext, PUSB_DESC_HEADER pdesc)
{
PDRIVER_OBJECT pdrvr_obj;
PGENDRV_EXT_DRVR_ENTRY pentry;
UNICODE_STRING usz, svc_name, svc_key, utemp;
PKEY_VALUE_FULL_INFORMATION val_info;
PWCHAR val_buf;
HANDLE handle;
NTSTATUS status;
if (pdrvr_ext == NULL || pdesc == NULL)
return NULL;
// try to search and load driver from outside
handle = NULL;
RtlZeroMemory(&svc_key, sizeof(svc_key));
val_info = NULL;
RtlInitUnicodeString(&usz, L"");
gendrv_build_reg_string(pdesc, &usz);
DbgPrint("UHCI: Trying to load driver %wZ\n", &usz);
if (gendrv_open_reg_key(&handle, NULL, &usz, KEY_READ, FALSE) != STATUS_SUCCESS)
{
goto ERROR_OUT;
}
if (gendrv_get_key_value(handle, L"service", &val_info) != STATUS_SUCCESS)
{
goto ERROR_OUT;
}
if (val_info->DataLength > 32)
goto ERROR_OUT;
val_buf = (PWCHAR) (((PBYTE) val_info) + val_info->DataOffset);
svc_key.Length = 0, svc_key.MaximumLength = 255;
svc_key.Buffer = usb_alloc_mem(NonPagedPool, 256);
RtlInitUnicodeString(&utemp, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
RtlAppendUnicodeStringToString(&svc_key, &utemp);
RtlInitUnicodeString(&svc_name, val_buf);
RtlAppendUnicodeStringToString(&svc_key, &svc_name);
status = ZwLoadDriver(&svc_key);
if (status != STATUS_SUCCESS)
goto ERROR_OUT;
svc_key.Length = 0;
RtlZeroMemory(svc_key.Buffer, 128);
RtlInitUnicodeString(&svc_key, L"\\Driver\\");
RtlAppendUnicodeStringToString(&svc_key, &svc_name);
pdrvr_obj = gendrv_open_ext_driver(&svc_key);
if (pdrvr_obj == NULL)
goto ERROR_OUT;
ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
// insert the driver to the drvr list
pentry = gendrv_alloc_ext_drvr_entry(pdrvr_ext);
if (pentry == NULL)
{
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
ObDereferenceObject(pdrvr_obj);
goto ERROR_OUT;
}
pentry->pext_drvr = pdrvr_obj;
InsertTailList(&pdrvr_ext->ext_drvr_list, &pentry->drvr_link);
pdrvr_ext->ext_drvr_count++;
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
ZwClose(handle);
return pdrvr_obj;
ERROR_OUT:
RtlFreeUnicodeString(&usz);
if (val_info != NULL)
{
usb_free_mem(val_info);
val_info = NULL;
}
if (svc_key.Buffer)
usb_free_mem(svc_key.Buffer);
if (handle)
ZwClose(handle);
return NULL;
}
VOID
gendrv_release_drvr(PGENDRV_DRVR_EXTENSION pdrvr_ext, PDRIVER_OBJECT pdrvr_obj)
{
PLIST_ENTRY pthis, pnext;
PGENDRV_EXT_DRVR_ENTRY pentry;
if (pdrvr_ext == NULL || pdrvr_obj == NULL)
return;
ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
ListFirst(&pdrvr_ext->ext_drvr_list, pthis);
while (pthis)
{
pentry = (PGENDRV_EXT_DRVR_ENTRY) pthis;
if (pentry->pext_drvr == pdrvr_obj)
{
ASSERT(pentry->ref_count);
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
return;
}
ListNext(&pdrvr_ext->ext_drvr_list, pthis, pnext);
pthis = pnext;
}
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
}
NTSTATUS
gendrv_send_pnp_msg(ULONG msg, PDEVICE_OBJECT pdev_obj, PVOID pctx)
{
if (pdev_obj == NULL)
return STATUS_INVALID_PARAMETER;
switch (msg)
{
case GENDRV_MSG_ADDDEVICE:
{
PDRIVER_OBJECT pdrvr_obj;
if (pctx == NULL)
return STATUS_INVALID_PARAMETER;
pdrvr_obj = (PDRIVER_OBJECT) pctx;
if (pdrvr_obj->DriverExtension)
{
return pdrvr_obj->DriverExtension->AddDevice(pdrvr_obj, pdev_obj);
}
return STATUS_IO_DEVICE_ERROR;
}
case GENDRV_MSG_STOPDEVICE:
case GENDRV_MSG_DISCDEVICE:
{
NTSTATUS status;
IO_STACK_LOCATION *irpstack;
IRP *irp;
// IRP_MJ_PNP_POWER
irp = IoAllocateIrp(2, FALSE);
if (irp == NULL)
return STATUS_NO_MEMORY;
irpstack = IoGetNextIrpStackLocation(irp);
irpstack->MajorFunction = IRP_MJ_PNP_POWER;
irpstack->MinorFunction =
(msg == GENDRV_MSG_STOPDEVICE) ? IRP_MN_STOP_DEVICE : IRP_MN_REMOVE_DEVICE;
status = IoCallDriver(pdev_obj, irp);
ASSERT(status != STATUS_PENDING);
status = irp->IoStatus.Status;
IoFreeIrp(irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
}
return STATUS_INVALID_PARAMETER;
}
BOOLEAN
gendrv_if_connect(PDEV_CONNECT_DATA params, DEV_HANDLE if_handle)
{
//
// try to search the registry to find one driver.
// if found, create the PDO, load the driver.
// and call its AddDevice.
//
LONG if_idx, i;
NTSTATUS status;
PUSB_DEV pdev;
PUSB_DRIVER pdrvr;
PUSB_INTERFACE_DESC pif_desc;
PGENDRV_DEVICE_EXTENSION pdev_ext;
PUSB_CONFIGURATION_DESC pconfig_desc;
PUSB_DEV_MANAGER dev_mgr;
PGENDRV_DRVR_EXTENSION pdrvr_ext;
PGENDRV_EXT_DRVR_ENTRY pentry;
PDEVICE_OBJECT pdev_obj;
PDRIVER_OBJECT pdrvr_obj;
PLIST_ENTRY pthis, pnext;
USE_BASIC_NON_PENDING_IRQL;
pdev = NULL;
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_if_connect(): entering...\n"));
if (params == NULL)
return FALSE;
dev_mgr = params->dev_mgr;
pdrvr = params->pdriver;
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) pdrvr->driver_ext;
status = usb_query_and_lock_dev(dev_mgr, if_handle, &pdev);
if (status != STATUS_SUCCESS)
{
goto ERROR_OUT;
}
// obtain the pointer to the config desc, the dev won't go away in this routine
pconfig_desc = pdev->usb_config->pusb_config_desc; //
usb_unlock_dev(pdev);
pdev = NULL;
if_idx = if_idx_from_handle(if_handle);
pif_desc = (PUSB_INTERFACE_DESC) (&pconfig_desc[1]);
for(i = 0; i < if_idx; i++)
{
//search for our if
if (usb_skip_if_and_altif((PUCHAR *) & pif_desc) == FALSE)
break;
}
if (pif_desc == NULL)
return FALSE;
//
// well, let's do the hard work to see if there is a class driver
// for this device.
//
i = gendrv_make_key((PUSB_DESC_HEADER) pif_desc);
if (i == -1)
{
return FALSE;
}
pdrvr_obj = gendrv_find_drvr_by_key(pdrvr_ext, (ULONG) i);
if (!pdrvr_obj)
{
if ((pdrvr_obj = gendrv_load_ext_drvr(pdrvr_ext, (PUSB_DESC_HEADER) pif_desc)) == NULL)
return FALSE;
}
pdev_obj = gendrv_create_device(dev_mgr, pdrvr, if_handle);
if (pdev_obj == NULL)
{
goto ERROR_OUT;
}
lock_dev(pdev, FALSE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB ||
dev_mgr_set_if_driver(dev_mgr, if_handle, pdrvr, pdev) == FALSE)
{
unlock_dev(pdev, FALSE);
gendrv_delete_device(dev_mgr, pdev_obj);
goto ERROR_OUT;
}
if (pdev->usb_config)
{
pdev->usb_config->interf[if_idx].if_ext = pdev_obj;
pdev->usb_config->interf[if_idx].if_ext_size = 0;
}
unlock_dev(pdev, FALSE);
pdev_ext = (PGENDRV_DEVICE_EXTENSION) pdev_obj->DeviceExtension;
pdev_ext->desc_buf = usb_alloc_mem(NonPagedPool, 512);
if (!pdev_ext->desc_buf)
{
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_if_connect(): memory allocation failed!\n"));
goto ERROR_OUT;
}
RtlCopyMemory(pdev_ext->desc_buf, pconfig_desc, 512);
pdev_ext->if_ctx.pif_desc =
(PUSB_INTERFACE_DESC) & pdev_ext->desc_buf[(PBYTE) pif_desc - (PBYTE) pconfig_desc];
// insert the device to the dev_list
ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
ListFirst(&pdrvr_ext->ext_drvr_list, pthis);
pentry = NULL;
while (pthis)
{
pentry = (PGENDRV_EXT_DRVR_ENTRY) pthis;
if (pentry->pext_drvr == pdrvr_obj)
break;
ListNext(&pdrvr_ext->ext_drvr_list, pthis, pnext);
pthis = pnext;
pentry = NULL;
}
ASSERT(pentry);
InsertTailList(&pentry->dev_list, &pdev_ext->dev_obj_link);
pdev_ext->ext_drvr_entry = pentry;
pentry->ref_count++;
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
// notify the class driver, some device comes
gendrv_send_pnp_msg(GENDRV_MSG_ADDDEVICE, pdev_obj, pdrvr_obj);
usb_unlock_dev(pdev);
return TRUE;
ERROR_OUT:
usb_unlock_dev(pdev);
return FALSE;
}
BOOLEAN
gendrv_do_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle, BOOLEAN is_if)
{
PUSB_DEV pdev;
PDEVICE_OBJECT pdev_obj;
ULONG if_idx;
if (dev_mgr == NULL)
return FALSE;
// clean up the irps
if_idx = if_idx_from_handle(dev_handle);
if (usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev) != STATUS_SUCCESS)
{
return FALSE;
}
if (is_if && pdev->usb_config)
pdev_obj = (PDEVICE_OBJECT) pdev->usb_config->interf[if_idx].if_ext;
else
pdev_obj = pdev->dev_obj;
gendrv_clean_up_queued_irps(pdev_obj);
usb_unlock_dev(pdev);
// send message to class drivers.
gendrv_send_pnp_msg(GENDRV_MSG_STOPDEVICE, pdev_obj, NULL);
return TRUE;
}
BOOLEAN
gendrv_if_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
{
if (dev_mgr == NULL)
return FALSE;
return gendrv_do_stop(dev_mgr, dev_handle, TRUE);
}
BOOLEAN
gendrv_do_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE if_handle, BOOLEAN is_if)
{
PUSB_DEV pdev;
PDEVICE_OBJECT dev_obj = NULL;
NTSTATUS status;
PUSB_DRIVER pdrvr;
PGENDRV_DRVR_EXTENSION pdrvr_ext = NULL;
PGENDRV_DEVICE_EXTENSION pdev_ext = NULL;
ULONG if_idx;
status = usb_query_and_lock_dev(dev_mgr, if_handle, &pdev);
if (pdev == NULL)
{
return FALSE;
}
if (status == STATUS_SUCCESS)
{
// must be a bug
TRAP();
}
if_idx = if_idx_from_handle(if_handle);
if (pdev->usb_config)
{
if (is_if)
{
pdrvr = pdev->usb_config->interf[if_idx].pif_drv;
dev_obj = (PDEVICE_OBJECT) pdev->usb_config->interf[if_idx].if_ext;
}
else
{
pdrvr = pdev->dev_driver;
dev_obj = pdev->dev_obj;
}
if (dev_obj == NULL)
{
// it means no driver was found for the device and thus no device object created
// we just do nothing here
return TRUE;
}
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) pdrvr->driver_ext;
pdev_ext = (PGENDRV_DEVICE_EXTENSION) dev_obj->DeviceExtension;
}
else
TRAP();
pdev = NULL;
// remove the device from the list
ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
RemoveEntryList(&pdev_ext->dev_obj_link);
pdev_ext->ext_drvr_entry->ref_count--;
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
// send message to class driver
gendrv_send_pnp_msg(GENDRV_MSG_DISCDEVICE, dev_obj, NULL);
// delete the device object
gendrv_delete_device(dev_mgr, dev_obj);
return TRUE;
}
BOOLEAN
gendrv_if_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE if_handle)
{
return gendrv_do_disconnect(dev_mgr, if_handle, TRUE);
}
BOOLEAN
gendrv_if_driver_init(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver)
{
PGENDRV_DRVR_EXTENSION pdrvr_ext;
if (dev_mgr == NULL || pdriver == NULL)
return FALSE;
pdriver->driver_desc.flags = USB_DRIVER_FLAG_IF_CAPABLE;
pdriver->driver_desc.vendor_id = 0x0000; // USB Vendor ID
pdriver->driver_desc.product_id = 0x0000; // USB Product ID.
pdriver->driver_desc.release_num = 0x100; // Release Number of Device
pdriver->driver_desc.config_val = 0; // Configuration Value
pdriver->driver_desc.if_num = 0; // Interface Number
pdriver->driver_desc.if_class = 0x0; // Interface Class
pdriver->driver_desc.if_sub_class = 0x0; // Interface SubClass
pdriver->driver_desc.if_protocol = 0x0; // Interface Protocol
pdriver->driver_desc.driver_name = "USB generic interface driver"; // Driver name for Name Registry
pdriver->driver_desc.dev_class = 0;
pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
//we have no extra data sturcture currently
pdriver->disp_tbl.version = 1;
pdriver->disp_tbl.dev_connect = gendrv_if_connect;
pdriver->disp_tbl.dev_disconnect = gendrv_if_disconnect;
pdriver->disp_tbl.dev_stop = gendrv_if_stop;
pdriver->disp_tbl.dev_reserved = NULL;
pdriver->driver_ext = usb_alloc_mem(NonPagedPool, sizeof(GENDRV_DRVR_EXTENSION));
if (!pdriver->driver_ext) return FALSE;
pdriver->driver_ext_size = sizeof(GENDRV_DRVR_EXTENSION);
RtlZeroMemory(pdriver->driver_ext, pdriver->driver_ext_size);
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) pdriver->driver_ext;
// InitializeListHead( &pdrvr_ext->dev_list );
InitializeListHead(&pdrvr_ext->ext_drvr_list);
pdrvr_ext->ext_drvr_count = 0;
ExInitializeFastMutex(&pdrvr_ext->drvr_ext_mutex);
return TRUE;
}
BOOLEAN
gendrv_if_driver_destroy(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver)
{
PGENDRV_DRVR_EXTENSION pdrvr_ext;
PLIST_ENTRY pthis;
PGENDRV_EXT_DRVR_ENTRY pentry;
if (dev_mgr == NULL || pdriver == NULL)
return FALSE;
if (pdriver->driver_ext)
{
// should we lock it?
// ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) pdriver->driver_ext;
if (pdrvr_ext->ext_drvr_count)
{
while (IsListEmpty(&pdrvr_ext->ext_drvr_list))
{
pthis = RemoveHeadList(&pdrvr_ext->ext_drvr_list);
pentry = (PGENDRV_EXT_DRVR_ENTRY) pthis;
if (pentry->pext_drvr)
{
if (pentry->ref_count)
{
// FIXME: really fail?
continue;
}
ObDereferenceObject(pentry->pext_drvr);
gendrv_release_ext_drvr_entry(pdrvr_ext, pentry);
}
}
pdrvr_ext->ext_drvr_count = 0;
}
usb_free_mem(pdriver->driver_ext);
pdriver->driver_ext = NULL;
pdriver->driver_ext_size = 0;
}
return TRUE;
}
PDRIVER_OBJECT
gendrv_open_ext_driver(PUNICODE_STRING unicode_string)
{
NTSTATUS status;
OBJECT_ATTRIBUTES oa;
HANDLE drvr_handle;
UNICODE_STRING oname;
PDRIVER_OBJECT pdrvr = NULL;
RtlZeroMemory(&oa, sizeof(oa));
oa.Length = sizeof(oa);
oa.ObjectName = &oname;
oa.Attributes = OBJ_CASE_INSENSITIVE;
RtlInitUnicodeString(&oname, L"");
RtlAppendUnicodeStringToString(&oname, unicode_string);
status = ObOpenObjectByName(&oa, IoDriverObjectType, // object type
KernelMode, // access mode
NULL, // access state
FILE_READ_DATA, // STANDARD_RIGHTS_READ, access right
NULL,
&drvr_handle);
if (status != STATUS_SUCCESS)
{
return NULL;
}
ObReferenceObjectByHandle(drvr_handle,
FILE_READ_DATA,
IoDriverObjectType,
KernelMode,
(PVOID)&pdrvr,
NULL); // OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
ZwClose(drvr_handle);
return pdrvr;
}
BOOLEAN
gendrv_close_ext_driver(PDRIVER_OBJECT pdrvr)
{
if (pdrvr == NULL)
return FALSE;
ObDereferenceObject(pdrvr);
return TRUE;
}
NTSTATUS
gendrv_dispatch(PDEVICE_OBJECT dev_obj, PIRP irp)
{
IO_STACK_LOCATION *irpstack;
PUSB_DEV_MANAGER dev_mgr;
PDEVEXT_HEADER ext_hdr;
NTSTATUS status;
if (dev_obj == NULL || irp == NULL)
return STATUS_INVALID_PARAMETER;
ext_hdr = dev_obj->DeviceExtension;
dev_mgr = ext_hdr->dev_mgr;
irpstack = IoGetNextIrpStackLocation(irp);
switch (irpstack->MajorFunction)
{
case IRP_MJ_PNP_POWER:
{
irp->IoStatus.Information = 0;
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_SUCCESS, irp);
}
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
{
status = STATUS_NOT_SUPPORTED;
if (irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SUBMIT_URB_RD ||
irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SUBMIT_URB_WR ||
irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SUBMIT_URB_NOIO)
{
PURB purb;
DEV_HANDLE endp_handle;
PGENDRV_DEVICE_EXTENSION pdev_ext;
pdev_ext = dev_obj->DeviceExtension;
if (irpstack->Parameters.DeviceIoControl.InputBufferLength < sizeof(URB))
{
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_INVALID_PARAMETER, irp);
}
purb = (PURB) irp->AssociatedIrp.SystemBuffer;
endp_handle = purb->endp_handle;
if (purb->data_buffer == NULL || purb->data_length == 0)
{
if (irpstack->Parameters.DeviceIoControl.IoControlCode != IOCTL_SUBMIT_URB_NOIO)
{
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_INVALID_PARAMETER, irp);
}
}
if (!default_endp_handle(endp_handle))
{
//no permit to other interface if interface dev
if (if_dev(dev_obj) && if_idx_from_handle(endp_handle) != pdev_ext->if_ctx.if_idx)
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_INVALID_PARAMETER, irp);
}
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_PENDING, irp);
}
else if (irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_DEV_DESC)
{
// this is a synchronous call, route to dev_mgr_dispatch
return dev_mgr_dispatch(dev_mgr, irp);
}
else if (irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_DEV_HANDLE)
{
PGENDRV_DEVICE_EXTENSION pdev_ext;
pdev_ext = dev_obj->DeviceExtension;
if (irpstack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(LONG))
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_INVALID_PARAMETER, irp);
*((PLONG) irp->AssociatedIrp.SystemBuffer) = pdev_ext->dev_handle;
irp->IoStatus.Information = sizeof(LONG);
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_SUCCESS, irp);
}
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_NOT_SUPPORTED, irp);
}
case IRP_MJ_DEVICE_CONTROL:
{
status = STATUS_NOT_SUPPORTED;
if (irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SUBMIT_URB_RD ||
irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SUBMIT_URB_WR ||
irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SUBMIT_URB_NOIO)
{
PURB purb;
DEV_HANDLE endp_handle;
PGENDRV_DEVICE_EXTENSION pdev_ext;
pdev_ext = dev_obj->DeviceExtension;
if (irpstack->Parameters.DeviceIoControl.InputBufferLength < sizeof(URB))
{
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_INVALID_PARAMETER, irp);
}
purb = (PURB) irp->AssociatedIrp.SystemBuffer;
endp_handle = purb->endp_handle;
if (!default_endp_handle(endp_handle))
{
//no permit to other interface if interface dev
if (if_dev(dev_obj) && if_idx_from_handle(endp_handle) != pdev_ext->if_ctx.if_idx)
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_INVALID_PARAMETER, irp);
}
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_PENDING, irp);
}
else if (irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_DEV_DESC)
{
// this is a synchronous call, route to dev_mgr_dispatch
return dev_mgr_dispatch(dev_mgr, irp);
}
else if (irpstack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_DEV_HANDLE)
{
PGENDRV_DEVICE_EXTENSION pdev_ext;
pdev_ext = dev_obj->DeviceExtension;
if (irpstack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(LONG))
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_INVALID_PARAMETER, irp);
*((PLONG) irp->AssociatedIrp.SystemBuffer) = pdev_ext->dev_handle;
irp->IoStatus.Information = sizeof(LONG);
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_SUCCESS, irp);
}
GENDRV_EXIT_DISPATCH(dev_obj, STATUS_NOT_SUPPORTED, irp);
}
}
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_NOT_SUPPORTED;
}
BOOLEAN
gendrv_init_dev_ext_hdr(PDEVICE_OBJECT dev_obj, PUSB_DEV_MANAGER dev_mgr)
{
PDEVEXT_HEADER dev_hdr = NULL;
if (dev_obj == NULL || dev_mgr == NULL)
return FALSE;
dev_hdr = (PDEVEXT_HEADER) dev_obj->DeviceExtension;
dev_hdr->type = NTDEV_TYPE_CLIENT_DEV;
dev_hdr->dispatch = gendrv_dispatch;
dev_hdr->start_io = (PDRIVER_STARTIO) gendrv_startio;
return TRUE;
}
PDEVICE_OBJECT
gendrv_create_device(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER gen_drvr, DEV_HANDLE dev_handle)
{
BOOLEAN is_if;
PDEVICE_OBJECT pdev;
PGENDRV_DEVICE_EXTENSION pdev_ext;
ULONG dev_id;
PGENDRV_DRVR_EXTENSION pdrvr_ext;
CHAR dev_name[64];
STRING string;
UNICODE_STRING name_string, symb_link;
NTSTATUS status;
if (dev_mgr == NULL || gen_drvr == NULL || dev_handle == 0)
return NULL;
is_if = (gen_drvr->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE) ? 1 : 0;
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_create_device(): entering...\n"));
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) gen_drvr->driver_ext;
dev_id = (UCHAR) dev_id_from_handle(dev_handle);
if (is_if == FALSE)
sprintf(dev_name, "\\Device\\gendev_%d", (int)dev_id);
else
sprintf(dev_name, "\\Device\\genifdev_%d", (int)dev_id);
RtlInitString(&string, dev_name);
RtlAnsiStringToUnicodeString(&name_string, &string, TRUE);
pdev = NULL;
status = IoCreateDevice(dev_mgr->usb_driver_obj,
sizeof(GENDRV_DEVICE_EXTENSION), &name_string, FILE_USB_DEV_TYPE, 0, TRUE, &pdev);
if (status == STATUS_SUCCESS)
{
//
// We do direct io
//
pdev->Flags |= DO_DIRECT_IO;
pdev->Flags &= ~DO_DEVICE_INITIALIZING;
pdev->StackSize = 2; //one for fdo, one for file device obj
pdev_ext = (PGENDRV_DEVICE_EXTENSION) pdev->DeviceExtension;
//may be accessed by other thread
gendrv_init_dev_ext_hdr(pdev, dev_mgr);
pdev_ext->dev_id = (UCHAR) dev_id;
pdev_ext->pdo = pdev;
pdev_ext->dev_handle = dev_handle;
pdev_ext->dev_mgr = dev_mgr;
pdev_ext->pdriver = gen_drvr;
if (is_if == FALSE)
sprintf(dev_name, "\\DosDevices\\gendev%d", (int)dev_id);
else
sprintf(dev_name, "\\DosDevices\\genifdev%d", (int)dev_id);
RtlInitString(&string, dev_name);
RtlAnsiStringToUnicodeString(&symb_link, &string, TRUE);
IoCreateSymbolicLink(&symb_link, &name_string);
RtlFreeUnicodeString(&symb_link);
KeInitializeEvent(&pdev_ext->sync_event, SynchronizationEvent, FALSE);
KeInitializeSpinLock(&pdev_ext->dev_lock);
}
RtlFreeUnicodeString(&name_string);
return pdev;
}
VOID
gendrv_deferred_delete_device(PVOID context)
{
PDEVICE_OBJECT dev_obj;
PGENDRV_DEVICE_EXTENSION pdev_ext;
PGENDRV_DRVR_EXTENSION pdrvr_ext;
LARGE_INTEGER interval;
if (context == NULL)
return;
dev_obj = (PDEVICE_OBJECT) context;
pdev_ext = dev_obj->DeviceExtension;
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) pdev_ext->pdriver->driver_ext;
interval.QuadPart = -20000; //two ms
for(;;)
{
if (dev_obj->ReferenceCount)
KeDelayExecutionThread(KernelMode, TRUE, &interval);
else
{
KeDelayExecutionThread(KernelMode, TRUE, &interval);
if (dev_obj->ReferenceCount == 0)
break;
}
}
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_deferred_delete_device(): delete device, 0x%x\n", dev_obj));
ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
RemoveEntryList(&pdev_ext->dev_obj_link);
pdev_ext->ext_drvr_entry->ref_count--;
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
IoDeleteDevice(dev_obj);
return;
}
BOOLEAN
gendrv_delete_device(PUSB_DEV_MANAGER dev_mgr, PDEVICE_OBJECT dev_obj)
{
BOOLEAN is_if;
PUSB_DRIVER pdrvr;
PGENDRV_DEVICE_EXTENSION pdev_ext;
CHAR dev_name[64];
STRING string;
UNICODE_STRING symb_link;
PGENDRV_DRVR_EXTENSION pdrvr_ext;
if (dev_mgr == NULL || dev_obj == 0)
return FALSE;
pdev_ext = (PGENDRV_DEVICE_EXTENSION) dev_obj->DeviceExtension;
pdrvr = pdev_ext->pdriver;
pdrvr_ext = (PGENDRV_DRVR_EXTENSION) pdrvr->driver_ext;
is_if = (BOOLEAN) if_dev(dev_obj);
if (is_if == FALSE)
sprintf(dev_name, "\\DosDevices\\gendev%d", (int)pdev_ext->dev_id);
else
sprintf(dev_name, "\\DosDevices\\genifdev%d", (int)pdev_ext->dev_id);
RtlInitString(&string, dev_name);
RtlAnsiStringToUnicodeString(&symb_link, &string, TRUE);
IoDeleteSymbolicLink(&symb_link);
RtlFreeUnicodeString(&symb_link);
if (pdev_ext->desc_buf)
{
usb_dbg_print(DBGLVL_MAXIMUM, ("gendrv_delete_device(): delete desc_buf\n"));
usb_free_mem(pdev_ext->desc_buf);
pdev_ext->desc_buf = NULL;
}
if (dev_obj->ReferenceCount == 0)
{
ExAcquireFastMutex(&pdrvr_ext->drvr_ext_mutex);
RemoveEntryList(&pdev_ext->dev_obj_link);
pdev_ext->ext_drvr_entry->ref_count--; //the ext_drvr_entry is actually in pdrvr_ext, so lock it.
ExReleaseFastMutex(&pdrvr_ext->drvr_ext_mutex);
IoDeleteDevice(dev_obj);
return TRUE;
}
// borrow from umss's work routine
return umss_schedule_workitem(dev_obj, gendrv_deferred_delete_device, NULL, 0);
}
// must have the drvr_ext_mutex acquired.
PGENDRV_EXT_DRVR_ENTRY
gendrv_alloc_ext_drvr_entry(PGENDRV_DRVR_EXTENSION pdrvr_ext)
{
LONG i;
if (pdrvr_ext == NULL)
return NULL;
if (pdrvr_ext->ext_drvr_count == GENDRV_MAX_EXT_DRVR)
return NULL;
for(i = 0; i < GENDRV_MAX_EXT_DRVR; i++)
{
if (pdrvr_ext->ext_drvr_array[i].drvr_link.Flink == NULL &&
pdrvr_ext->ext_drvr_array[i].drvr_link.Blink == NULL)
{
return &pdrvr_ext->ext_drvr_array[i];
}
}
return NULL;
}
// must have the drvr_ext_mutex acquired.
VOID
gendrv_release_ext_drvr_entry(PGENDRV_DRVR_EXTENSION pdrvr_ext, PGENDRV_EXT_DRVR_ENTRY pentry)
{
if (pdrvr_ext == NULL || pentry == NULL)
return;
RtlZeroMemory(pentry, sizeof(GENDRV_EXT_DRVR_ENTRY));
InitializeListHead(&pentry->dev_list);
return;
}
NTSTATUS
gendrv_open_reg_key(OUT PHANDLE handle,
IN HANDLE base_handle OPTIONAL,
IN PUNICODE_STRING keyname, IN ACCESS_MASK desired_access, IN BOOLEAN create)
/*++
Routine Description:
Opens or creates a VOLATILE registry key using the name passed in based
at the BaseHandle node.
Arguments:
Handle - Pointer to the handle which will contain the registry key that
was opened.
BaseHandle - Handle to the base path from which the key must be opened.
KeyName - Name of the Key that must be opened/created.
DesiredAccess - Specifies the desired access that the caller needs to
the key.
Create - Determines if the key is to be created if it does not exist.
Return Value:
The function value is the final status of the operation.
--*/
{
OBJECT_ATTRIBUTES object_attr;
ULONG disposition;
//
// Initialize the object for the key.
//
InitializeObjectAttributes(&object_attr,
keyname, OBJ_CASE_INSENSITIVE, base_handle, (PSECURITY_DESCRIPTOR) NULL);
//
// Create the key or open it, as appropriate based on the caller's
// wishes.
//
if (create)
{
return ZwCreateKey(handle,
desired_access,
&object_attr, 0, (PUNICODE_STRING) NULL, REG_OPTION_VOLATILE, &disposition);
}
else
{
return ZwOpenKey(handle, desired_access, &object_attr);
}
return STATUS_INVALID_PARAMETER;
}
NTSTATUS
gendrv_get_key_value(IN HANDLE KeyHandle, IN PWSTR ValueName, OUT PKEY_VALUE_FULL_INFORMATION * Information)
/*++
Routine Description:
This routine is invoked to retrieve the data for a registry key's value.
This is done by querying the value of the key with a zero-length buffer
to determine the size of the value, and then allocating a buffer and
actually querying the value into the buffer.
It is the responsibility of the caller to free the buffer.
Arguments:
KeyHandle - Supplies the key handle whose value is to be queried
ValueName - Supplies the null-terminated Unicode name of the value.
Information - Returns a pointer to the allocated data buffer.
Return Value:
The function value is the final status of the query operation.
--*/
{
UNICODE_STRING unicodeString;
NTSTATUS status;
PKEY_VALUE_FULL_INFORMATION infoBuffer;
ULONG keyValueLength;
PAGED_CODE();
RtlInitUnicodeString(&unicodeString, ValueName);
//
// Figure out how big the data value is so that a buffer of the
// appropriate size can be allocated.
//
status = ZwQueryValueKey(KeyHandle,
&unicodeString, KeyValueFullInformation, (PVOID) NULL, 0, &keyValueLength);
if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL)
{
return status;
}
//
// Allocate a buffer large enough to contain the entire key data value.
//
infoBuffer = usb_alloc_mem(NonPagedPool, keyValueLength);
if (!infoBuffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Query the data for the key value.
//
status = ZwQueryValueKey(KeyHandle,
&unicodeString,
KeyValueFullInformation, infoBuffer, keyValueLength, &keyValueLength);
if (!NT_SUCCESS(status))
{
usb_free_mem(infoBuffer);
return status;
}
//
// Everything worked, so simply return the address of the allocated
// buffer to the caller, who is now responsible for freeing it.
//
*Information = infoBuffer;
return STATUS_SUCCESS;
}
VOID
gendrv_startio(IN PDEVICE_OBJECT dev_obj, IN PIRP irp)
{
PIO_STACK_LOCATION irp_stack;
ULONG ctrl_code;
PUSB_DEV_MANAGER dev_mgr;
USE_NON_PENDING_IRQL;
if (dev_obj == NULL || irp == NULL)
return;
// standard process from walter oney
IoAcquireCancelSpinLock(&old_irql);
if (irp != dev_obj->CurrentIrp || irp->Cancel)
{
// already move on to other irp
IoReleaseCancelSpinLock(old_irql);
return;
}
else
{
(void)IoSetCancelRoutine(irp, NULL);
}
IoReleaseCancelSpinLock(old_irql);
irp->IoStatus.Information = 0;
irp_stack = IoGetCurrentIrpStackLocation(irp);
ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
dev_mgr = ((PDEVEXT_HEADER) dev_obj->DeviceExtension)->dev_mgr;
if (irp_stack->MajorFunction != IRP_MJ_DEVICE_CONTROL &&
irp_stack->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL)
{
GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp);
}
switch (ctrl_code)
{
case IOCTL_SUBMIT_URB_RD:
case IOCTL_SUBMIT_URB_NOIO:
case IOCTL_SUBMIT_URB_WR:
{
PURB purb;
ULONG endp_idx, if_idx, user_buffer_length = 0;
PUCHAR user_buffer = NULL;
PUSB_DEV pdev;
DEV_HANDLE endp_handle;
PUSB_ENDPOINT pendp;
NTSTATUS status;
if (irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(URB))
{
GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_PARAMETER, irp);
}
purb = (PURB) irp->AssociatedIrp.SystemBuffer;
if (ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR)
{
if (irp_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
user_buffer = MmGetSystemAddressForMdl(irp->MdlAddress);
user_buffer_length = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
if (user_buffer_length == 0)
GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_PARAMETER, irp);
}
else
{
if (purb->data_buffer == NULL || purb->data_length == 0)
GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_PARAMETER, irp);
user_buffer = purb->data_buffer;
user_buffer_length = purb->data_length;
}
}
purb->reference = 0;
endp_handle = purb->endp_handle;
if (usb_query_and_lock_dev(dev_mgr, endp_handle & ~0xffff, &pdev) != STATUS_SUCCESS)
{
GENDRV_COMPLETE_START_IO(dev_obj, STATUS_IO_DEVICE_ERROR, irp);
}
lock_dev(pdev, FALSE);
if (dev_state(pdev) == USB_DEV_STATE_ZOMB || (dev_state(pdev) < USB_DEV_STATE_ADDRESSED))
{
status = STATUS_INVALID_DEVICE_STATE;
goto ERROR_OUT1;
}
if (dev_state(pdev) == USB_DEV_STATE_ADDRESSED && !default_endp_handle(endp_handle))
{
status = STATUS_DEVICE_NOT_READY;
goto ERROR_OUT1;
}
if_idx = if_idx_from_handle(endp_handle);
endp_idx = endp_idx_from_handle(endp_handle);
//if_idx exceeds the upper limit
if (pdev->usb_config)
{
if (if_idx >= pdev->usb_config->if_count
|| endp_idx >= pdev->usb_config->interf[if_idx].endp_count)
{
if (!default_endp_handle(endp_handle))
{
status = STATUS_INVALID_DEVICE_STATE;
goto ERROR_OUT1;
}
}
}
endp_from_handle(pdev, endp_handle, pendp);
// FIXME: don't know what evil will let loose
if (endp_type(pendp) != USB_ENDPOINT_XFER_CONTROL)
{
if (user_buffer_length > 16)
{
status = STATUS_INVALID_PARAMETER;
goto ERROR_OUT1;
}
}
purb->pirp = irp;
purb->context = dev_mgr;
purb->reference = ctrl_code;
if (ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR)
{
purb->data_buffer = user_buffer;
purb->data_length = user_buffer_length;
purb->completion = disp_urb_completion;
}
else
{
purb->completion = disp_noio_urb_completion;
}
unlock_dev(pdev, FALSE);
//
// we have to register irp before the urb is scheduled to
// avoid race condition.
//
ASSERT(dev_mgr_register_irp(dev_mgr, irp, purb));
//
// the irp can not be canceled at this point, since it is
// now the current irp and not in any urb queue. dev_mgr_cancel_irp
// can not find it and simply return.
//
// FIXME: there is a time window that the irp is registered and
// the urb is not queued. In the meantime, the cancel
// request may come and cause the irp removed from the irp
// queue while fail to cancel due to urb not in any urb queue .
// Thus from that point on, the irp can not be canceled till it
// is completed or hanging there forever.
//
status = usb_submit_urb(dev_mgr, purb);
if (status != STATUS_PENDING)
{
// unmark the pending bit
IoGetCurrentIrpStackLocation((irp))->Control &= ~SL_PENDING_RETURNED;
dev_mgr_remove_irp(dev_mgr, irp);
}
usb_unlock_dev(pdev);
if (status != STATUS_PENDING)
{
irp->IoStatus.Status = status;
GENDRV_COMPLETE_START_IO(dev_obj, status, irp);
}
return;
ERROR_OUT1:
unlock_dev(pdev, FALSE);
usb_unlock_dev(pdev);
irp->IoStatus.Information = 0;
GENDRV_COMPLETE_START_IO(dev_obj, status, irp);
}
}
GENDRV_COMPLETE_START_IO(dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp);
}
VOID
gendrv_clean_up_queued_irps(PDEVICE_OBJECT dev_obj)
{
// called when device may not function or about to be removed, need cleanup
KIRQL cancelIrql;
PIRP irp, cur_irp;
PKDEVICE_QUEUE_ENTRY packet;
LIST_ENTRY cancel_irps, *pthis;
//
// cancel all the irps in the queue
//
if (dev_obj == NULL)
return;
InitializeListHead(&cancel_irps);
// remove the irps from device queue
IoAcquireCancelSpinLock(&cancelIrql);
cur_irp = dev_obj->CurrentIrp;
while ((packet = KeRemoveDeviceQueue(&dev_obj->DeviceQueue)))
{
irp = struct_ptr(packet, IRP, Tail.Overlay.DeviceQueueEntry);
InsertTailList(&cancel_irps, &irp->Tail.Overlay.DeviceQueueEntry.DeviceListEntry);
}
IoReleaseCancelSpinLock(cancelIrql);
// cancel the irps in process
// we did not cancel the current irp, it will be done by hcd when
// disconnect is detected.
// remove_irp_from_list( &dev_mgr->irp_list, cur_irp, dev_mgr );
while (IsListEmpty(&cancel_irps) == FALSE)
{
pthis = RemoveHeadList(&cancel_irps);
irp = struct_ptr(pthis, IRP, Tail.Overlay.DeviceQueueEntry.DeviceListEntry);
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
return;
}
VOID
NTAPI
gendrv_cancel_queued_irp(PDEVICE_OBJECT dev_obj, PIRP pirp)
{
// cancel routine for irps queued in the device queue
PUSB_DEV_MANAGER dev_mgr;
PDEVEXT_HEADER pdev_ext_hdr;
pdev_ext_hdr = (PDEVEXT_HEADER) dev_obj->DeviceExtension;
dev_mgr = pdev_ext_hdr->dev_mgr;
if (dev_obj->CurrentIrp == pirp)
{
// just before start_io set the cancel routine to null
IoReleaseCancelSpinLock(pirp->CancelIrql);
// we did not IoStartNextPacket, leave it for dev_mgr_cancel_irp, that
// is user have to call CancelIo again.
return;
}
KeRemoveEntryDeviceQueue(&dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry);
IoReleaseCancelSpinLock(pirp->CancelIrql);
pirp->IoStatus.Information = 0;
pirp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(pirp, IO_NO_INCREMENT);
// the device queue is moved on, no need to call IoStartNextPacket
return;
}