|
|
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
link.c
Abstract:
This module contains the code that is very specific to initialization and unload operations in the irenum driver
Author:
Brian Lieuallen, 7-13-2000
Environment:
Kernel mode
Revision History :
--*/
//#include "internal.h"
#define UNICODE 1
#include <ntosp.h>
#include <zwapi.h>
#include <tdikrnl.h>
#define UINT ULONG //tmp
#include <irioctl.h>
#include <ircommtdi.h>
#include <ircomm.h>
#include <ircommdbg.h>
#include "buffer.h"
#include <ntddser.h>
#include "link.h"
typedef enum { LINK_IDLE, LINK_PRE_CONNECT, LINK_ACCEPTED, LINK_ACCEPT_FAILED, LINK_CONNECTED, LINK_DISCONNECTING, LINK_CLOSING } LINK_STATE ;
typedef struct {
LONG ReferenceCount; LINK_STATE State;
BUFFER_POOL_HANDLE SendBufferPool; BUFFER_POOL_HANDLE ControlBufferPool; BUFFER_POOL_HANDLE ReceiveBufferPool;
} CONNECTION_OBJECT, *PCONNECTION_OBJECT;
typedef struct _TDI_OBJECTS {
#if DBG
PEPROCESS OpenProcess; #endif
LONG ReferenceCount; KEVENT CloseEvent;
HANDLE AddressFileHandle; PFILE_OBJECT AddressFileObject;
HANDLE ConnectionFileHandle; PFILE_OBJECT ConnectionFileObject;
} TDI_OBJECTS, *PTDI_OBJECTS;
typedef struct _LINK_OBJECT {
KSPIN_LOCK Lock;
LONG ReferenceCount; KEVENT CloseEvent; BOOLEAN Closing;
PTDI_OBJECTS TdiObjects;
PVOID Context; PLINK_RECEIVE LinkReceiveHandler; PLINK_STATE LinkStateHandler;
WORK_QUEUE_ITEM WorkItem;
ULONG SendBuffers; ULONG ControlBuffers; ULONG ReceiveBuffers;
CONNECTION_OBJECT Connection;
} LINK_OBJECT, *PLINK_OBJECT;
PTDI_OBJECTS TdiObjectFromHandle( TDI_OBJECT_HANDLE Handle );
VOID RemoveRefTdiObjects( TDI_OBJECT_HANDLE Handle );
VOID RemoveReferenceFromConnection( PLINK_OBJECT LinkObject );
NTSTATUS GetMaxSendPdu( PFILE_OBJECT FileObject, PULONG MaxPdu );
NTSTATUS ClientEventReceive ( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket );
NTSTATUS LinkEventDisconnect( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN int DisconnectDataLength, IN PVOID DisconnectData, IN int DisconnectInformationLength, IN PVOID DisconnectInformation, IN ULONG DisconnectFlags );
NTSTATUS LinkEventConnect( IN PVOID TdiEventContext, IN int RemoteAddressLength, IN PVOID RemoteAddress, IN int UserDataLength, IN PVOID UserData, IN int OptionsLength, IN PVOID Options, OUT CONNECTION_CONTEXT *ConnectionContext, OUT PIRP *AcceptIrp );
NTSTATUS IrdaCompleteAcceptIrp( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context );
NTSTATUS IrdaSetEventHandler ( IN PFILE_OBJECT FileObject, IN ULONG EventType, IN PVOID EventHandler, IN PVOID EventContext );
VOID ConnectionPassiveWorkRoutine( PVOID Context );
NTSTATUS IrdaCreateAddress( IN PTDI_ADDRESS_IRDA pRequestedIrdaAddr, OUT PHANDLE pAddrHandle )
{ NTSTATUS Status; UNICODE_STRING DeviceName; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK Iosb; UCHAR EaBuf[sizeof(FILE_FULL_EA_INFORMATION)-1 + TDI_TRANSPORT_ADDRESS_LENGTH+1 + sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IRDA)]; PFILE_FULL_EA_INFORMATION pEa = (PFILE_FULL_EA_INFORMATION) EaBuf; ULONG EaBufLen = sizeof(EaBuf); TRANSPORT_ADDRESS UNALIGNED * pTranAddr = (PTRANSPORT_ADDRESS) &(pEa->EaName[TDI_TRANSPORT_ADDRESS_LENGTH + 1]); TDI_ADDRESS_IRDA UNALIGNED * pIrdaAddr = (PTDI_ADDRESS_IRDA) pTranAddr->Address[0].Address; pEa->NextEntryOffset = 0; pEa->Flags = 0; pEa->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; RtlCopyMemory(pEa->EaName, TdiTransportAddress, pEa->EaNameLength + 1);
pEa->EaValueLength = sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IRDA); pTranAddr->TAAddressCount = 1; pTranAddr->Address[0].AddressLength = sizeof(TDI_ADDRESS_IRDA); pTranAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IRDA;
RtlCopyMemory(pIrdaAddr, pRequestedIrdaAddr, sizeof(TDI_ADDRESS_IRDA)); RtlInitUnicodeString(&DeviceName, IRDA_DEVICE_NAME);
InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ZwCreateFile( pAddrHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &Iosb, // returned status information.
0, // block size (unused).
0, // file attributes.
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, // create disposition.
0, // create options.
pEa, EaBufLen);
return Status; }
NTSTATUS IrdaCreateConnection( OUT PHANDLE pConnHandle, IN PVOID ClientContext) { NTSTATUS Status; UNICODE_STRING DeviceName; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK Iosb; UCHAR EaBuf[sizeof(FILE_FULL_EA_INFORMATION)-1 + TDI_CONNECTION_CONTEXT_LENGTH + 1 + sizeof(CONNECTION_CONTEXT)]; PFILE_FULL_EA_INFORMATION pEa = (PFILE_FULL_EA_INFORMATION) EaBuf; ULONG EaBufLen = sizeof(EaBuf); CONNECTION_CONTEXT UNALIGNED *ctx;
pEa->NextEntryOffset = 0; pEa->Flags = 0; pEa->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; pEa->EaValueLength = sizeof(CONNECTION_CONTEXT);
RtlCopyMemory(pEa->EaName, TdiConnectionContext, pEa->EaNameLength + 1); ctx = (CONNECTION_CONTEXT UNALIGNED *)&pEa->EaName[pEa->EaNameLength + 1]; *ctx = (CONNECTION_CONTEXT) ClientContext; RtlInitUnicodeString(&DeviceName, IRDA_DEVICE_NAME);
InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ZwCreateFile(pConnHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &Iosb, // returned status information.
0, // block size (unused).
0, // file attributes.
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, // create disposition.
0, // create options.
pEa, EaBufLen);
return Status; }
NTSTATUS IrdaDisconnect( PFILE_OBJECT ConnectionFileObject ) { PIRP pIrp; KEVENT Event; IO_STATUS_BLOCK Iosb; NTSTATUS Status; KeInitializeEvent( &Event, SynchronizationEvent, FALSE );
pIrp = TdiBuildInternalDeviceControlIrp( TDI_DISCONNECT, IoGetRelatedDeviceObject(ConnectionFileObject), ConnectionFileObject, &Event, &Iosb );
if (pIrp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES; } TdiBuildDisconnect( pIrp, IoGetRelatedDeviceObject(ConnectionFileObject), ConnectionFileObject, NULL, NULL, NULL, TDI_DISCONNECT_ABORT, NULL, NULL ); IoCallDriver(IoGetRelatedDeviceObject(ConnectionFileObject), pIrp);
KeWaitForSingleObject((PVOID) &Event, Executive, KernelMode, FALSE, NULL);
Status = Iosb.Status; return Status; }
NTSTATUS IrdaAssociateAddress( PFILE_OBJECT ConnectionFileObject, HANDLE AddressHandle ) { PIRP pIrp; KEVENT Event; IO_STATUS_BLOCK Iosb; NTSTATUS Status; KeInitializeEvent( &Event, SynchronizationEvent, FALSE );
pIrp = TdiBuildInternalDeviceControlIrp( TDI_ASSOCIATE_ADDRESS, IoGetRelatedDeviceObject(ConnectionFileObject), ConnectionFileObject, &Event, &Iosb);
if (pIrp == NULL) return STATUS_INSUFFICIENT_RESOURCES; TdiBuildAssociateAddress( pIrp, IoGetRelatedDeviceObject(ConnectionFileObject), ConnectionFileObject, NULL, NULL, AddressHandle); Status = IoCallDriver(IoGetRelatedDeviceObject(ConnectionFileObject), pIrp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject((PVOID) &Event, Executive, KernelMode, FALSE, NULL); } else { ASSERT(NT_ERROR(Status) || KeReadStateEvent(&Event)); } if (NT_SUCCESS(Status)) { Status = Iosb.Status; } return Status; }
NTSTATUS IrdaCreateConnectionForAddress( HANDLE AddressFileHandle, PVOID Context, PFILE_OBJECT *ConnectionFileObject, HANDLE *ConnectionFileHandle ) { NTSTATUS Status;
*ConnectionFileHandle=NULL; *ConnectionFileObject=NULL;
Status = IrdaCreateConnection(ConnectionFileHandle, Context);
if (!NT_SUCCESS(Status)) {
goto done; } Status = ObReferenceObjectByHandle( *ConnectionFileHandle, 0L, // DesiredAccess
NULL, KernelMode, ConnectionFileObject, NULL );
if (!NT_SUCCESS(Status)) {
ZwClose(*ConnectionFileHandle); *ConnectionFileHandle=NULL; *ConnectionFileObject=NULL;
goto done; } Status = IrdaAssociateAddress(*ConnectionFileObject, AddressFileHandle); if (!NT_SUCCESS(Status)) {
ZwClose(*ConnectionFileHandle); *ConnectionFileHandle=NULL;
ObDereferenceObject(*ConnectionFileObject); *ConnectionFileObject=NULL; }
done:
return Status; }
NTSTATUS InitiateConnection( PFILE_OBJECT ConnectionFileObject, ULONG DeviceAddress, PSTR ServiceName )
{ UCHAR AddrBuf[sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IRDA)]; PTRANSPORT_ADDRESS pTranAddr = (PTRANSPORT_ADDRESS) AddrBuf; PTDI_ADDRESS_IRDA pIrdaAddr = (PTDI_ADDRESS_IRDA) pTranAddr->Address[0].Address; TDI_CONNECTION_INFORMATION ConnInfo;
PIRP Irp; NTSTATUS Status; KEVENT Event; IO_STATUS_BLOCK Iosb;
KeInitializeEvent( &Event, NotificationEvent, FALSE );
Irp = TdiBuildInternalDeviceControlIrp( TDI_CONNECT, IoGetRelatedDeviceObject(ConnectionFileObject), ConnectionFileObject, &Event, &Iosb );
if (Irp == NULL) {
D_ERROR(DbgPrint("IRCOMM: TdiBuildInternalDeviceControlIrp Failed\n");)
Status=STATUS_INSUFFICIENT_RESOURCES; goto CleanUp; }
RtlZeroMemory(pIrdaAddr,sizeof(*pIrdaAddr)); RtlCopyMemory(pIrdaAddr->irdaDeviceID, &DeviceAddress, 4);
strcpy(pIrdaAddr->irdaServiceName,ServiceName);
pTranAddr->TAAddressCount = 1;
ConnInfo.UserDataLength = 0; ConnInfo.UserData = NULL; ConnInfo.OptionsLength = 0; ConnInfo.Options = NULL; ConnInfo.RemoteAddressLength = sizeof(AddrBuf); ConnInfo.RemoteAddress = pTranAddr;
TdiBuildConnect( Irp, IoGetRelatedDeviceObject(ConnectionFileObject), ConnectionFileObject, NULL, // CompRoutine
NULL, // Context
NULL, // Timeout
&ConnInfo, NULL); // ReturnConnectionInfo
Status = IoCallDriver(IoGetRelatedDeviceObject(ConnectionFileObject), Irp);
//
// If necessary, wait for the I/O to complete.
//
D_ERROR(DbgPrint("IRCOMM: status %08lx, %08lx\n",Status,Iosb.Status);)
KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
Status = Iosb.Status;
CleanUp:
return Status; }
VOID RemoveReferenceOnLink( PLINK_OBJECT LinkObject )
{
LONG Count=InterlockedDecrement(&LinkObject->ReferenceCount);
if (Count == 0) {
ASSERT(LinkObject->Closing);
KeSetEvent( &LinkObject->CloseEvent, IO_NO_INCREMENT, FALSE ); }
return; }
NTSTATUS CreateTdiLink( TDI_OBJECT_HANDLE TdiObjectHandle, ULONG DeviceAddress, CHAR *ServiceName, BOOLEAN OutGoingConnection, LINK_HANDLE *LinkHandle, PVOID Context, PLINK_RECEIVE LinkReceiveHandler, PLINK_STATE LinkStateHandler, ULONG SendBuffers, ULONG ControlBuffers, ULONG ReceiveBuffers )
{
NTSTATUS Status; PLINK_OBJECT LinkObject;
UCHAR AddrBuf[sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IRDA)];
PTRANSPORT_ADDRESS TranAddr = (PTRANSPORT_ADDRESS) AddrBuf; PTDI_ADDRESS_IRDA IrdaAddr = (PTDI_ADDRESS_IRDA) TranAddr->Address[0].Address;
*LinkHandle=NULL;
LinkObject=ALLOCATE_NONPAGED_POOL(sizeof(*LinkObject));
if (LinkObject == NULL) {
return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(LinkObject,sizeof(*LinkObject));
KeInitializeSpinLock(&LinkObject->Lock);
ExInitializeWorkItem( &LinkObject->WorkItem, ConnectionPassiveWorkRoutine, LinkObject );
LinkObject->ReferenceCount=1;
KeInitializeEvent( &LinkObject->CloseEvent, NotificationEvent, FALSE );
LinkObject->Connection.State=LINK_IDLE;
LinkObject->LinkReceiveHandler=LinkReceiveHandler; LinkObject->LinkStateHandler=LinkStateHandler; LinkObject->Context=Context;
LinkObject->SendBuffers=SendBuffers; LinkObject->ControlBuffers=ControlBuffers; LinkObject->ReceiveBuffers=ReceiveBuffers;
//
// a pointer to the tdi objects from the handle, this will add a refcount
// to the object that will need to release when we done with it
//
LinkObject->TdiObjects=TdiObjectFromHandle(TdiObjectHandle);
Status = IrdaSetEventHandler( LinkObject->TdiObjects->AddressFileObject, TDI_EVENT_RECEIVE, ClientEventReceive, LinkObject );
if (!NT_SUCCESS(Status)) {
D_ERROR(DbgPrint("IRCOMM: IrdaSetEventHandler failed for TDI_EVENT_RECEIVE : " "%08lx\n",Status);)
goto CleanUp; }
Status = IrdaSetEventHandler( LinkObject->TdiObjects->AddressFileObject, TDI_EVENT_DISCONNECT, LinkEventDisconnect, LinkObject );
if (!NT_SUCCESS(Status)) {
D_ERROR(DbgPrint("IRCOMM: IrdaSetEventHandler failed for TDI_EVENT_DISCONNECT : " "%08lx\n",Status);)
goto CleanUp; }
//
// save this now, since we may get a callbacks for a connection already
//
*LinkHandle=LinkObject;
if (!OutGoingConnection) { //
// we are going to be waiting for an incoming connection
//
UCHAR IasData[]={0x00, 0x01, 0x4, // service type 9 wire
0x01, 0x01, 0x01}; // port type serial
Status = IrdaIASOctetSet( LinkObject->TdiObjects->AddressFileObject, "IrDA:IrCOMM", "Parameters", (PUCHAR)&IasData[0], sizeof(IasData) );
if (!NT_SUCCESS(Status)) {
D_ERROR(DbgPrint("IRCOMM: IrdaIASOctetSet failed : %08lx\n",Status);)
goto CleanUp; }
Status = IrdaSetEventHandler( LinkObject->TdiObjects->AddressFileObject, TDI_EVENT_CONNECT, LinkEventConnect, LinkObject );
if (!NT_SUCCESS(Status)) { D_ERROR(DbgPrint("IRCOMM: IrdaSetEventHandler failed for TDI_EVENT_CONNECT : " "%08lx\n",Status);)
goto CleanUp; }
Status=STATUS_SUCCESS;
} else { //
// we are creating an outgoing connection
//
Status=InitiateConnection( LinkObject->TdiObjects->ConnectionFileObject, DeviceAddress, ServiceName );
if (NT_SUCCESS(Status)) {
KIRQL OldIrql;
KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
//
// we a connection begining now
//
InterlockedIncrement(&LinkObject->Connection.ReferenceCount);
//
// the connection counts against the link
//
InterlockedIncrement(&LinkObject->ReferenceCount);
LinkObject->Connection.State=LINK_PRE_CONNECT;
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
ExQueueWorkItem( &LinkObject->WorkItem, CriticalWorkQueue );
} else { //
// could not create the connection
//
*LinkHandle=NULL;
goto CleanUp; }
}
return Status;
CleanUp:
if (LinkObject->TdiObjects != NULL) {
RemoveRefTdiObjects(LinkObject->TdiObjects); LinkObject->TdiObjects=NULL; }
FREE_POOL(LinkObject);
return Status; }
VOID CloseTdiLink( LINK_HANDLE LinkHandle )
{ PLINK_OBJECT LinkObject=LinkHandle; KIRQL OldIrql; BOOLEAN Release=FALSE; NTSTATUS Status = STATUS_SUCCESS;
LinkObject->Closing=TRUE;
Status = IrdaSetEventHandler( LinkObject->TdiObjects->AddressFileObject, TDI_EVENT_RECEIVE, NULL, NULL );
if (!NT_SUCCESS(Status)) {
D_ERROR(DbgPrint("IRCOMM: IrdaSetEventHandler in CloseTdiLink failed for " "TDI_EVENT_RECEIVE : %08lx\n",Status);)
}
Status = IrdaSetEventHandler( LinkObject->TdiObjects->AddressFileObject, TDI_EVENT_DISCONNECT, NULL, NULL );
if (!NT_SUCCESS(Status)) {
D_ERROR(DbgPrint("IRCOMM: IrdaSetEventHandler in CloseTdiLink failed for " "TDI_EVENT_DISCONNECT : %08lx\n",Status);)
}
Status = IrdaSetEventHandler( LinkObject->TdiObjects->AddressFileObject, TDI_EVENT_CONNECT, NULL, NULL );
if (!NT_SUCCESS(Status)) {
D_ERROR(DbgPrint("IRCOMM: IrdaSetEventHandler in CloseTdiLink failed for " "TDI_EVENT_CONNECT : %08lx\n",Status);)
}
KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
switch (LinkObject->Connection.State) {
case LINK_IDLE: case LINK_DISCONNECTING: case LINK_ACCEPT_FAILED:
break;
case LINK_CONNECTED: //
// it is in the connected state, we need to get it cleaned up
//
LinkObject->Connection.State=LINK_DISCONNECTING; Release=TRUE;
break;
case LINK_PRE_CONNECT:
ASSERT(0); break;
default:
ASSERT(0);
break; }
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
if (Release) {
RemoveReferenceFromConnection(LinkObject); }
RemoveReferenceOnLink(LinkObject);
KeWaitForSingleObject( &LinkObject->CloseEvent, Executive, KernelMode, FALSE, NULL );
//
// the link should now be inactive
//
LinkObject->Connection.State=LINK_CLOSING;
//
// done with the TdiObjects
//
RemoveRefTdiObjects(LinkObject->TdiObjects); LinkObject->TdiObjects=NULL;
FREE_POOL(LinkObject);
return; }
CONNECTION_HANDLE GetCurrentConnection( LINK_HANDLE LinkHandle )
{
PLINK_OBJECT LinkObject=LinkHandle; CONNECTION_HANDLE ConnectionHandle=NULL; KIRQL OldIrql;
KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
if (LinkObject->Connection.State == LINK_CONNECTED) {
InterlockedIncrement(&LinkObject->Connection.ReferenceCount);
ConnectionHandle=LinkHandle; }
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
return ConnectionHandle; }
VOID ReleaseConnection( CONNECTION_HANDLE ConnectionHandle )
{ PLINK_OBJECT LinkObject=ConnectionHandle;
RemoveReferenceFromConnection(LinkObject);
return; }
PFILE_OBJECT ConnectionGetFileObject( CONNECTION_HANDLE ConnectionHandle ) {
PLINK_OBJECT LinkObject=ConnectionHandle; PFILE_OBJECT FileObject;
FileObject=LinkObject->TdiObjects->ConnectionFileObject;
ObReferenceObject(FileObject);
return FileObject;
}
VOID ConnectionReleaseFileObject( CONNECTION_HANDLE ConnectionHandle, PFILE_OBJECT FileObject ) {
PLINK_OBJECT LinkObject=ConnectionHandle;
ObDereferenceObject(FileObject);
return; }
PIRCOMM_BUFFER ConnectionGetBuffer( CONNECTION_HANDLE ConnectionHandle, BUFFER_TYPE BufferType )
{
PLINK_OBJECT LinkObject=ConnectionHandle;
switch (BufferType) {
case BUFFER_TYPE_SEND:
return GetBuffer(LinkObject->Connection.SendBufferPool);
case BUFFER_TYPE_CONTROL:
return GetBuffer(LinkObject->Connection.ControlBufferPool);
case BUFFER_TYPE_RECEIVE:
return GetBuffer(LinkObject->Connection.ReceiveBufferPool);
default:
return NULL; }
return NULL;
}
NTSTATUS IrdaRestartDeviceControl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { //
// N.B. This routine can never be demand paged because it can be
// called before any endpoints have been placed on the global
// list--see IrdaAllocateEndpoint() and it's call to
// IrdaGetTransportInfo().
//
//
// If there was an MDL in the IRP, free it and reset the pointer to
// NULL. The IO system can't handle a nonpaged pool MDL being freed
// in an IRP, which is why we do it here.
//
if ( Irp->MdlAddress != NULL ) { IoFreeMdl( Irp->MdlAddress ); Irp->MdlAddress = NULL; }
return STATUS_SUCCESS;
} // IrdaRestartDeviceControl
NTSTATUS IrdaIssueDeviceControl ( IN HANDLE FileHandle OPTIONAL, IN PFILE_OBJECT FileObject OPTIONAL, IN PVOID IrpParameters, IN ULONG IrpParametersLength, IN PVOID MdlBuffer, IN ULONG MdlBufferLength, IN UCHAR MinorFunction )
/*++
Routine Description:
Issues a device control returst to a TDI provider and waits for the request to complete.
Note that while FileHandle and FileObject are both marked as optional, in reality exactly one of these must be specified.
Arguments:
FileHandle - a TDI handle.
FileObject - a pointer to the file object corresponding to a TDI handle
IrpParameters - information to write to the parameters section of the stack location of the IRP.
IrpParametersLength - length of the parameter information. Cannot be greater than 16.
MdlBuffer - if non-NULL, a buffer of nonpaged pool to be mapped into an MDL and placed in the MdlAddress field of the IRP.
MdlBufferLength - the size of the buffer pointed to by MdlBuffer.
MinorFunction - the minor function code for the request.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{ NTSTATUS status; PFILE_OBJECT fileObject; PIRP irp; PIO_STACK_LOCATION irpSp; KEVENT event; IO_STATUS_BLOCK ioStatusBlock; PDEVICE_OBJECT deviceObject; PMDL mdl;
PAGED_CODE( );
//
// Initialize the kernel event that will signal I/O completion.
//
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
if( FileHandle != NULL ) {
ASSERT( FileObject == NULL );
//
// Get the file object corresponding to the directory's handle.
// Referencing the file object every time is necessary because the
// IO completion routine dereferences it.
//
status = ObReferenceObjectByHandle( FileHandle, 0L, // DesiredAccess
NULL, // ObjectType
KernelMode, (PVOID *)&fileObject, NULL ); if ( !NT_SUCCESS(status) ) { return status; }
} else {
ASSERT( FileObject != NULL );
//
// Reference the passed in file object. This is necessary because
// the IO completion routine dereferences it.
//
ObReferenceObject( FileObject );
fileObject = FileObject;
}
//
// Set the file object event to a non-signaled state.
//
(VOID) KeResetEvent( &fileObject->Event );
//
// Attempt to allocate and initialize the I/O Request Packet (IRP)
// for this operation.
//
deviceObject = IoGetRelatedDeviceObject ( fileObject );
irp = IoAllocateIrp( (deviceObject)->StackSize, TRUE ); if ( irp == NULL ) { ObDereferenceObject( fileObject ); return STATUS_INSUFFICIENT_RESOURCES; }
//
// Fill in the service independent parameters in the IRP.
//
irp->Flags = (LONG)IRP_SYNCHRONOUS_API; irp->RequestorMode = KernelMode; irp->PendingReturned = FALSE;
irp->UserIosb = &ioStatusBlock; irp->UserEvent = &event;
irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
irp->AssociatedIrp.SystemBuffer = NULL; irp->UserBuffer = NULL;
irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->Tail.Overlay.OriginalFileObject = fileObject; irp->Tail.Overlay.AuxiliaryBuffer = NULL;
/*
DEBUG ioStatusBlock.Status = STATUS_UNSUCCESSFUL; DEBUG ioStatusBlock.Information = (ULONG)-1; */ //
// If an MDL buffer was specified, get an MDL, map the buffer,
// and place the MDL pointer in the IRP.
//
if ( MdlBuffer != NULL ) {
mdl = IoAllocateMdl( MdlBuffer, MdlBufferLength, FALSE, FALSE, irp ); if ( mdl == NULL ) { IoFreeIrp( irp ); ObDereferenceObject( fileObject ); return STATUS_INSUFFICIENT_RESOURCES; }
MmBuildMdlForNonPagedPool( mdl );
} else {
irp->MdlAddress = NULL; }
//
// Put the file object pointer in the stack location.
//
irpSp = IoGetNextIrpStackLocation( irp ); irpSp->FileObject = fileObject; irpSp->DeviceObject = deviceObject;
//
// Fill in the service-dependent parameters for the request.
//
ASSERT( IrpParametersLength <= sizeof(irpSp->Parameters) ); RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength );
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; irpSp->MinorFunction = MinorFunction;
//
// Set up a completion routine which we'll use to free the MDL
// allocated previously.
//
IoSetCompletionRoutine( irp, IrdaRestartDeviceControl, NULL, TRUE, TRUE, TRUE );
//
// Queue the IRP to the thread and pass it to the driver.
//
IoEnqueueIrp( irp );
status = IoCallDriver( deviceObject, irp );
//
// If necessary, wait for the I/O to complete.
//
if ( status == STATUS_PENDING ) { KeWaitForSingleObject( (PVOID)&event, UserRequest, KernelMode, FALSE, NULL ); }
//
// If the request was successfully queued, get the final I/O status.
//
if ( NT_SUCCESS(status) ) { status = ioStatusBlock.Status; }
return status;
} // IrdaIssueDeviceControl
NTSTATUS IrdaSetEventHandler ( IN PFILE_OBJECT FileObject, IN ULONG EventType, IN PVOID EventHandler, IN PVOID EventContext )
/*++
Routine Description:
Sets up a TDI indication handler on a connection or address object (depending on the file handle). This is done synchronously, which shouldn't usually be an issue since TDI providers can usually complete indication handler setups immediately.
Arguments:
FileObject - a pointer to the file object for an open connection or address object.
EventType - the event for which the indication handler should be called.
EventHandler - the routine to call when tghe specified event occurs.
EventContext - context which is passed to the indication routine.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{ TDI_REQUEST_KERNEL_SET_EVENT parameters;
PAGED_CODE( );
parameters.EventType = EventType; parameters.EventHandler = EventHandler; parameters.EventContext = EventContext;
return IrdaIssueDeviceControl( NULL, FileObject, ¶meters, sizeof(parameters), NULL, 0, TDI_SET_EVENT_HANDLER );
} // IrdaSetEventHandler
NTSTATUS ClientEventReceive ( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket ) { PLINK_OBJECT LinkObject=(PLINK_OBJECT)TdiEventContext;
NTSTATUS Status;
if (!LinkObject->Closing) {
InterlockedIncrement(&LinkObject->ReferenceCount);
Status= (LinkObject->LinkReceiveHandler)( LinkObject->Context, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket );
RemoveReferenceOnLink(LinkObject);
} else {
Status=STATUS_SUCCESS; *BytesTaken=BytesAvailable; }
return Status;
}
NTSTATUS LinkEventDisconnect( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN int DisconnectDataLength, IN PVOID DisconnectData, IN int DisconnectInformationLength, IN PVOID DisconnectInformation, IN ULONG DisconnectFlags )
{ PLINK_OBJECT LinkObject=(PLINK_OBJECT)TdiEventContext; KIRQL OldIrql; BOOLEAN Release=FALSE;
if (!LinkObject->Closing) {
KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
if (LinkObject->Connection.State == LINK_CONNECTED) {
LinkObject->Connection.State=LINK_DISCONNECTING;
Release=TRUE;
}
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
if (Release) {
RemoveReferenceFromConnection(LinkObject); } }
return STATUS_SUCCESS; }
NTSTATUS LinkEventConnect( IN PVOID TdiEventContext, IN int RemoteAddressLength, IN PVOID RemoteAddress, IN int UserDataLength, IN PVOID UserData, IN int OptionsLength, IN PVOID Options, OUT CONNECTION_CONTEXT *ConnectionContext, OUT PIRP *AcceptIrp )
{ PLINK_OBJECT LinkObject=(PLINK_OBJECT)TdiEventContext; PIRP Irp; PDEVICE_OBJECT DeviceObject=IoGetRelatedDeviceObject ( LinkObject->TdiObjects->ConnectionFileObject); KIRQL OldIrql;
Irp = IoAllocateIrp((CCHAR)(DeviceObject->StackSize), FALSE); if ( Irp == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES; }
KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
if ((LinkObject->Connection.State != LINK_IDLE) || LinkObject->Closing) {
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
IoFreeIrp(Irp);
return STATUS_CONNECTION_REFUSED; }
LinkObject->Connection.State=LINK_ACCEPTED;
//
// we now have a connection starting, in the refcount
//
InterlockedIncrement(&LinkObject->Connection.ReferenceCount);
//
// the connection counts agains the link
//
InterlockedIncrement(&LinkObject->ReferenceCount);
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
TdiBuildAccept( Irp, DeviceObject, LinkObject->TdiObjects->ConnectionFileObject, IrdaCompleteAcceptIrp, LinkObject, NULL, // request connection information
NULL // return connection information
); IoSetNextIrpStackLocation(Irp);
//
// Set the return IRP so the transport processes this accept IRP.
//
*AcceptIrp = Irp;
//
// Set up the connection context as a pointer to the connection block
// we're going to use for this connect request. This allows the
// TDI provider to which connection object to use.
//
*ConnectionContext = (CONNECTION_CONTEXT) LinkObject; return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS IrdaCompleteAcceptIrp( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context )
{ PLINK_OBJECT LinkObject=(PLINK_OBJECT)Context; KIRQL OldIrql;
KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
if (NT_SUCCESS(Irp->IoStatus.Status)) {
LinkObject->Connection.State=LINK_PRE_CONNECT;
ExQueueWorkItem( &LinkObject->WorkItem, CriticalWorkQueue );
} else {
LinkObject->Connection.State=LINK_ACCEPT_FAILED; }
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
if (!NT_SUCCESS(Irp->IoStatus.Status)) { //
// no connection anymore
//
RemoveReferenceFromConnection(LinkObject); }
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS GetMaxSendPdu( PFILE_OBJECT FileObject, PULONG MaxPdu )
{
PIRP Irp; IO_STATUS_BLOCK IoStatus; KEVENT Event;
*MaxPdu=50;
KeInitializeEvent( &Event, NotificationEvent, FALSE );
Irp=IoBuildDeviceIoControlRequest( IOCTL_IRDA_GET_SEND_PDU_LEN, IoGetRelatedDeviceObject(FileObject), NULL, 0, MaxPdu, sizeof(*MaxPdu), FALSE, &Event, &IoStatus );
if (Irp != NULL) {
PIO_STACK_LOCATION IrpSp=IoGetNextIrpStackLocation(Irp);
IrpSp->FileObject=FileObject;
IoCallDriver( IoGetRelatedDeviceObject(FileObject), Irp );
KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
DbgPrint("IRCOMM: maxsendpdu=%d\n",*MaxPdu);
return IoStatus.Status; }
return STATUS_INSUFFICIENT_RESOURCES;
}
VOID ConnectionPassiveWorkRoutine( PVOID Context )
{ PLINK_OBJECT LinkObject=Context; KIRQL OldIrql; ULONG MaxSendPdu=50; BOOLEAN Connected;
KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
switch (LinkObject->Connection.State) {
case LINK_PRE_CONNECT:
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
GetMaxSendPdu(LinkObject->TdiObjects->ConnectionFileObject,&MaxSendPdu);
LinkObject->Connection.SendBufferPool=CreateBufferPool( IoGetRelatedDeviceObject(LinkObject->TdiObjects->ConnectionFileObject)->StackSize, MaxSendPdu, LinkObject->SendBuffers );
LinkObject->Connection.ControlBufferPool=CreateBufferPool( IoGetRelatedDeviceObject(LinkObject->TdiObjects->ConnectionFileObject)->StackSize, MaxSendPdu, LinkObject->ControlBuffers );
LinkObject->Connection.ReceiveBufferPool=CreateBufferPool( IoGetRelatedDeviceObject(LinkObject->TdiObjects->ConnectionFileObject)->StackSize, 1, LinkObject->ReceiveBuffers );
LinkObject->Connection.State=LINK_CONNECTED;
Connected=TRUE;
break;
case LINK_DISCONNECTING:
Connected=FALSE;
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
IrdaDisconnect(LinkObject->TdiObjects->ConnectionFileObject);
if (LinkObject->Connection.SendBufferPool != NULL) {
FreeBufferPool(LinkObject->Connection.SendBufferPool); LinkObject->Connection.SendBufferPool=NULL; }
if (LinkObject->Connection.ControlBufferPool != NULL) {
FreeBufferPool(LinkObject->Connection.ControlBufferPool); LinkObject->Connection.ControlBufferPool=NULL; }
if (LinkObject->Connection.ReceiveBufferPool != NULL) {
FreeBufferPool(LinkObject->Connection.ReceiveBufferPool); LinkObject->Connection.ReceiveBufferPool=NULL; }
LinkObject->Connection.State=LINK_IDLE;
break;
case LINK_ACCEPT_FAILED:
Connected=FALSE; LinkObject->Connection.State=LINK_IDLE;
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
break;
default:
ASSERT(0); Connected=FALSE; KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
break;
}
if (!LinkObject->Closing) { //
// tell the client about the state change
//
InterlockedIncrement(&LinkObject->ReferenceCount);
(LinkObject->LinkStateHandler)( LinkObject->Context, Connected, MaxSendPdu ); RemoveReferenceOnLink(LinkObject); }
if (!Connected) { //
// we have completed the disconnection, remove the reference the connection
// has to the link
//
RemoveReferenceOnLink(LinkObject); }
return; }
VOID RemoveReferenceFromConnection( PLINK_OBJECT LinkObject )
{ KIRQL OldIrql; LONG Count;
KeAcquireSpinLock(&LinkObject->Lock,&OldIrql);
Count=InterlockedDecrement(&LinkObject->Connection.ReferenceCount);
if (Count == 0) {
ExQueueWorkItem( &LinkObject->WorkItem, CriticalWorkQueue );
}
KeReleaseSpinLock(&LinkObject->Lock,OldIrql);
return;
}
TDI_OBJECT_HANDLE OpenTdiObjects( const CHAR *ServiceName, BOOLEAN OutGoingConnection )
{
PTDI_OBJECTS TdiObject; NTSTATUS Status;
UCHAR AddrBuf[sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IRDA)];
PTRANSPORT_ADDRESS TranAddr = (PTRANSPORT_ADDRESS) AddrBuf; PTDI_ADDRESS_IRDA IrdaAddr = (PTDI_ADDRESS_IRDA) TranAddr->Address[0].Address;
TdiObject=ALLOCATE_NONPAGED_POOL(sizeof(*TdiObject));
if (TdiObject == NULL) {
return NULL; }
RtlZeroMemory(TdiObject,sizeof(*TdiObject));
//
// start the ref count at one
//
TdiObject->ReferenceCount=1;
#if DBG
TdiObject->OpenProcess=IoGetCurrentProcess(); #endif
KeInitializeEvent( &TdiObject->CloseEvent, NotificationEvent, FALSE );
if (OutGoingConnection) {
IrdaAddr->irdaServiceName[0] = 0; // tells irda.sys addrObj is a client
} else {
strcpy(IrdaAddr->irdaServiceName,ServiceName); }
//
// open the tdi address and get a handle
//
Status=IrdaCreateAddress( IrdaAddr, &TdiObject->AddressFileHandle );
if (!NT_SUCCESS(Status)) {
goto CleanUp; }
//
// get the file object the handle refers to
//
Status = ObReferenceObjectByHandle( TdiObject->AddressFileHandle, 0L, // DesiredAccess
NULL, KernelMode, (PVOID *)&TdiObject->AddressFileObject, NULL );
if (Status != STATUS_SUCCESS) {
D_ERROR(DbgPrint("IRCOMM: ObReferenceObjectByHandle Failed %08lx\n",Status);)
goto CleanUp; }
//
// create a connection object and associate it with the address
//
Status=IrdaCreateConnectionForAddress( TdiObject->AddressFileHandle, NULL, &TdiObject->ConnectionFileObject, &TdiObject->ConnectionFileHandle );
if (!NT_SUCCESS(Status)) {
D_ERROR(DbgPrint("IRCOMM: IrdaCreateConnectionForAddress Failed %08lx\n",Status);)
goto CleanUp; }
return TdiObject;
CleanUp:
CloseTdiObjects(TdiObject);
return NULL;
}
VOID AddRefTdiObjects( TDI_OBJECT_HANDLE Handle )
{
PTDI_OBJECTS TdiObject=(PTDI_OBJECTS)Handle;
ASSERT(TdiObject->ReferenceCount > 0);
InterlockedIncrement(&TdiObject->ReferenceCount);
return;
}
VOID RemoveRefTdiObjects( TDI_OBJECT_HANDLE Handle )
{
PTDI_OBJECTS TdiObject=(PTDI_OBJECTS)Handle; LONG NewRefCount;
NewRefCount=InterlockedDecrement(&TdiObject->ReferenceCount);
ASSERT(NewRefCount >= 0);
if (NewRefCount == 0) {
KeSetEvent(&TdiObject->CloseEvent,IO_NO_INCREMENT,FALSE); }
return;
}
PTDI_OBJECTS TdiObjectFromHandle( TDI_OBJECT_HANDLE Handle )
{ PTDI_OBJECTS TdiObject=(PTDI_OBJECTS)Handle;
AddRefTdiObjects(TdiObject);
return TdiObject; }
VOID CloseTdiObjects( TDI_OBJECT_HANDLE Handle )
{
PTDI_OBJECTS TdiObject=(PTDI_OBJECTS)Handle;
#if DBG
ASSERT(TdiObject->OpenProcess == IoGetCurrentProcess()); #endif
RemoveRefTdiObjects(Handle);
//
// when the ref count goes to zero the event will be signaled
//
KeWaitForSingleObject( &TdiObject->CloseEvent, Executive, KernelMode, FALSE, NULL );
if (TdiObject->AddressFileHandle != NULL) {
ZwClose(TdiObject->AddressFileHandle); TdiObject->AddressFileHandle=NULL; }
if (TdiObject->AddressFileObject != NULL) {
ObDereferenceObject(TdiObject->AddressFileObject); TdiObject->AddressFileObject=NULL; }
if (TdiObject->ConnectionFileHandle != NULL) {
ZwClose(TdiObject->ConnectionFileHandle); TdiObject->ConnectionFileHandle=NULL; }
if (TdiObject->ConnectionFileObject != NULL) {
ObDereferenceObject(TdiObject->ConnectionFileObject); TdiObject->ConnectionFileObject=NULL; }
RtlZeroMemory(TdiObject,sizeof(*TdiObject));
FREE_POOL(TdiObject);
return; }
|