mirror of
https://github.com/reactos/reactos.git
synced 2025-01-11 16:51:06 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
511 lines
14 KiB
C
511 lines
14 KiB
C
/**
|
|
* compdrv.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 if device
|
|
#include "usbdriver.h"
|
|
|
|
VOID compdev_set_cfg_completion(PURB purb, PVOID context);
|
|
VOID compdev_select_driver(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle);
|
|
BOOLEAN compdev_connect(PDEV_CONNECT_DATA param, DEV_HANDLE dev_handle);
|
|
BOOLEAN compdev_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle);
|
|
BOOLEAN compdev_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle);
|
|
|
|
BOOLEAN
|
|
compdev_driver_init(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver)
|
|
{
|
|
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 = 0; // Interface Class
|
|
pdriver->driver_desc.if_sub_class = 0; // Interface SubClass
|
|
pdriver->driver_desc.if_protocol = 0; // Interface Protocol
|
|
|
|
pdriver->driver_desc.driver_name = "USB composit dev driver"; // Driver name for Name Registry
|
|
pdriver->driver_desc.dev_class = USB_CLASS_PER_INTERFACE;
|
|
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->driver_ext = NULL;
|
|
pdriver->driver_ext_size = 0;
|
|
|
|
pdriver->disp_tbl.version = 1;
|
|
pdriver->disp_tbl.dev_connect = compdev_connect;
|
|
pdriver->disp_tbl.dev_disconnect = compdev_disconnect;
|
|
pdriver->disp_tbl.dev_stop = compdev_stop;
|
|
pdriver->disp_tbl.dev_reserved = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
compdev_driver_destroy(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver)
|
|
{
|
|
UNREFERENCED_PARAMETER(dev_mgr);
|
|
UNREFERENCED_PARAMETER(pdriver);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
compdev_connect(PDEV_CONNECT_DATA param, DEV_HANDLE dev_handle)
|
|
{
|
|
PURB purb;
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
NTSTATUS status;
|
|
PUCHAR buf;
|
|
LONG credit, i, j;
|
|
PUSB_CONFIGURATION_DESC pconfig_desc;
|
|
PUSB_INTERFACE_DESC pif_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, ("compdev_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;
|
|
}
|
|
|
|
// let's scan the interfacs for those we recognize
|
|
pconfig_desc = (PUSB_CONFIGURATION_DESC) buf;
|
|
if (pconfig_desc->wTotalLength > 512)
|
|
{
|
|
usb_free_mem(buf);
|
|
usb_free_mem(purb);
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_connect(): error, bad configuration desc\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
pif_desc = (PUSB_INTERFACE_DESC) & pconfig_desc[1];
|
|
for(i = 0, credit = 0; i < (LONG) pconfig_desc->bNumInterfaces; i++)
|
|
{
|
|
for(j = 0; j < DEVMGR_MAX_DRIVERS; j++)
|
|
{
|
|
credit = dev_mgr_score_driver_for_if(dev_mgr, &dev_mgr->driver_list[j], pif_desc);
|
|
if (credit)
|
|
break;
|
|
}
|
|
if (credit)
|
|
break;
|
|
|
|
if (usb_skip_if_and_altif((PUCHAR *) & pif_desc))
|
|
break;
|
|
}
|
|
|
|
i = pconfig_desc->bConfigurationValue;
|
|
usb_free_mem(buf);
|
|
buf = NULL;
|
|
if (credit == 0)
|
|
{
|
|
usb_free_mem(purb);
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_connect(): oops..., no supported interface found\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
//set the configuration
|
|
urb_init(purb);
|
|
purb->endp_handle = dev_handle | 0xffff;
|
|
purb->data_buffer = NULL;
|
|
purb->data_length = 0;
|
|
purb->completion = compdev_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, ("compdev_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;
|
|
}
|
|
|
|
VOID
|
|
compdev_event_select_if_driver(PUSB_DEV pdev, ULONG event, ULONG context, ULONG param)
|
|
{
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
DEV_HANDLE dev_handle;
|
|
|
|
UNREFERENCED_PARAMETER(param);
|
|
UNREFERENCED_PARAMETER(context);
|
|
UNREFERENCED_PARAMETER(event);
|
|
|
|
if (pdev == NULL)
|
|
return;
|
|
|
|
//
|
|
// RtlZeroMemory( &cd, sizeof( cd ) );
|
|
//
|
|
dev_mgr = dev_mgr_from_dev(pdev);
|
|
dev_handle = usb_make_handle(pdev->dev_id, 0, 0);
|
|
compdev_select_driver(dev_mgr, dev_handle);
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
compdev_post_event_select_driver(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
|
|
{
|
|
PUSB_EVENT pevent;
|
|
BOOLEAN bret;
|
|
PUSB_DEV pdev;
|
|
USE_BASIC_NON_PENDING_IRQL;
|
|
|
|
if (dev_mgr == NULL || dev_handle == 0)
|
|
return FALSE;
|
|
|
|
if (usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev) != STATUS_SUCCESS)
|
|
return FALSE;
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock);
|
|
lock_dev(pdev, TRUE);
|
|
|
|
if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
|
|
{
|
|
bret = FALSE;
|
|
goto LBL_OUT;
|
|
}
|
|
|
|
pevent = alloc_event(&dev_mgr->event_pool, 1);
|
|
if (pevent == NULL)
|
|
{
|
|
bret = FALSE;
|
|
goto LBL_OUT;
|
|
}
|
|
pevent->flags = USB_EVENT_FLAG_ACTIVE;
|
|
pevent->event = USB_EVENT_DEFAULT;
|
|
pevent->pdev = pdev;
|
|
pevent->context = 0;
|
|
pevent->param = 0;
|
|
pevent->pnext = 0; //vertical queue for serialized operation
|
|
pevent->process_event = compdev_event_select_if_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); // wake up the dev_mgr_thread
|
|
bret = TRUE;
|
|
|
|
LBL_OUT:
|
|
|
|
unlock_dev(pdev, TRUE);
|
|
KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock);
|
|
usb_unlock_dev(pdev);
|
|
return bret;
|
|
}
|
|
|
|
VOID
|
|
compdev_set_cfg_completion(PURB purb, PVOID context)
|
|
{
|
|
DEV_HANDLE dev_handle;
|
|
PUSB_DEV_MANAGER dev_mgr;
|
|
PUSB_DRIVER pdriver;
|
|
NTSTATUS status;
|
|
PUSB_DEV pdev;
|
|
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;
|
|
}
|
|
// safe to release the pdev ref since we are in urb completion
|
|
usb_unlock_dev(pdev);
|
|
|
|
lock_dev(pdev, TRUE);
|
|
if (dev_state(pdev) >= USB_DEV_STATE_BEFORE_ZOMB)
|
|
{
|
|
unlock_dev(pdev, TRUE);
|
|
return;
|
|
}
|
|
|
|
if (dev_mgr_set_driver(dev_mgr, dev_handle, pdriver, pdev) == FALSE)
|
|
return;
|
|
|
|
//transit the state to configured
|
|
pdev->flags &= ~USB_DEV_STATE_MASK;
|
|
pdev->flags |= USB_DEV_STATE_CONFIGURED;
|
|
unlock_dev(pdev, TRUE);
|
|
|
|
//
|
|
// we change to use our thread for driver choosing. it will reduce
|
|
// the race condition when different pnp event comes simultaneously
|
|
//
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_set_cfg_completion(): start select driver for the dev\n"));
|
|
compdev_post_event_select_driver(dev_mgr, dev_handle);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
compdev_select_driver(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
|
|
{
|
|
URB urb;
|
|
LONG i, j, k, credit;
|
|
ULONG dev_id;
|
|
PUCHAR buf;
|
|
NTSTATUS status;
|
|
PUSB_DRIVER pcand, ptemp_drv;
|
|
|
|
PUSB_CTRL_SETUP_PACKET psetup;
|
|
PUSB_INTERFACE_DESC pif_desc;
|
|
PUSB_CONFIGURATION_DESC pconfig_desc;
|
|
PUSB_DEV pdev;
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_select_driver(): entering...\n"));
|
|
|
|
dev_id = dev_handle >> 16;
|
|
|
|
buf = usb_alloc_mem(NonPagedPool, 512);
|
|
|
|
if (buf == NULL)
|
|
return;
|
|
|
|
// now let's get the descs, one configuration
|
|
urb_init(&urb);
|
|
psetup = (PUSB_CTRL_SETUP_PACKET) urb.setup_packet;
|
|
urb.endp_handle = dev_handle | 0xffff;
|
|
urb.data_buffer = buf;
|
|
urb.data_length = 512;
|
|
urb.completion = NULL; // this is an immediate request, no completion required
|
|
urb.context = NULL;
|
|
urb.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, &urb);
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
TRAP();
|
|
}
|
|
|
|
// let's scan the interfaces for those we recognize
|
|
pconfig_desc = (PUSB_CONFIGURATION_DESC) buf;
|
|
if (pconfig_desc->wTotalLength > 512)
|
|
{
|
|
usb_free_mem(buf);
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_select_driver(): error, bad configuration desc\n"));
|
|
return;
|
|
}
|
|
pif_desc = (PUSB_INTERFACE_DESC) & pconfig_desc[1];
|
|
|
|
if (usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev) != STATUS_SUCCESS)
|
|
{
|
|
usb_free_mem(buf);
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_select_driver(): error, dev does not exist\n"));
|
|
return;
|
|
}
|
|
|
|
usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_select_driver(): got %d interfaces\n",
|
|
(LONG)pconfig_desc->bNumInterfaces));
|
|
|
|
for(i = 0; i < (LONG) pconfig_desc->bNumInterfaces; i++)
|
|
{
|
|
for(j = 0, credit = 0, pcand = NULL; j < DEVMGR_MAX_DRIVERS; j++)
|
|
{
|
|
ptemp_drv = &dev_mgr->driver_list[j];
|
|
k = dev_mgr_score_driver_for_if(dev_mgr, ptemp_drv, pif_desc);
|
|
if (k > credit)
|
|
credit = k, pcand = ptemp_drv;
|
|
}
|
|
|
|
if (credit)
|
|
{
|
|
// ok, we find one
|
|
DEV_CONNECT_DATA param;
|
|
|
|
if (pcand->disp_tbl.dev_connect)
|
|
{
|
|
param.dev_mgr = dev_mgr;
|
|
param.pdriver = pcand;
|
|
param.dev_handle = 0;
|
|
param.if_desc = pif_desc;
|
|
pcand->disp_tbl.dev_connect(¶m, usb_make_handle(dev_id, i, 0));
|
|
}
|
|
}
|
|
if (usb_skip_if_and_altif((PUCHAR *) & pif_desc) == FALSE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
usb_unlock_dev(pdev);
|
|
|
|
if (buf)
|
|
{
|
|
usb_free_mem(buf);
|
|
buf = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
compdev_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
|
|
{
|
|
PUSB_DEV pdev;
|
|
LONG i;
|
|
ULONG dev_id;
|
|
PUSB_DRIVER pdrv;
|
|
NTSTATUS status;
|
|
|
|
if (dev_mgr == NULL || dev_handle == 0)
|
|
return FALSE;
|
|
|
|
pdev = NULL;
|
|
dev_id = dev_handle >> 16;
|
|
status = usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev);
|
|
if (pdev)
|
|
{
|
|
if (pdev->usb_config)
|
|
{
|
|
for(i = 0; i < pdev->usb_config->if_count; i++)
|
|
{
|
|
if ((pdrv = pdev->usb_config->interf[i].pif_drv))
|
|
{
|
|
pdrv->disp_tbl.dev_stop(dev_mgr, usb_make_handle(dev_id, i, 0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
usb_unlock_dev(pdev);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
compdev_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
|
|
{
|
|
PUSB_DEV pdev;
|
|
LONG i;
|
|
ULONG dev_id;
|
|
PUSB_DRIVER pdrv;
|
|
NTSTATUS status;
|
|
|
|
if (dev_mgr == NULL || dev_handle == 0)
|
|
return FALSE;
|
|
|
|
pdev = NULL;
|
|
dev_id = dev_handle >> 16;
|
|
status = usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev);
|
|
if (pdev)
|
|
{
|
|
if (pdev->usb_config)
|
|
{
|
|
for(i = 0; i < pdev->usb_config->if_count; i++)
|
|
{
|
|
if ((pdrv = pdev->usb_config->interf[i].pif_drv))
|
|
{
|
|
pdrv->disp_tbl.dev_disconnect(dev_mgr, usb_make_handle(dev_id, i, 0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
usb_unlock_dev(pdev);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// note:
|
|
// dev_mgr_set_driver seems to be dangeous since compdev, gendrv and hub and
|
|
// umss use it to set the driver while there may exist race condition when the
|
|
// dev_mgr_disconnect_dev is called. If the driver is set and the
|
|
// disconnect_dev cut in immediately, the stop or disconnect may not function
|
|
// well. Now hub and compdev's set dev_mgr_set_driver are ok.
|
|
//
|
|
// another danger comes from umss's dev irp processing. This may confuse the device
|
|
// when the disk is being read or written, and at the same time dmgrdisp's dispatch
|
|
// route irp request to the device a control request.
|