Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

633 lines
18 KiB

/*
*************************************************************************
* File: I1394.C
*
* Module: HID1394.SYS
* HID (Human Input Device) minidriver for IEEE 1394 devices.
*
* Copyright (c) 1998 Microsoft Corporation
*
* Author: ervinp
*
*************************************************************************
*/
#include <wdm.h>
#include <hidport.h>
#include <1394.h>
#include "hid1394.h"
#include "debug.h"
ULONG resetGeneration = 0;
/*
********************************************************************************
* HIDT_SubmitIRB
********************************************************************************
*
*
* Submit an IRB (IO Request Block) to the IEEE 1394 bus
* by sending the bus an IRP with IoControlCode==IOCTL_1394_CLASS.
*
*/
NTSTATUS HIDT_SubmitIRB(PDEVICE_OBJECT devObj, PIRB irb)
{
NTSTATUS status;
PDEVICE_EXTENSION devExt;
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(devObj);
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_1394_CLASS,
GET_NEXT_DEVICE_OBJECT(devObj),
NULL,
0,
NULL,
0,
TRUE, /* INTERNAL */
&event,
&ioStatus);
if (irp){
PIO_STACK_LOCATION nextSp;
nextSp = IoGetNextIrpStackLocation(irp);
nextSp->Parameters.Others.Argument1 = irb;
status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(devObj), irp);
if (status == STATUS_PENDING) {
NTSTATUS waitStatus;
/*
* Specify a timeout of 5 seconds for this call to complete.
* Negative timeout indicates time relative to now (in 100ns units).
*
* BUGBUG - timeout happens rarely for HumGetReportDescriptor
* when you plug in and out repeatedly very fast.
* Figure out why this call never completes.
*/
static LARGE_INTEGER timeout = {(ULONG) -50000000, 0xFFFFFFFF };
waitStatus = KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, &timeout);
if (waitStatus == STATUS_TIMEOUT){
/*
* Note - Return STATUS_IO_TIMEOUT, not STATUS_TIMEOUT.
* STATUS_IO_TIMEOUT is an NT error status, STATUS_TIMEOUT is not.
*/
ioStatus.Status = STATUS_IO_TIMEOUT;
// BUGBUG - test timeout with faulty nack-ing device from glens
// BUGBUG - also need to cancel read irps at HIDCLASS level
//
// Cancel the Irp we just sent.
//
IoCancelIrp(irp);
//
// Now wait for the Irp to be cancelled/completed below
//
waitStatus = KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
}
status = ioStatus.Status;
}
}
else {
status = STATUS_DATA_ERROR;
}
return status;
}
NTSTATUS BuildIRB_GetAddrFromDevObj(PIRB irb)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_GET_ADDR_FROM_DEVICE_OBJECT;
irb->Flags = 0;
irb->u.Get1394AddressFromDeviceObject.fulFlags = 0; // BUGBUG ?
status = STATUS_SUCCESS;
return status;
}
NTSTATUS BuildIRB_BusReset(PIRB irb)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_BUS_RESET;
irb->Flags = 0;
irb->u.BusReset.fulFlags = 0;
status = STATUS_SUCCESS;
return status;
}
NTSTATUS BuildIRB_AsyncRead( PIRB irb,
PIO_ADDRESS addr,
PMDL bufferMdl,
ULONG bufLen,
ULONG resetGeneration)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_ASYNC_READ;
irb->Flags = 0;
irb->u.AsyncRead.DestinationAddress = *addr;
irb->u.AsyncRead.nNumberOfBytesToRead = bufLen;
irb->u.AsyncRead.nBlockSize = 0;
irb->u.AsyncRead.fulFlags = 0;
irb->u.AsyncRead.Mdl = bufferMdl;
irb->u.AsyncRead.ulGeneration = resetGeneration;
// BUGBUG FINISH irb->u.AsyncRead.chPriority = ;
// irb->u.AsyncRead.nSpeed = ;
// irb->u.AsyncRead.tCode = ;
irb->u.AsyncRead.Reserved = 0;
status = STATUS_SUCCESS;
return status;
}
NTSTATUS BuildIRB_AsyncWrite( PIRB irb,
PIO_ADDRESS addr,
PMDL bufferMdl,
ULONG bufLen,
ULONG resetGeneration)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_ASYNC_WRITE;
irb->Flags = 0;
irb->u.AsyncRead.DestinationAddress = *addr;
irb->u.AsyncRead.nNumberOfBytesToRead = bufLen;
irb->u.AsyncRead.nBlockSize = 0;
irb->u.AsyncRead.fulFlags = 0;
irb->u.AsyncRead.Mdl = bufferMdl;
irb->u.AsyncRead.ulGeneration = resetGeneration;
// BUGBUG FINISH irb->u.AsyncRead.chPriority = ;
// irb->u.AsyncRead.nSpeed = ;
// irb->u.AsyncRead.tCode = ;
irb->u.AsyncRead.Reserved = 0;
status = STATUS_SUCCESS;
return status;
}
NTSTATUS BuildIRB_IsochAllocateChannel(PIRB irb, ULONG requestedChannel)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_CHANNEL;
irb->Flags = 0;
irb->u.IsochAllocateChannel.nRequestedChannel = requestedChannel;
// BUGBUG is there a reserved HID channel ?
status = STATUS_SUCCESS;
return status;
}
NTSTATUS BuildIRB_IsochFreeChannel(PIRB irb, ULONG channel)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_ISOCH_FREE_CHANNEL;
irb->Flags = 0;
irb->u.IsochFreeChannel.nChannel = channel;
status = STATUS_SUCCESS;
return status;
}
NTSTATUS BuildIRB_GetLocalHostInfo(PIRB irb, ULONG level, PVOID infoPtr)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_GET_LOCAL_HOST_INFO;
irb->Flags = 0;
irb->u.GetLocalHostInformation.nLevel = level;
irb->u.GetLocalHostInformation.Information = infoPtr;
status = STATUS_SUCCESS;
return status;
}
NTSTATUS BuildIRB_GetNodeAddress(PIRB irb)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_GET_ADDR_FROM_DEVICE_OBJECT;
irb->Flags = 0;
irb->u.Get1394AddressFromDeviceObject.fulFlags = 0;
status = STATUS_SUCCESS;
return status;
}
NTSTATUS BuildIRB_Control(PIRB irb,
ULONG controlCode,
PMDL inBuffer,
ULONG inBufferLen,
PMDL outBuffer,
ULONG outBufferLen)
{
NTSTATUS status;
irb->FunctionNumber = REQUEST_GET_ADDR_FROM_DEVICE_OBJECT;
irb->Flags = 0;
irb->u.Control.ulIoControlCode = controlCode;
irb->u.Control.pInBuffer = inBuffer;
irb->u.Control.ulInBufferLength = inBufferLen;
irb->u.Control.pOutBuffer = outBuffer;
irb->u.Control.ulOutBufferLength = outBufferLen;
irb->u.Control.BytesReturned = 0;
status = STATUS_SUCCESS;
return status;
}
/*
********************************************************************************
* HIDT_ReadCompletion
********************************************************************************
*
*
*/
NTSTATUS HIDT_ReadCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context)
{
NTSTATUS status;
NTSTATUS result = STATUS_SUCCESS;
PIRB irb;
ULONG bytesRead;
PDEVICE_EXTENSION devExt;
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(devObj);
//
// We passed a pointer to the IRB as our context, get it now.
//
irb = (PIRB)context;
status = irp->IoStatus.Status;
// BUGBUG FINISH
/*
* Free the IRB we allocated in HIDT_ReadReport.
*/
ExFreePool(irb);
/*
* If the lower driver returned PENDING, mark our stack location as
* pending also. This prevents the IRP's thread from being freed if
* the client's call returns pending.
*/
if (irp->PendingReturned){
IoMarkIrpPending(irp);
}
return status;
}
/*
* HIDT_ReadReport
*
*
*
*/
NTSTATUS HIDT_ReadReport(PDEVICE_OBJECT devObj, PIRP irp, OUT BOOLEAN *needsCompletion)
{
PIRB irb;
NTSTATUS status;
ASSERT(irp->UserBuffer);
irb = ExAllocatePoolWithTag( NonPagedPool, sizeof(IRB), HID1394_TAG);
if (irb){
BOOLEAN sentIrb = FALSE;
PIO_STACK_LOCATION irpSp, nextSp;
ULONG bufLen;
PMDL bufferMdl = NULL; // BUGBUG
IO_ADDRESS addr; // BUGBUG
irpSp = IoGetCurrentIrpStackLocation(irp);
nextSp = IoGetCurrentIrpStackLocation(irp);
bufLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
ASSERT(bufLen);
// BUGBUG init bufferMdl, addr
if (BuildIRB_AsyncRead(irb, &addr, bufferMdl, bufLen, resetGeneration)){
nextSp->Parameters.Others.Argument1 = irb;
nextSp->MajorFunction = irpSp->MajorFunction;
// BUGBUG ? nextSp->Parameters.DeviceIoControl.IoControlCode = xxx;
nextSp->DeviceObject = GET_NEXT_DEVICE_OBJECT(devObj); // BUGBUG ?
IoSetCompletionRoutine( irp,
HIDT_ReadCompletion,
irb, // context
TRUE,
TRUE,
TRUE);
status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(devObj), irp);
*needsCompletion = FALSE;
sentIrb = TRUE;
}
else {
status = STATUS_DEVICE_DATA_ERROR;
}
if (!sentIrb){
ExFreePool(irb);
}
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
/*
********************************************************************************
* HIDT_ReadCompletion
********************************************************************************
*
*
*/
NTSTATUS HIDT_WriteCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context)
{
NTSTATUS status;
NTSTATUS result = STATUS_SUCCESS;
PIRB irb;
ULONG bytesRead;
PDEVICE_EXTENSION devExt;
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(devObj);
//
// We passed a pointer to the IRB as our context, get it now.
//
irb = (PIRB)context;
status = irp->IoStatus.Status;
// BUGBUG FINISH
/*
* Free the IRB we allocated in HIDT_ReadReport.
*/
ExFreePool(irb);
/*
* If the lower driver returned PENDING, mark our stack location as
* pending also. This prevents the IRP's thread from being freed if
* the client's call returns pending.
*/
if (irp->PendingReturned){
IoMarkIrpPending(irp);
}
return status;
}
/*
* HIDT_ReadReport
*
*
*
*/
NTSTATUS HIDT_WriteReport(PDEVICE_OBJECT devObj, PIRP irp, OUT BOOLEAN *needsCompletion)
{
PIRB irb;
NTSTATUS status;
ASSERT(irp->UserBuffer);
irb = ExAllocatePoolWithTag( NonPagedPool, sizeof(IRB), HID1394_TAG);
if (irb){
BOOLEAN sentIrb = FALSE;
PIO_STACK_LOCATION irpSp, nextSp;
ULONG bufLen;
PMDL bufferMdl = NULL; // BUGBUG
IO_ADDRESS addr; // BUGBUG
irpSp = IoGetCurrentIrpStackLocation(irp);
nextSp = IoGetCurrentIrpStackLocation(irp);
bufLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
ASSERT(bufLen);
// BUGBUG init bufferMdl, addr
if (BuildIRB_AsyncWrite(irb, &addr, bufferMdl, bufLen, resetGeneration)){
nextSp->Parameters.Others.Argument1 = irb;
nextSp->MajorFunction = irpSp->MajorFunction;
// BUGBUG ? nextSp->Parameters.DeviceIoControl.IoControlCode = xxx;
nextSp->DeviceObject = GET_NEXT_DEVICE_OBJECT(devObj); // BUGBUG ?
IoSetCompletionRoutine( irp,
HIDT_WriteCompletion,
irb, // context
TRUE,
TRUE,
TRUE);
status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(devObj), irp);
*needsCompletion = FALSE;
sentIrb = TRUE;
}
else {
status = STATUS_DEVICE_DATA_ERROR;
}
if (!sentIrb){
ExFreePool(irb);
}
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
#if 0
// BUGBUG: george says using host config rom is wrong;
// use GetConfigurationInfo
/*
* GetConfigROM
*
*
*/
NTSTATUS GetConfigROM(PDEVICE_OBJECT devObj)
{
PDEVICE_EXTENSION devExt;
IRB irb;
GET_LOCAL_HOST_INFO5 configRomInfo;
NTSTATUS status;
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
BuildIRB_GetLocalHostInfo(&irb, GET_HOST_CONFIG_ROM, &configRomInfo);
/*
* Make one call just to get the required length.
*/
configRomInfo.ConfigRom = NULL;
configRomInfo.ConfigRomLength = 0;
status = HIDT_SubmitIRB(devObj, &irb);
if (NT_SUCCESS(status)){
if (configRomInfo.ConfigRomLength > 0){
configRomInfo.ConfigRom = ExAllocatePoolWithTag(NonPagedPool, configRomInfo.ConfigRomLength, HID1394_TAG);
if (configRomInfo.ConfigRom){
status = HIDT_SubmitIRB(devObj, &irb);
if (NT_SUCCESS(status)){
devExt->configROM = configRomInfo.ConfigRom;
devExt->configROMlength = configRomInfo.ConfigRomLength;
}
else {
ExFreePool(configRomInfo.ConfigRom);
}
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else {
ASSERT(configRomInfo.ConfigRomLength > 0);
status = STATUS_DEVICE_DATA_ERROR;
}
}
ASSERT(NT_SUCCESS(status));
return status;
}
#endif
NTSTATUS GetConfigInfo(PDEVICE_OBJECT devObj)
{
PDEVICE_EXTENSION devExt;
IRB irb;
NTSTATUS status;
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(devObj);
irb.FunctionNumber = REQUEST_GET_CONFIGURATION_INFO;
irb.Flags = 0;
/*
* Make one call just to get the required buffer lengths.
*/
irb.u.GetConfigurationInformation.UnitDirectory = NULL;
irb.u.GetConfigurationInformation.UnitDirectoryBufferSize = 0;
irb.u.GetConfigurationInformation.UnitDependentDirectory = NULL;
irb.u.GetConfigurationInformation.UnitDependentDirectoryBufferSize = 0;
irb.u.GetConfigurationInformation.VendorLeaf = NULL;
irb.u.GetConfigurationInformation.VendorLeafBufferSize = 0;
irb.u.GetConfigurationInformation.ModelLeaf = NULL;
irb.u.GetConfigurationInformation.ModelLeafBufferSize = 0;
status = HIDT_SubmitIRB(devObj, &irb);
if (NT_SUCCESS(status)){
if (irb.u.GetConfigurationInformation.UnitDirectoryBufferSize &&
irb.u.GetConfigurationInformation.UnitDependentDirectoryBufferSize &&
irb.u.GetConfigurationInformation.VendorLeafBufferSize &&
irb.u.GetConfigurationInformation.ModelLeafBufferSize){
/*
* Allocate the required buffers
*/
irb.u.GetConfigurationInformation.UnitDirectory =
ExAllocatePoolWithTag(NonPagedPool, irb.u.GetConfigurationInformation.UnitDirectoryBufferSize, HID1394_TAG);
irb.u.GetConfigurationInformation.UnitDependentDirectory =
ExAllocatePoolWithTag(NonPagedPool, irb.u.GetConfigurationInformation.UnitDependentDirectoryBufferSize, HID1394_TAG);
irb.u.GetConfigurationInformation.VendorLeaf =
ExAllocatePoolWithTag(NonPagedPool, irb.u.GetConfigurationInformation.VendorLeafBufferSize, HID1394_TAG);
irb.u.GetConfigurationInformation.ModelLeaf =
ExAllocatePoolWithTag(NonPagedPool, irb.u.GetConfigurationInformation.ModelLeafBufferSize, HID1394_TAG);
if (irb.u.GetConfigurationInformation.UnitDirectory &&
irb.u.GetConfigurationInformation.UnitDependentDirectory &&
irb.u.GetConfigurationInformation.VendorLeaf &&
irb.u.GetConfigurationInformation.ModelLeaf){
irb.u.GetConfigurationInformation.ConfigRom = &devExt->configROM;
status = HIDT_SubmitIRB(devObj, &irb);
// BUGBUG FINISH
// UnitDirectory contains sequence of keys.
// look for HID key ?
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
if (!NT_SUCCESS(status)){
/*
* Free any of the buffers which we were able to allocate
*/
if (irb.u.GetConfigurationInformation.UnitDirectory){
ExFreePool(irb.u.GetConfigurationInformation.UnitDirectory);
}
if (irb.u.GetConfigurationInformation.UnitDependentDirectory){
ExFreePool(irb.u.GetConfigurationInformation.UnitDependentDirectory);
}
if (irb.u.GetConfigurationInformation.VendorLeaf){
ExFreePool(irb.u.GetConfigurationInformation.VendorLeaf);
}
if (irb.u.GetConfigurationInformation.ModelLeaf){
ExFreePool(irb.u.GetConfigurationInformation.ModelLeaf);
}
}
}
else {
status = STATUS_BAD_DEVICE_TYPE;
}
}
ASSERT(NT_SUCCESS(status));
return status;
}