2006 lines
44 KiB
C
2006 lines
44 KiB
C
/*++
|
|
|
|
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;
|
|
}
|