/*++ 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; }