windows-nt/Source/XPSP1/NT/net/irda/irsir/receive.c

1546 lines
41 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*****************************************************************************
*
* Copyright (c) 1996-1999 Microsoft Corporation
*
* @doc
* @module receive.c | IrSIR NDIS Miniport Driver
* @comm
*
*-----------------------------------------------------------------------------
*
* Author: Scott Holden (sholden)
*
* Date: 10/4/1996 (created)
*
* Contents:
*
*****************************************************************************/
#include "irsir.h"
VOID
SetSpeedCallback(
PIR_WORK_ITEM pWorkItem
);
#if LOGGING
ULONG LogIndex = 0;
LOG Log[NUM_LOG];
#endif
#ifdef DEBUG_IRSIR
static ULONG_PTR irpCount;
static ULONG_PTR bytesReceived;
#endif //DEBUG_IRSIR
//
// Declarations.
//
NTSTATUS SerialIoCompleteRead(
IN PDEVICE_OBJECT pSerialDevObj,
IN PIRP pIrp,
IN PVOID Context
);
NTSTATUS
SerialIoCompleteWait(
IN PDEVICE_OBJECT pSerialDevObj,
IN PIRP pIrp,
IN PVOID Context
);
NTSTATUS ProcessData(
IN PIR_DEVICE pThisDev,
IN PUCHAR rawBuffer,
IN UINT rawBytesRead
);
VOID
DeliverBuffer(
IN PIR_DEVICE pThisDev
);
VOID StartSerialReadCallback(PIR_WORK_ITEM pWorkItem);
#pragma alloc_text(PAGE,SetSpeedCallback)
#pragma alloc_text(PAGE,StartSerialReadCallback)
VOID DBG_PrintBuf(PUCHAR bufptr, UINT buflen)
{
UINT i, linei;
#define ISPRINT(ch) (((ch) >= ' ') && ((ch) <= '~'))
#define PRINTCHAR(ch) (UCHAR)(ISPRINT(ch) ? (ch) : '.')
DbgPrint("\r\n %d bytes @%x:", buflen, bufptr);
/*
* Print whole lines of 8 characters with HEX and ASCII
*/
for (i = 0; i+8 <= buflen; i += 8) {
UCHAR ch0 = bufptr[i+0],
ch1 = bufptr[i+1], ch2 = bufptr[i+2],
ch3 = bufptr[i+3], ch4 = bufptr[i+4],
ch5 = bufptr[i+5], ch6 = bufptr[i+6],
ch7 = bufptr[i+7];
DbgPrint("\r\n %02x %02x %02x %02x %02x %02x %02x %02x"
" %c %c %c %c %c %c %c %c",
ch0, ch1, ch2, ch3, ch4, ch5, ch6, ch7,
PRINTCHAR(ch0), PRINTCHAR(ch1),
PRINTCHAR(ch2), PRINTCHAR(ch3),
PRINTCHAR(ch4), PRINTCHAR(ch5),
PRINTCHAR(ch6), PRINTCHAR(ch7));
}
/*
* Print final incomplete line
*/
DbgPrint("\r\n ");
for (linei = 0; (linei < 8) && (i < buflen); i++, linei++){
DbgPrint(" %02x", (UINT)(bufptr[i]));
}
DbgPrint(" ");
i -= linei;
while (linei++ < 8) DbgPrint(" ");
for (linei = 0; (linei < 8) && (i < buflen); i++, linei++){
UCHAR ch = bufptr[i];
DbgPrint(" %c", PRINTCHAR(ch));
}
DbgPrint("\t\t<>\r\n");
}
NTSTATUS StartSerialRead(IN PIR_DEVICE pThisDev)
/*++
Routine Description:
Allocates an irp and calls the serial driver.
Arguments:
pThisDev - Current IR device.
Return Value:
STATUS_INSUFFICIENT_RESOURCES or result of IoCallDriver
--*/
{
NTSTATUS Status;
PIRP pIrp;
LOG_ENTRY('SR', pThisDev, 0, 0);
#if DBG
NdisZeroMemory(
pThisDev->pRcvIrpBuffer,
SERIAL_RECEIVE_BUFFER_LENGTH
);
#endif
//
// Now that we have processed the irp, we will send another read
// request to the serial device object.
//
pIrp = SerialBuildReadWriteIrp(
pThisDev->pSerialDevObj,
IRP_MJ_READ,
pThisDev->pRcvIrpBuffer,
SERIAL_RECEIVE_BUFFER_LENGTH,
NULL
);
if (pIrp == NULL)
{
DEBUGMSG(DBG_ERR, (" SerialBuildReadWriteIrp failed.\n"));
Status = STATUS_INSUFFICIENT_RESOURCES;
pThisDev->fReceiving = FALSE;
goto done;
}
//
// Set up the io completion routine for the irp.
//
IoSetCompletionRoutine(
pIrp, // irp to use
SerialIoCompleteRead, // routine to call when irp is done
DEV_TO_CONTEXT(pThisDev), // context to pass routine
TRUE, // call on success
TRUE, // call on error
TRUE); // call on cancel
//
// Call IoCallDriver to send the irp to the serial port.
//
LOG_ENTRY('2I', pThisDev, pIrp, 0);
IoCallDriver(
pThisDev->pSerialDevObj,
pIrp
);
Status=STATUS_PENDING;
done:
return Status;
}
VOID
StartSerialReadCallback(PIR_WORK_ITEM pWorkItem)
/*++
Routine Description:
Arguments:
Return Value:
none
--*/
{
PIR_DEVICE pThisDev = pWorkItem->pIrDevice;
FreeWorkItem(pWorkItem);
(void)StartSerialRead(pThisDev);
return;
}
/*****************************************************************************
*
* Function: InitializeReceive
*
* Synopsis: Initialize the receive functionality.
*
* Arguments: pThisDevice - pointer to current ir device object
*
* Returns: NDIS_STATUS_SUCCESS - if irp is successfully sent to serial
* device object
* NDIS_STATUS_RESOURCES - if mem can't be alloc'd
* NDIS_STATUS_FAILURE - otherwise
*
* Algorithm:
* 1) Set the receive timeout to READ_INTERVAL_TIMEOUT_MSEC.
* 2) Initialize our rcvInfo and associate info for our
* receive state machine.
* 3) Build an IRP_MJ_READ irp to send to the serial device
* object, and set the completion(or timeout) routine
* to SerialIoCompleteRead.
*
* History: dd-mm-yyyy Author Comment
* 10/4/1996 sholden author
*
* Notes:
*
* This routine must be called in IRQL PASSIVE_LEVEL.
*
*****************************************************************************/
NDIS_STATUS
InitializeReceive(
IN PIR_DEVICE pThisDev
)
{
PIRP pIrp;
PIO_STACK_LOCATION irpSp;
NDIS_STATUS status;
#if IRSIR_EVENT_DRIVEN
NTSTATUS NtStatus;
SERIAL_CHARS SerialChars;
#endif
DEBUGMSG(DBG_FUNC, ("+InitializeReceive\n"));
#ifdef DEBUG_IRSIR
irpCount = 0;
bytesReceived = 0;
#endif //DEBUG_IRSIR
//
// Set up the receive information for our read completion routine.
//
pThisDev->rcvInfo.rcvState = RCV_STATE_READY;
pThisDev->rcvInfo.rcvBufPos = 0;
if (pThisDev->rcvInfo.pRcvBuffer == NULL)
{
pThisDev->rcvInfo.pRcvBuffer =
(PRCV_BUFFER)MyInterlockedRemoveHeadList(
&(pThisDev->rcvFreeQueue),
&(pThisDev->rcvQueueSpinLock)
);
ASSERT(pThisDev->rcvInfo.pRcvBuffer != NULL);
}
#if IRSIR_EVENT_DRIVEN
NtStatus = (NDIS_STATUS) SerialSetTimeouts(pThisDev->pSerialDevObj,
&SerialTimeoutsActive);
NtStatus = SerialGetChars(pThisDev->pSerialDevObj, &SerialChars);
if (NtStatus!=STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, ("IRSIR: SerialGetChars failed (0x%x:%d)\n", NtStatus));
}
else
{
SerialChars.EventChar = SLOW_IR_EOF;
NtStatus = SerialSetChars(pThisDev->pSerialDevObj, &SerialChars);
}
if (NtStatus!=STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, ("IRSIR: SerialSetChars failed (0x%x:%d)\n", NtStatus));
}
else
{
ULONG WaitMask = SERIAL_EV_RXFLAG | SERIAL_EV_RX80FULL;
NtStatus = SerialSetWaitMask(pThisDev->pSerialDevObj, &WaitMask);
}
if (NtStatus!=STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, ("IRSIR: SerialSetWaitMask failed (0x%x:%d)\n", NtStatus));
}
else
{
if (InterlockedExchange(&pThisDev->fWaitPending, 1)==0)
{
NtStatus = SerialCallbackOnMask(pThisDev->pSerialDevObj,
SerialIoCompleteWait,
&pThisDev->WaitIosb,
DEV_TO_CONTEXT(pThisDev),
&pThisDev->MaskResult);
if (NtStatus==STATUS_PENDING)
{
NtStatus = STATUS_SUCCESS;
}
}
}
if (NtStatus!=STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, ("IRSIR: SerialCallbackOnMask failed (0x%x:%d)\n", NtStatus));
ASSERT(0);
}
status = NtStatus;
#else
pThisDev->fReceiving = TRUE;
(void)SerialSetTimeouts(pThisDev->pSerialDevObj,
&SerialTimeoutsIdle);
status = (NDIS_STATUS)StartSerialRead(pThisDev);
if ( (status != STATUS_SUCCESS) &&
(status != STATUS_PENDING) &&
(status != STATUS_TIMEOUT) )
{
DEBUGMSG(DBG_ERR, (" IoCallDriver failed. Returned 0x%.8x\n", status));
status = NDIS_STATUS_FAILURE;
pThisDev->fReceiving = FALSE;
goto error10;
}
//
// If IoCallDriver returned STATUS_PENDING, we were successful
// in sending the irp to the serial device object. This
// routine will return STATUS_SUCCESS.
//
if (status == NDIS_STATUS_PENDING)
{
status = NDIS_STATUS_SUCCESS;
}
//
// Set us into the receive state.
//
goto done;
error10:
#endif
done:
DEBUGMSG(DBG_FUNC, ("-InitializeReceive\n"));
return status;
}
VOID
SetSpeedCallback(
PIR_WORK_ITEM pWorkItem
)
{
PIR_DEVICE pThisDev = pWorkItem->pIrDevice;
NDIS_STATUS status;
BOOLEAN fSwitchSuccessful;
NDIS_HANDLE hSwitchToMiniport;
//
// Set speed of serial device object by request of
// IrsirSetInformation(OID_IRDA_LINK_SPEED).
//
DEBUGMSG(DBG_STAT, (" primPassive = PASSIVE_SET_SPEED\n"));
//
// The set speed event should not be set until a set
// speed is required.
//
ASSERT(pThisDev->fPendingSetSpeed == TRUE);
//
// Ensure that receives and sends have been stopped.
//
ASSERT(pThisDev->fReceiving == FALSE);
PausePacketProcessing(&pThisDev->SendPacketQueue,TRUE);
//
// We can perform the set speed now.
//
status = SetSpeed(pThisDev);
if (status != STATUS_SUCCESS)
{
DEBUGMSG(DBG_STAT, (" SetSpeed failed. Returned 0x%.8x\n", status));
}
ActivatePacketProcessing(&pThisDev->SendPacketQueue);
pThisDev->fPendingSetSpeed = FALSE;
{
NdisMSetInformationComplete(
pThisDev->hNdisAdapter,
(NDIS_STATUS)status
);
}
//
// NOTE: PassiveLevelThread is only signalled with primPassive
// equal to PASSIVE_SET_SPEED from the receive completion
// routine. After this thread is signalled, the receive
// completion routine is shut down...we need to start
// it up again.
//
status = InitializeReceive(pThisDev);
if (status != STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERROR, (" InitializeReceive failed = 0x%.8x\n", status));
}
FreeWorkItem(pWorkItem);
return;
}
/*****************************************************************************
*
* Function: SerialIoCompleteRead
*
* Synopsis:
*
* Arguments: pSerialDevObj - pointer to the serial device object which
* completed the irp
* pIrp - the irp which was completed by the serial device
* object
* Context - the context given to IoSetCompletionRoutine
* before calling IoCallDriver on the irp
* The Context is a pointer to the ir device object.
*
* Returns: STATUS_MORE_PROCESSING_REQUIRED - allows the completion routine
* (IofCompleteRequest) to stop working on the irp.
*
* Algorithm:
* This is the completion routine for all pending IRP_MJ_READ irps
* sent to the serial device object.
*
* If there is a pending halt or reset, we exit the completion
* routine without sending another irp to the serial device object.
*
* If there is a pending set speed, this function will wait for
* any pending sends to complete and then perform the set speed.
*
* If the IRP_MJ_READ irp returned either STATUS_SUCCESS or
* STATUS_TIMEOUT, we must process any data (stripping BOFs, ESC
* sequences, and EOF) into an NDIS_BUFFER and NDIS_PACKET.
*
* Another irp is then built (we just re-use the incoming irp) and
* sent to the serial device object with another IRP_MJ_READ
* request.
*
* History: dd-mm-yyyy Author Comment
* 10/5/1996 sholden author
*
* Notes:
*
* This routine is called (by the io manager) in IRQL DISPATCH_LEVEL.
*
*****************************************************************************/
NTSTATUS
SerialIoCompleteRead(
IN PDEVICE_OBJECT pSerialDevObj,
IN PIRP pIrp,
IN PVOID Context
)
{
PIR_DEVICE pThisDev;
BOOLEAN fSwitchSuccessful;
NDIS_HANDLE hSwitchToMiniport;
NTSTATUS status;
ULONG_PTR BytesRead;
BOOLEAN NewRead = TRUE;
// DEBUGMSG(DBG_FUNC, ("+SerialIoCompleteRead\n"));
//
// The context given to IoSetCompletionRoutine is simply the the ir
// device object pointer.
//
pThisDev = CONTEXT_TO_DEV(Context);
//
// Need to check if there is a pending halt or reset. If there is, we
// just leave the receive completion. Since we maintain one irp associated
// with the receive functionality, the irp will be deallocated in
// the ir device object deinitialization routine.
//
if ((pThisDev->fPendingHalt == TRUE) ||
(pThisDev->fPendingReset == TRUE))
{
//
// Set the fReceiving boolean so that the halt and reset routines
// know when it is okay to continue.
//
pThisDev->fReceiving = FALSE;
//
// Free the irp and associate memory...the rest will be
// freed in the halt or reset.
//
LOG_ENTRY('3i', pThisDev, pIrp, 0);
IoFreeIrp(pIrp);
goto done;
}
//
// Next we take care of any pending set speeds.
//
//
// This completion routine is running at IRQL DISPATCH_LEVEL. Therefore,
// we cannot make a synchronous call to the serial driver. Set an event
// to notify the PassiveLevelThread to perform the speed change. We will
// exit this without creating another irp to the serial device object.
// PassiveLevelThread will call InitializeReceive after the speed has
// been set.
//
if (pThisDev->fPendingSetSpeed == TRUE)
{
pThisDev->fReceiving = FALSE;
if (ScheduleWorkItem(PASSIVE_SET_SPEED, pThisDev,
SetSpeedCallback, NULL, 0) != NDIS_STATUS_SUCCESS)
{
status = NDIS_STATUS_SUCCESS;
}
else
{
status = NDIS_STATUS_PENDING;
}
LOG_ENTRY('4i', pThisDev, pIrp, 0);
IoFreeIrp(pIrp);
goto done;
}
//
// We have a number of cases:
// 1) The serial read timed out and we received no data.
// 2) The serial read timed out and we received some data.
// 3) The serial read was successful and fully filled our irp buffer.
// 4) The irp was cancelled.
// 5) Some other failure from the serial device object.
//
status = pIrp->IoStatus.Status;
BytesRead = pIrp->IoStatus.Information;
LOG_ENTRY('CR', pThisDev, BytesRead, 0);
switch (status)
{
case STATUS_SUCCESS:
case STATUS_TIMEOUT:
if (BytesRead > 0)
{
#ifdef DEBUG_IRSIR
//
// Count number of irps received with data. Count will be
// reset when delivering a buffer to the protocol.
//
irpCount++;
bytesReceived += pIrp->IoStatus.Information;
#endif //DEBUG_IRSIR
//
// Indicate that the next send should implement
// the min turnaround delay.
//
pThisDev->fRequireMinTurnAround = TRUE;
ProcessData(
pThisDev,
pThisDev->pRcvIrpBuffer,
(UINT) pIrp->IoStatus.Information
);
}
break; // STATUS_SUCCESS || STATUS_TIMEOUT
case STATUS_DELETE_PENDING:
NewRead = FALSE;
pThisDev->fReceiving = FALSE;
break;
case STATUS_CANCELLED:
//
// If our irp was cancelled, we just ignore and continue as if
// we processed data.
//
break;
case STATUS_PENDING:
case STATUS_UNSUCCESSFUL:
case STATUS_INSUFFICIENT_RESOURCES:
default:
ASSERT(FALSE);
break;
}
//
// Free the irp and reinit the buffer and status block.
//
LOG_ENTRY('5i', pThisDev, pIrp, 0);
IoFreeIrp(pIrp);
if (NewRead)
{
pThisDev->NumReads++;
if (InterlockedIncrement(&pThisDev->ReadRecurseLevel)>1)
{
if (ScheduleWorkItem(0, pThisDev, StartSerialReadCallback, 0, 0)!=NDIS_STATUS_SUCCESS)
{
DEBUGMSG(DBG_ERR, ("IRSIR:SerialIoCompleteRead: Timed out and couldn't reschedule read.\n"
" We're going down.\n"));
pThisDev->fReceiving = FALSE;
}
}
else
{
StartSerialRead(pThisDev);
}
InterlockedDecrement(&pThisDev->ReadRecurseLevel);
}
done:
// DEBUGMSG(DBG_FUNC, ("-SerialIoCompleteRead\n"));
//
// We return STATUS_MORE_PROCESSING_REQUIRED so that the completion
// routine (IofCompleteRequest) will stop working on the irp.
//
status = STATUS_MORE_PROCESSING_REQUIRED;
return status;
}
/*****************************************************************************
*
* Function: ProcessData
*
* Synopsis: State machine to process the input data by stripping BOFs, EOFs
* and ESC sequences in the data.
*
* Arguments: pThisDev - a pointer to the current ir device object
* rawBuffer - a pointer to the input data to process
* rawBytesRead - the number of bytes in rawBuffer
*
* Returns: STATUS_SUCCESS
*
* Algorithm:
*
* The state machine for receiving characters is as follows:
*
* -------------------------------------------------------------------
* | Event/State || READY | BOF | IN_ESC | RX |
* -------------------------------------------------------------------
* -------------------------------------------------------------------
* | || | | | |
* | char = BOF || state = | | reset | reset |
* | || BOF | | state = | state = |
* | || | | BOF | BOF |
* -------------------------------------------------------------------
* | || | | error | |
* | char = ESC || | state = | reset | state = |
* | || | IN_ESC | state = | IN_ESC |
* | || | | READY | |
* -------------------------------------------------------------------
* | || | | | if valid |
* | char = EOF || | state = | error | FCS { |
* | || | READY | reset | indicate |
* | || | | state = | data |
* | || | | READY | state = |
* | || | | | READY } |
* -------------------------------------------------------------------
* | || | | complement| |
* | char = || | state = | bit 6 of | data[] = |
* | || | RX | char | char |
* | || | | data[] = | |
* | || | data[] = | char | |
* | || | char | state = | |
* | || | | RX | |
* -------------------------------------------------------------------
*
* History: dd-mm-yyyy Author Comment
* 10/7/1996 sholden author
*
* Notes:
*
*
*****************************************************************************/
NTSTATUS
ProcessData(
IN PIR_DEVICE pThisDev,
IN PUCHAR rawBuffer,
IN UINT rawBytesRead
)
{
UINT rawBufPos;
UCHAR currentChar;
PUCHAR pReadBuffer;
NTSTATUS status;
#if DBG
int i = 0;
#endif //DBG
DEBUGMSG(DBG_FUNC, ("+ProcessData\n"));
DBGTIME("+ProcessData");
DEBUGMSG(DBG_OUT, (" Address: 0x%.8x, Length: %d\n", rawBuffer, rawBytesRead));
LOG_ENTRY('DP', pThisDev, rawBuffer, rawBytesRead);
status = STATUS_SUCCESS;
pReadBuffer = pThisDev->rcvInfo.pRcvBuffer->dataBuf;
//
// While there is data in the buffer which we have not processed.
//
//
// NOTE: We have to loop once more after getting MAX_RCV_DATA_SIZE bytes
// so that we can see the 'EOF'; hence the <= and not <.
// Also, to ensure that we don't overrun the buffer,
// RCV_BUFFER_SIZE = MAX_RCV_DATA_SIZE + 4;
//
for (
rawBufPos = 0;
(rawBufPos < rawBytesRead) && (pThisDev->rcvInfo.rcvBufPos <= MAX_RCV_DATA_SIZE);
rawBufPos++
)
{
#if DBG
i++;
if (i > 10000)
{
ASSERT(0);
}
#endif //DBG
currentChar = rawBuffer[rawBufPos];
switch (pThisDev->rcvInfo.rcvState)
{
case RCV_STATE_READY:
switch (currentChar)
{
case SLOW_IR_BOF:
pThisDev->rcvInfo.rcvState = RCV_STATE_BOF;
break;
case SLOW_IR_EOF:
case SLOW_IR_ESC:
default:
//
// Ignore this data.
//
break;
}
break; // RCV_STATE_READY
case RCV_STATE_BOF:
switch (currentChar)
{
case SLOW_IR_EOF:
pThisDev->rcvInfo.rcvState = RCV_STATE_READY;
pThisDev->rcvInfo.rcvBufPos = 0;
break;
case SLOW_IR_ESC:
pThisDev->rcvInfo.rcvState = RCV_STATE_IN_ESC;
pThisDev->rcvInfo.rcvBufPos = 0;
break;
case SLOW_IR_BOF:
//
// state = RCV_STATE_BOF
//
break;
default:
//
// We have data, copy the character into the buffer and
// change our state to RCV_STATE_RX.
//
pReadBuffer[0] = currentChar;
pThisDev->rcvInfo.rcvState = RCV_STATE_RX;
pThisDev->rcvInfo.rcvBufPos = 1;
break;
}
break; // RCV_STATE_BOF
case RCV_STATE_IN_ESC:
switch (currentChar)
{
//
// ESC + (ESC||EOF||BOF) is an abort sequence.
//
// If ESC + (ESC||EOF) then state = READY.
// If ESC + BOF then state = BOF.
//
case SLOW_IR_ESC:
case SLOW_IR_EOF:
pThisDev->rcvInfo.rcvState = RCV_STATE_READY;
pThisDev->rcvInfo.rcvBufPos = 0;
break;
case SLOW_IR_BOF:
pThisDev->rcvInfo.rcvState = RCV_STATE_BOF;
pThisDev->rcvInfo.rcvBufPos = 0;
break;
case SLOW_IR_BOF^SLOW_IR_ESC_COMP:
case SLOW_IR_ESC^SLOW_IR_ESC_COMP:
case SLOW_IR_EOF^SLOW_IR_ESC_COMP:
//
// Escape sequence for BOF, ESC or EOF chars.
//
//
// Fall through, do same as unnecessary escape
// sequence.
//
default:
//
// Unnecessary escape sequence, copy the data in to the buffer
// we must complement bit 6 of the data.
//
pReadBuffer[pThisDev->rcvInfo.rcvBufPos++] =
currentChar ^ SLOW_IR_ESC_COMP;
pThisDev->rcvInfo.rcvState = RCV_STATE_RX;
break;
}
break; // RCV_STATE_IN_ESC
case RCV_STATE_RX:
switch (currentChar)
{
case SLOW_IR_BOF:
//
// Reset.
//
pThisDev->rcvInfo.rcvState = RCV_STATE_BOF;
pThisDev->rcvInfo.rcvBufPos = 0;
break;
case SLOW_IR_ESC:
pThisDev->rcvInfo.rcvState = RCV_STATE_IN_ESC;
break;
case SLOW_IR_EOF:
if (pThisDev->rcvInfo.rcvBufPos <
(SLOW_IR_ADDR_SIZE + SLOW_IR_CONTROL_SIZE +
SLOW_IR_FCS_SIZE)
)
{
//
// Reset. Not enough data.
//
pThisDev->rcvInfo.rcvState = RCV_STATE_READY;
pThisDev->rcvInfo.rcvBufPos = 0;
break;
}
//
// Need to set the length to the proper amount.
// (It isn't rcvBufPos + 1 since it was incremented
// the next free location...which we are not using.)
//
pThisDev->rcvInfo.pRcvBuffer->dataLen =
pThisDev->rcvInfo.rcvBufPos;
DEBUGMSG(DBG_OUT, (" RcvBuffer = 0x%.8x, Length = %d\n",
pReadBuffer,
pThisDev->rcvInfo.rcvBufPos
));
//
// DeliverBuffer attempts to deliver the current
// frame in pThisDev->rcvInfo. If the ownership
// of the packet is retained by the protocol, the
// DeliverBuffer routine gives us a new receive
// buffer.
//
DeliverBuffer(
pThisDev
);
//
// Since DeliverBuffer could have given us a new
// buffer, we must update our pReadBuffer pointer.
//
pReadBuffer = pThisDev->rcvInfo.pRcvBuffer->dataBuf;
pThisDev->rcvInfo.rcvState = RCV_STATE_READY;
pThisDev->rcvInfo.rcvBufPos = 0;
break;
default:
//
// The current character is data in the frame.
//
pReadBuffer[pThisDev->rcvInfo.rcvBufPos++] =
currentChar;
break;
}
break; // RCV_STATE_RX
default:
DEBUGMSG(DBG_ERR, (" Illegal state\n"));
//
// Reset.
//
pThisDev->rcvInfo.rcvState = RCV_STATE_READY;
pThisDev->rcvInfo.rcvBufPos = 0;
break;
}
}
//
// There are two ways to break the for loop:
// 1) out of data - this is fine
// 2) overrun, the frame is larger than our buffer size
//
if (pThisDev->rcvInfo.rcvBufPos > MAX_RCV_DATA_SIZE)
{
DEBUGMSG(DBG_WARN, (" Overrun in ProcessData!!!\n"));
//
// Reset the buffer for our next read.
//
pThisDev->rcvInfo.rcvState = RCV_STATE_READY;
pThisDev->rcvInfo.rcvBufPos = 0;
pThisDev->packetsReceivedOverflow++;
}
DEBUGMSG(DBG_FUNC, ("-ProcessData\n"));
return status;
}
VOID
ProcessReturnPacket(
PIR_DEVICE pThisDev,
PRCV_BUFFER pRcvBuffer
)
{
PNDIS_BUFFER pBuffer;
NdisQueryPacket(
pRcvBuffer->packet,
NULL, // physical buffer count, don't care
NULL, // buffer count, don't care, we know it is 1
&pBuffer, // get a pointer to our buffer
NULL // total packet lenght, don't care
);
//
// We adjusted the buffer length of the NDIS_BUFFER to the size
// of the data before we gave ownership to the protocol. Now we
// should reset the buffer length to the full size of the data
// buffer.
//
NdisAdjustBufferLength(
pBuffer,
RCV_BUFFER_SIZE
);
#if DBG
NdisZeroMemory(
pRcvBuffer->dataBuf,
RCV_BUFFER_SIZE
);
#endif
pRcvBuffer->dataLen = 0;
InterlockedDecrement(&pThisDev->packetsHeldByProtocol);
//
// Add the buffer to the free queue.
//
MyInterlockedInsertTailList(
&(pThisDev->rcvFreeQueue),
&pRcvBuffer->linkage,
&(pThisDev->rcvQueueSpinLock)
);
}
/*****************************************************************************
*
* Function: DeliverBuffer
*
* Synopsis: Delivers the buffer to the protocol via
* NdisMIndicateReceivePacket.
*
* Arguments: pThisDev - pointer to the current ir device object
*
* Returns: STATUS_SUCCESS - on success
* STATUS_UNSUCCESSFUL - if packet can't be delivered to protocol
*
* Algorithm:
*
* History: dd-mm-yyyy Author Comment
* 10/7/1996 sholden author
*
* Notes:
*
*
*****************************************************************************/
VOID
DeliverBuffer(
IN PIR_DEVICE pThisDev
)
{
SLOW_IR_FCS_TYPE fcs;
PNDIS_BUFFER pBuffer;
BOOLEAN fProcessPacketNow;
NDIS_HANDLE hSwitchToMiniport;
NTSTATUS status;
PRCV_BUFFER pThisBuffer, pNextBuffer;
DEBUGMSG(DBG_FUNC, ("+DeliverBuffer\n"));
LOG_ENTRY('BD', pThisDev,
pThisDev->rcvInfo.pRcvBuffer->dataBuf,
pThisDev->rcvInfo.pRcvBuffer->dataLen);
#if 0
LOG_ENTRY('DD',
((PULONG)pThisDev->rcvInfo.pRcvBuffer->dataBuf)[0],
((PULONG)pThisDev->rcvInfo.pRcvBuffer->dataBuf)[1],
((PULONG)pThisDev->rcvInfo.pRcvBuffer->dataBuf)[2]);
#endif
#ifdef DEBUG_IRSIR
//
// This is the count of how many irps with data to get this frame.
//
DEBUGMSG(DBG_STAT, ("****IrpCount = %d, Bytes = %d, Frame Length = %d\n",
irpCount, bytesReceived, pThisDev->rcvInfo.pRcvBuffer->dataLen));
irpCount = 0;
bytesReceived = 0;
#endif //DEBUG_IRSIR
pNextBuffer = (PRCV_BUFFER)MyInterlockedRemoveHeadList(
&(pThisDev->rcvFreeQueue),
&(pThisDev->rcvQueueSpinLock)
);
//
// Compute the FCS.
//
fcs = ComputeFCS(
pThisDev->rcvInfo.pRcvBuffer->dataBuf,
pThisDev->rcvInfo.pRcvBuffer->dataLen
);
if (fcs != GOOD_FCS || !pNextBuffer)
{
//
// Bad frame, just drop it and increment our dropped packets
// count.
//
pThisDev->packetsReceivedDropped++;
#if DBG
if (fcs != GOOD_FCS)
{
LOG_ENTRY('CF', pThisDev, 0, 0);
DEBUGMSG(DBG_STAT|DBG_WARN, (" FCS ERR Len(%d)\n", pThisDev->rcvInfo.pRcvBuffer->dataLen));
}
if (!pNextBuffer)
{
LOG_ENTRY('BI', pThisDev, 0, 0);
DEBUGMSG(DBG_STAT|DBG_WARN, (" Dropped packet due to insufficient buffers\n"));
}
#endif
#if 0
DBG_PrintBuf(pThisDev->rcvInfo.pRcvBuffer->dataBuf,
pThisDev->rcvInfo.pRcvBuffer->dataLen);
#endif
status = STATUS_UNSUCCESSFUL;
NdisZeroMemory(
pThisDev->rcvInfo.pRcvBuffer->dataBuf,
RCV_BUFFER_SIZE
);
pThisDev->rcvInfo.pRcvBuffer->dataLen = 0;
if (pNextBuffer)
{
MyInterlockedInsertHeadList(
&(pThisDev->rcvFreeQueue),
&pNextBuffer->linkage,
&(pThisDev->rcvQueueSpinLock)
);
}
goto done;
}
LOG_ENTRY('HF', pThisDev, 0, 0);
//
// Remove fcs from the end of the packet.
//
pThisDev->rcvInfo.pRcvBuffer->dataLen -= SLOW_IR_FCS_SIZE;
//
// Fix up some other packet fields.
//
NDIS_SET_PACKET_HEADER_SIZE(
pThisDev->rcvInfo.pRcvBuffer->packet,
SLOW_IR_ADDR_SIZE + SLOW_IR_CONTROL_SIZE
);
//
// We need to call NdisQueryPacket to get a pointer to the
// NDIS_BUFFER so that we can adjust the buffer length
// to the actual size of the data and not the size
// of the buffer.
//
// NdisQueryPacket will return other information, but since
// we built the packet ourselves, we already know that info.
//
NdisQueryPacket(
pThisDev->rcvInfo.pRcvBuffer->packet,
NULL, // physical buffer count, don't care
NULL, // buffer count, don't care, we know it is 1
&pBuffer, // get a pointer to our buffer
NULL // total packet lenght, don't care
);
NdisAdjustBufferLength(
pBuffer,
pThisDev->rcvInfo.pRcvBuffer->dataLen
);
//
// Set to use the new buffer before we indicate the packet.
//
pThisBuffer = pThisDev->rcvInfo.pRcvBuffer;
pThisDev->rcvInfo.pRcvBuffer = pNextBuffer;
ASSERT(pThisDev->rcvInfo.pRcvBuffer != NULL);
//
// Indicate the packet to NDIS.
//
InterlockedIncrement(&pThisDev->packetsHeldByProtocol);
NdisMIndicateReceivePacket(
pThisDev->hNdisAdapter,
&pThisBuffer->packet,
1
);
done:
DEBUGMSG(DBG_FUNC, ("-DeliverBuffer\n"));
return;
}
/*****************************************************************************
*
* Function: IrsirReturnPacket
*
* Synopsis: The protocol returns ownership of a receive packet to
* the ir device object.
*
* Arguments: Context - a pointer to the current ir device obect.
* pReturnedPacket - a pointer the packet which the protocol
* is returning ownership.
*
* Returns: None.
*
* Algorithm:
* 1) Take the receive buffer off of the pending queue.
* 2) Put the receive buffer back on the free queue.
*
* History: dd-mm-yyyy Author Comment
* 10/8/1996 sholden author
*
* Notes:
*
*
*****************************************************************************/
VOID
IrsirReturnPacket(
IN NDIS_HANDLE Context,
IN PNDIS_PACKET pReturnedPacket
)
{
PIR_DEVICE pThisDev;
PNDIS_BUFFER pBuffer;
PRCV_BUFFER pRcvBuffer;
PLIST_ENTRY pTmpListEntry;
DEBUGMSG(DBG_FUNC, ("+IrsirReturnPacket\n"));
//
// The context is just the pointer to the current ir device object.
//
pThisDev = CONTEXT_TO_DEV(Context);
pThisDev->packetsReceived++;
{
PPACKET_RESERVED_BLOCK PacketReserved;
PacketReserved=(PPACKET_RESERVED_BLOCK)&pReturnedPacket->MiniportReservedEx[0];
pRcvBuffer=PacketReserved->Context;
}
ProcessReturnPacket(pThisDev, pRcvBuffer);
DEBUGMSG(DBG_FUNC, ("-IrsirReturnPacket\n"));
return;
}
VOID
SerialWaitCallback(PIR_WORK_ITEM pWorkItem)
/*++
Routine Description:
Arguments:
Return Value:
none
--*/
{
PIR_DEVICE pThisDev = pWorkItem->pIrDevice;
NTSTATUS Status;
ULONG BytesRead;
FreeWorkItem(pWorkItem);
do
{
SerialSynchronousRead(pThisDev->pSerialDevObj,
pThisDev->pRcvIrpBuffer,
SERIAL_RECEIVE_BUFFER_LENGTH,
&BytesRead);
if (BytesRead>0)
{
ProcessData(pThisDev, pThisDev->pRcvIrpBuffer, BytesRead);
}
} while ( BytesRead == SERIAL_RECEIVE_BUFFER_LENGTH );
if (InterlockedExchange(&pThisDev->fWaitPending, 1)==0)
{
LARGE_INTEGER Time;
KeQuerySystemTime(&Time);
LOG_ENTRY('WS', pThisDev, Time.LowPart/10000, Time.HighPart);
Status = SerialCallbackOnMask(pThisDev->pSerialDevObj,
SerialIoCompleteWait,
&pThisDev->WaitIosb,
DEV_TO_CONTEXT(pThisDev),
&pThisDev->MaskResult);
if (Status!=STATUS_SUCCESS && Status!=STATUS_PENDING)
{
DEBUGMSG(DBG_ERROR, ("IRSIR: SerialCallbackOnMask failed (0x%x:%d)\n", Status));
ASSERT(0);
}
}
return;
}
NTSTATUS
SerialIoCompleteWait(
IN PDEVICE_OBJECT pSerialDevObj,
IN PIRP pIrp,
IN PVOID Context
)
{
PIR_DEVICE pThisDev;
BOOLEAN fSwitchSuccessful;
NDIS_HANDLE hSwitchToMiniport;
NTSTATUS status = STATUS_SUCCESS;
ULONG BytesRead;
ULONG WaitWasPending;
DEBUGMSG(DBG_FUNC, ("+SerialIoCompleteWait\n"));
//
// The context given to IoSetCompletionRoutine is simply the the ir
// device object pointer.
//
pThisDev = CONTEXT_TO_DEV(Context);
WaitWasPending = InterlockedExchange(&pThisDev->fWaitPending, 0);
ASSERT(WaitWasPending);
*pIrp->UserIosb = pIrp->IoStatus;
LOG_ENTRY('1i', pThisDev, pIrp, 0);
IoFreeIrp(pIrp);
//
// Need to check if there is a pending halt or reset. If there is, we
// just leave the receive completion. Since we maintain one irp associated
// with the receive functionality, the irp will be deallocated in
// the ir device object deinitialization routine.
//
if ((pThisDev->fPendingHalt == TRUE) ||
(pThisDev->fPendingReset == TRUE))
{
//
// Set the fReceiving boolean so that the halt and reset routines
// know when it is okay to continue.
//
pThisDev->fReceiving = FALSE;
//
// Free the irp and associate memory...the rest will be
// freed in the halt or reset.
//
goto done;
}
//
// Next we take care of any pending set speeds.
//
//
// This completion routine is running at IRQL DISPATCH_LEVEL. Therefore,
// we cannot make a synchronous call to the serial driver. Set an event
// to notify the PassiveLevelThread to perform the speed change. We will
// exit this without creating another irp to the serial device object.
// PassiveLevelThread will call InitializeReceive after the speed has
// been set.
//
if (pThisDev->fPendingSetSpeed == TRUE)
{
pThisDev->fReceiving = FALSE;
goto done;
}
//
// Free the irp and reinit the buffer and status block.
//
{
LARGE_INTEGER Time;
KeQuerySystemTime(&Time);
LOG_ENTRY('ES', pThisDev, Time.LowPart/10000, Time.HighPart);
}
if (ScheduleWorkItem(0, pThisDev, SerialWaitCallback,
(PVOID)0, 0)!=NDIS_STATUS_SUCCESS
)
{
DEBUGMSG(DBG_ERR, ("IRSIR:SerialIoCompleteWait: Timed out and couldn't reschedule Wait.\n"
" We're going down.\n"));
}
done:
DEBUGMSG(DBG_FUNC, ("-SerialIoCompleteWait\n"));
//
// We return STATUS_MORE_PROCESSING_REQUIRED so that the completion
// routine (IofCompleteRequest) will stop working on the irp.
//
status = STATUS_MORE_PROCESSING_REQUIRED;
return status;
}