windows-nt/Source/XPSP1/NT/net/irda/comm/vuart/tdi.c
2020-09-26 16:20:57 +08:00

559 lines
12 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
tdi.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"
VOID
RemoveRefereneToConnection(
PTDI_CONNECTION Connection
)
{
LONG Count;
Count=InterlockedDecrement(&Connection->ReferenceCount);
if (Count == 0) {
KeSetEvent(
&Connection->CloseEvent,
IO_NO_INCREMENT,
FALSE
);
}
return;
}
VOID
HandleControlInformation(
PTDI_CONNECTION Connection,
PUCHAR Buffer,
ULONG Length
)
{
PUCHAR Current=Buffer;
while (Current < Buffer+Length) {
UCHAR PI=*Current;
UCHAR PL=*(Current+1);
D_TRACE1(
DbgPrint("IRCOMM: Receive Control, PI=%x, PL=%d, PV= ",PI,PL);
DumpBuffer(Current+2,PL);
DbgPrint("\n");
)
if ((Connection->EventCallBack != NULL)
&&
((PI == PI_DTESettings) || (PI == PI_DCESettings))) {
UCHAR PV=*(Current+2);
UCHAR NewPV;
ULONG LineDelta=0;
if (PI == PI_DTESettings) {
//
// the other machine is a DTE as well. mundge the control lines
//
PI=PI_DCESettings;
NewPV = PV & PV_DTESetting_Delta_DTR ? PV_DCESetting_Delta_DSR : 0;
NewPV |= PV & PV_DTESetting_Delta_RTS ? PV_DCESetting_Delta_CTS : 0;
NewPV |= PV & PV_DTESetting_DTR_High ? PV_DCESetting_DSR_State : 0;
NewPV |= PV & PV_DTESetting_RTS_High ? PV_DCESetting_CTS_State : 0;
} else {
//
// the other device is a DCE, just report the value straight back
//
NewPV=PV;
}
//
// save the current state of the control line here
//
Connection->Uart.ModemStatus=NewPV & 0xf0;
if (NewPV & PV_DCESetting_Delta_CTS ) {
LineDelta |= SERIAL_EV_CTS;
}
if (NewPV & PV_DCESetting_Delta_DSR ) {
LineDelta |= SERIAL_EV_DSR;
}
if (NewPV & PV_DCESetting_Delta_RI ) {
LineDelta |= SERIAL_EV_RING;
}
if (NewPV & PV_DCESetting_Delta_CD ) {
LineDelta |= SERIAL_EV_RLSD;
}
(*Connection->EventCallBack)(
Connection->EventContext,
LineDelta
);
}
Current+=2+PL;
}
return;
}
NTSTATUS
LinkReceiveHandler(
PVOID Context,
ULONG ReceiveFlags,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *BytesTaken,
IN PVOID Tsdu,
OUT PIRP *IoRequestPacket
)
{
PTDI_CONNECTION Connection=Context;
PUCHAR Data=Tsdu;
NTSTATUS Status;
ULONG ClientDataUsed;
*IoRequestPacket=NULL;
*BytesTaken=BytesAvailable;
D_TRACE(DbgPrint("IRCOMM: receive event, ind=%d, Avail=%d\n",BytesIndicated,BytesAvailable);)
if (BytesIndicated < 1) {
//
// ircomm frames should at least have the control length byte
//
D_ERROR(DbgPrint("IRCOMM: ClientEventRecieve: less than one byte indicated\n");)
return STATUS_SUCCESS;
}
if ((ULONG)((*Data) + 1) > BytesIndicated) {
//
// The control information is larger than the whole frame
//
D_ERROR(DbgPrint("IRCOMM: ClientEventRecieve: control length more than frame length, %d > %d\n",(ULONG)((*Data) + 1) , BytesIndicated);)
return STATUS_SUCCESS;
}
if ((*Data > 0) && (*Data < 3)) {
//
// There is control data, but it is less than a minimal PI,PL, and a one byte PV
//
D_ERROR(DbgPrint("IRCOMM: ClientEventRecieve: Control data is less than 3 bytes\n");)
return STATUS_SUCCESS;
}
if (Connection->ReceiveCallBack != NULL) {
//
// indicate the packet to the client
//
ULONG ClientDataLength=(BytesIndicated-*Data)-1;
if (ClientDataLength > 0) {
Status=(*Connection->ReceiveCallBack)(
Connection->ReceiveContext,
Data+1+*Data,
ClientDataLength,
&ClientDataUsed
);
if (Status == STATUS_DATA_NOT_ACCEPTED) {
//
// the clients buffer is full, let the tdi driver buffer the data
//
*BytesTaken=0;
//
// return now, before processing any control info so it will only be done once
// when the client request more data
//
return Status;
}
ASSERT(Status == STATUS_SUCCESS);
}
}
//
// process the control data now
//
HandleControlInformation(Connection,Data+1,*Data);
return STATUS_SUCCESS;
}
VOID
LinkStateHandler(
PVOID Context,
BOOLEAN LinkUp,
ULONG MaxSendPdu
)
{
PTDI_CONNECTION Connection=Context;
D_ERROR(DbgPrint("IRCOMM: LinkState %d\n",LinkUp);)
Connection->LinkUp=LinkUp;
if (!LinkUp) {
//
// link down
//
if (Connection->EventCallBack != NULL) {
//
// indicate that CTS, DSR, and CD are now low.
//
ULONG LineDelta;
Connection->Uart.ModemStatus=0;
LineDelta = SERIAL_EV_CTS;
LineDelta |= SERIAL_EV_DSR;
LineDelta |= SERIAL_EV_RING;
LineDelta |= SERIAL_EV_RLSD;
(*Connection->EventCallBack)(
Connection->EventContext,
LineDelta
);
}
} else {
UCHAR ControlBuffer[4];
CONNECTION_HANDLE ConnectionHandle;
Connection->MaxSendPdu=MaxSendPdu;
ConnectionHandle=GetCurrentConnection(Connection->LinkHandle);
if (ConnectionHandle != NULL) {
ControlBuffer[0]=PV_ServiceType_9_Wire;
SendSynchronousControlInfo(
ConnectionHandle,
PI_ServiceType,
1,
ControlBuffer
);
ControlBuffer[0]=(UCHAR)( Connection->Uart.BaudRate >> 24);
ControlBuffer[1]=(UCHAR)( Connection->Uart.BaudRate >> 16);
ControlBuffer[2]=(UCHAR)( Connection->Uart.BaudRate >> 8);
ControlBuffer[3]=(UCHAR)( Connection->Uart.BaudRate >> 0);
SendSynchronousControlInfo(
ConnectionHandle,
PI_DataRate,
4,
ControlBuffer
);
ControlBuffer[0] = Connection->Uart.RtsState ? PV_DTESetting_RTS_High : 0;
ControlBuffer[0] |= Connection->Uart.DtrState ? PV_DTESetting_DTR_High : 0;
SendSynchronousControlInfo(
ConnectionHandle,
PI_DTESettings,
1,
ControlBuffer
);
ReleaseConnection(ConnectionHandle);
}
ProcessSendAtPassive(Connection);
}
return;
}
NTSTATUS
IrdaConnect(
ULONG DeviceAddress,
CHAR *ServiceName,
BOOLEAN OutGoingConnection,
IRDA_HANDLE *ConnectionHandle,
RECEIVE_CALLBACK ReceiveCallBack,
EVENT_CALLBACK EventCallBack,
PVOID CallbackContext
)
{
NTSTATUS Status=STATUS_SUCCESS;
PIRP pIrp;
KEVENT Event;
IO_STATUS_BLOCK Iosb;
TDI_CONNECTION_INFORMATION ConnInfo;
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;
PTDI_CONNECTION Connection=NULL;
*ConnectionHandle=NULL;
Connection=ALLOCATE_NONPAGED_POOL(sizeof(*Connection));
if (Connection == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(Connection,sizeof(*Connection));
KeInitializeSpinLock(&Connection->Send.ControlLock);
ExInitializeWorkItem(
&Connection->Send.WorkItem,
SendWorkItemRountine,
Connection
);
Connection->ReceiveContext=CallbackContext;
Connection->ReceiveCallBack=ReceiveCallBack;
Connection->EventContext=CallbackContext;
Connection->EventCallBack=EventCallBack;
Connection->Uart.BaudRate=115200;
Connection->Uart.DtrState=1;
Connection->Uart.RtsState=1;
Connection->Uart.LineControl.WordLength=8;
Connection->Uart.LineControl.StopBits=NO_PARITY;
Connection->Uart.LineControl.Parity=STOP_BIT_1;
Connection->Uart.ModemStatus=0;
Connection->ReferenceCount=1;
KeInitializeEvent(
&Connection->CloseEvent,
NotificationEvent,
FALSE
);
*ConnectionHandle=Connection;
Status=CreateTdiLink(
DeviceAddress,
ServiceName,
OutGoingConnection, //outgoing
&Connection->LinkHandle,
Connection,
LinkReceiveHandler,
LinkStateHandler,
7,
3,
3
);
if (!NT_SUCCESS(Status)) {
*ConnectionHandle=NULL;
goto CleanUp;
}
return Status;
CleanUp:
FreeConnection(Connection);
return Status;
}
VOID
FreeConnection(
IRDA_HANDLE Handle
)
{
PTDI_CONNECTION Connection=Handle;
RemoveRefereneToConnection(
Connection
);
//
// wait for recount to goto zero
//
KeWaitForSingleObject(
&Connection->CloseEvent,
Executive,
KernelMode,
FALSE,
NULL
);
if (Connection->LinkHandle != NULL) {
CloseTdiLink(Connection->LinkHandle);
}
FREE_POOL(Connection);
return;
}
NTSTATUS
ReceiveCompletion(
PDEVICE_OBJECT DeviceObject,
PIRP BufferIrp,
PVOID Context
)
{
PIRCOMM_BUFFER Buffer=Context;
PTDI_CONNECTION Connection=Buffer->Context;
D_ERROR(DbgPrint("IRCOMM: receive restart complete\n");)
Buffer->FreeBuffer(Buffer);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
IndicateReceiveBufferSpaceAvailible(
IRDA_HANDLE Handle
)
{
PTDI_CONNECTION Connection=Handle;
CONNECTION_HANDLE ConnectionHandle;
//
// we will send a receive irp with a zero length to irda,
// this will get it to start indicating packets again
//
ConnectionHandle=GetCurrentConnection(Connection->LinkHandle);
if (ConnectionHandle != NULL) {
//
// we have a good connection
//
PFILE_OBJECT FileObject;
PIRCOMM_BUFFER Buffer;
FileObject=ConnectionGetFileObject(ConnectionHandle);
Buffer=ConnectionGetBuffer(ConnectionHandle,BUFFER_TYPE_RECEIVE);
if (Buffer != NULL) {
PDEVICE_OBJECT IrdaDeviceObject=IoGetRelatedDeviceObject(FileObject);
ULONG Length=0;
IoReuseIrp(Buffer->Irp,STATUS_SUCCESS);
Buffer->Irp->Tail.Overlay.OriginalFileObject = FileObject;
Buffer->Context=Connection;
TdiBuildReceive(
Buffer->Irp,
IrdaDeviceObject,
FileObject,
ReceiveCompletion,
Buffer,
Buffer->Mdl,
0, // send flags
Length
);
IoCallDriver(IrdaDeviceObject, Buffer->Irp);
} else {
//
// we could not get a buffer, We preallocate 3 of these so this should not happen
// If there are not any availibe, then they should be in use telling irda we want
// packets as well
//
ASSERT(0);
}
ConnectionReleaseFileObject(ConnectionHandle,FileObject);
ReleaseConnection(ConnectionHandle);
}
return STATUS_SUCCESS;
}