/*****************************************************************************
*
*  Copyright (c) 1996-1999 Microsoft Corporation
*
*     @doc
*     @module   irsir.h | IrSIR NDIS Miniport Driver
*     @comm
*
*-----------------------------------------------------------------------------
*
*     Author:   Scott Holden (sholden)
*
*     Date:     10/1/1996 (created)
*
*     Contents:
*
*****************************************************************************/

#ifndef _IRSIR_H
#define _IRSIR_H

#define IRSIR_EVENT_DRIVEN 0
//
// BINARY_COMPATIBLE = 0 is required so that we can include both
// ntos.h and ndis.h (it is a flag in ndis.h). I think that it
// is a flag to be binary compatible with Win95; however, since
// we are using I/O manager we won't be.
//

#define BINARY_COMPATIBLE 0

#include <ntosp.h>
#include <zwapi.h>
#include <ndis.h>
#include <ntddndis.h>  // defines OID's
#include <ntddser.h>   // defines structs to access serial info

#include "debug.h"
#include "ioctl.h"
#include "settings.h"
#include "queue.h"

//
// NDIS version compatibility.
//

#define NDIS_MAJOR_VERSION 5
#define NDIS_MINOR_VERSION 0

//
// Wrapper to NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO.
//

PNDIS_IRDA_PACKET_INFO static __inline GetPacketInfo(PNDIS_PACKET packet)
{
    MEDIA_SPECIFIC_INFORMATION *mediaInfo;
    UINT size;
    NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(packet, &mediaInfo, &size);
    return (PNDIS_IRDA_PACKET_INFO)mediaInfo->ClassInformation;
}

//
// Structure to keep track of receive packets and buffers to indicate
// receive data to the protocol.
//

typedef struct
{
    LIST_ENTRY        linkage;
    PNDIS_PACKET      packet;
    UINT              dataLen;
    PUCHAR            dataBuf;
} RCV_BUFFER, *PRCV_BUFFER;

//
// States for receive finite state machine.
//

typedef enum _RCV_PROCESS_STATE
{
    RCV_STATE_READY = 0,
    RCV_STATE_BOF,
    RCV_STATE_EOF,
    RCV_STATE_IN_ESC,
    RCV_STATE_RX
} RCV_PROCESS_STATE;

//
// Structure to keep track of current state and information
// of the receive state machine.
//

typedef struct _RCV_INFORMATION
{
    RCV_PROCESS_STATE rcvState;
    UINT              rcvBufPos;
    PRCV_BUFFER       pRcvBuffer;
}RCV_INFORMATION, *PRCV_INFORMATION;

//
// Serial receive buffer size???
//

#define SERIAL_RECEIVE_BUFFER_LENGTH  2048

//
// Serial timeouts to use.
//
// Keep the write timeouts same as the default.
//
// When the interval = MAXULONG, the timeouts behave as follows:
// 1) If both constant and multiplier are 0, then the serial device
//    object returns immediately with whatever it has...even if
//    it is nothing.
// 2) If constant and multiplier are not MAXULONG, then the serial device
//    object returns immediately if any characters are present. If nothing
//    is there then the device object uses the timeouts as specified.
// 3) If multiplier is MAXULONG, then the serial device object returns
//    immediately if any characters are present. If nothing is there, the
//    device object will return the first character that arrives or wait
//    for the specified timeout and return nothing.
//

#define SERIAL_READ_INTERVAL_TIMEOUT            MAXULONG
#define SERIAL_READ_TOTAL_TIMEOUT_MULTIPLIER    0
#define SERIAL_READ_TOTAL_TIMEOUT_CONSTANT      10
#define SERIAL_WRITE_TOTAL_TIMEOUT_MULTIPLIER   0
#define SERIAL_WRITE_TOTAL_TIMEOUT_CONSTANT     0

extern SERIAL_TIMEOUTS SerialTimeoutsInit;
extern SERIAL_TIMEOUTS SerialTimeoutsIdle;
extern SERIAL_TIMEOUTS SerialTimeoutsActive;

//
// Maximum size of the name of the serial device object.
//

#define MAX_SERIAL_NAME_SIZE 100


//
// Enumeration of primitives for the PASSIVE_LEVEL thread.
//

typedef enum _PASSIVE_PRIMITIVE
{
    PASSIVE_SET_SPEED = 1,
    PASSIVE_RESET_DEVICE,
    PASSIVE_QUERY_MEDIA_BUSY,
    PASSIVE_CLEAR_MEDIA_BUSY,
    PASSIVE_HALT
}PASSIVE_PRIMITIVE;

typedef struct _IR_DEVICE
{
    //
    // Allows the ir device object to be put on a queue.
    //

    LIST_ENTRY linkage;

    //
    // Keep track of the serial port name and device.
    //

    UNICODE_STRING  serialDosName;
    UNICODE_STRING  serialDevName;
    PDEVICE_OBJECT  pSerialDevObj;
    HANDLE          serialHandle;
    PFILE_OBJECT    pSerialFileObj;

    //
    // This is the handle that the NDIS wrapper associates with a connection.
    // (The handle that the miniport driver associates with the connection
    // is just an index into the devStates array).
    //

    NDIS_HANDLE hNdisAdapter;

    //
    // The dongle interface allows us to check the tranceiver type once
    // and then set up the interface to allow us to init, set speed,
    // and deinit the dongle.
    //
    // We also want the dongle capabilities.
    //

    IR_TRANSCEIVER_TYPE transceiverType;
    DONGLE_INTERFACE    dongle;
    DONGLE_CAPABILITIES dongleCaps;

    ULONG               AllowedSpeedsMask;


    //
    // NDIS calls most of the MiniportXxx function with IRQL DISPATCH_LEVEL.
    // There are a number of instances where the ir device must send
    // requests to the serial device which may not be synchronous and
    // we can't block in DISPATCH_LEVEL. Therefore, we set up a thread to deal
    // with request which require PASSIVE_LEVEL. An event is used to signal
    // the thread that work is required.
    //

    LIST_ENTRY        leWorkItems;
    NDIS_SPIN_LOCK    slWorkItem;
    HANDLE            hPassiveThread;
    KEVENT            eventPassiveThread;
    KEVENT            eventKillThread;

    //
    // Current speed setting, in bits/sec.
    // Note: This is updated when we ACTUALLY change the speed,
    //       not when we get the request to change speed via
    //       IrsirSetInformation.
    //

    UINT currentSpeed;

    //
    // Current link speed information. This also will maintain the
    // chosen speed if the protocol requests a speed change.
    //

    baudRateInfo *linkSpeedInfo;

    //
    // Maintain statistical debug info.
    //

    UINT packetsReceived;
    UINT packetsReceivedDropped;
    UINT packetsReceivedOverflow;
    UINT packetsSent;
    UINT packetsSentDropped;
    ULONG packetsHeldByProtocol;

    //
    // Indicates that we have received an OID_GEN_CURRENT_PACKET_FILTER
    // indication from the protocol. We can deliver received packets to the
    // protocol.
    //

    BOOLEAN fGotFilterIndication;

    //
    // The variable fMediaBusy is set TRUE any time that this miniport
    // driver receives a data frame. It can be reset by the protocol via
    // IrsirSetInformation and later checked via IrsirQueryInformation
    // to detect interleaving activity.
    //
    // In order to check for framing errors, when the protocol calls
    // IrsirSetInformation(OID_IRDA_MEDIA_BUSY), the miniport
    // sends an irp to the serial device object to clear the performance
    // statistics. When the protocol calls
    // IrsirQueryInformation(OID_IRDA_MEDIA_BUSY), if the miniport
    // hasn't sensed the media busy, the miniport will query the
    // serial device object for the performance statistics to check
    // for media busy.
    //
    // A spin lock is used to interleave access to fMediaBusy variable.
    //

    BOOLEAN         fMediaBusy;
    NDIS_SPIN_LOCK  mediaBusySpinLock;

    //
    // The variable fReceiving is used to indicate that the ir device
    // object in pending a receive from the serial device object. Note,
    // that this does NOT necessarily mean that any data is being
    // received from the serial device object, since we are constantly
    // polling the serial device object for data.
    //
    // Under normal circumstances fReceiving should always be TRUE.
    // However, when IrsirHalt or IrsirReset are called, the receive
    // has to be shut down and this variable is used to synchronize
    // the halt and reset handler.
    //

    BOOLEAN fReceiving;


    //
    // The variable fRequireMinTurnAround indicates whether a time
    // delay is required between the last byte of the last byte
    // of the last frame sent by ANOTHER station, and the point
    // at which it (the other station) is ready to receive the
    // first byte from THIS station.
    //
    // This variable is initially set to TRUE. Whenever this variable
    // is true and a send occurs, the a delay will be implemented by
    // a stall execution before the irp is sent to the serial
    // device object.
    //
    // After a transmission occurs with the min turnaround delay, this
    // variable is set to FALSE. Everytime data is received, the
    // variable is set to TRUE.
    //

    BOOLEAN fRequireMinTurnAround;

    //
    // The variable fPendingSetSpeed allows the receive completion routine
    // to check if the a set speed is required.
    //

    BOOLEAN fPendingSetSpeed;

    //
    // The variableis fPendingHalt/fPendingReset allows the send and receive
    // completion routines to complete the current pending irp and
    // then cleanup and stop sending irps to the serial driver.
    //

    BOOLEAN fPendingHalt;
    BOOLEAN fPendingReset;

    //
    // We keep an array of receive buffers so that we don't continually
    // need to allocate buffers to indicate packets to the protocol.
    // Since the protocol can retain ownership of up to eight packets
    // and we can be receiving some data while the protocol has
    // ownership of eight packets, we will allocate nine packets for
    // receiving.
    //

    #define NUM_RCV_BUFS 14

    RCV_BUFFER rcvBufs[NUM_RCV_BUFS];

    //
    // Handles to the NDIS packet pool and NDIS buffer pool
    // for allocating the receive buffers.
    //

    NDIS_HANDLE hPacketPool;
    NDIS_HANDLE hBufferPool;

    //
    // When we indicate a packet to the protocol, the protocol may
    // retain ownership until at some point (asynchronously), it calls
    // IrsirReturnPacket.  No assumption is made about packet order.
    //
    // Therefore, we maintain a free queue and a pending queue of
    // receive buffers. Originally, all nine buffers are put on the
    // free queue. When data is being received, a receive buffer is
    // maintained in the RCV_INFORMATION described below. After we
    // indicate a full packet to the protocol and if the protocol
    // retains ownership of the packet, the receive buffer is queued
    // on the pending queue until IrsirReturnPacket is called.
    //
    // A spin lock is used to interleave access to both the free and
    // pending queues. There are three routines which use the
    // receive queues: InitializeReceive, SerialIoCompleteRead, and
    // IrsirReturnPacket.
    //

    LIST_ENTRY     rcvFreeQueue;
    NDIS_SPIN_LOCK rcvQueueSpinLock;

    //
    // The rcvInfo object, allows the device to keep track of the
    // current receive buffer, the state of the finite state machine,
    // and the write position in the buffer.
    //

    RCV_INFORMATION rcvInfo;

    //
    // The send spin lock is used for both inserting and removing from the
    // send queue as well as checking and modifying the variable fSending.
    //

    //
    // Since we only want to send one packet at a time to the serial driver,
    // we need to queue other packets until each preceding send packet
    // has been completed by the serial device object.
    //
    // Therefore, we maintain a queue of send packets (if required).
    // The MiniportReserved element of the NDIS_PACKET is used as the
    // 'next' pointer. We keep a pointer to both the head and the
    // tail of the list to speed access to the queue.
    //

    PACKET_QUEUE    SendPacketQueue;
    //
    // We will allocate irp buffers both send
    // and receive irps only once.
    //

    PUCHAR pSendIrpBuffer;

    PUCHAR pRcvIrpBuffer;

    // irp buffers and io status block for WaitOnMask

    ULONG MaskResult;
    IO_STATUS_BLOCK WaitIosb;

    // We use the following flag to indicate that a Wait has been issued
    // for the End of Frame character (0xc1).  It's a ULONG because we
    // access it using InterlockedExchange()

    ULONG fWaitPending;

    PVOID pQueryInfoBuffer;

    BOOLEAN SerialBased;

    PVOID PnpNotificationEntry;


    // We do some timeout modulation during activity
    ULONG NumReads;
    ULONG ReadRecurseLevel;

}IR_DEVICE, *PIR_DEVICE;


VOID
SendPacketToSerial(
    PVOID           Context,
    PNDIS_PACKET    Packet
    );


typedef	VOID	(*WORK_PROC)(struct _IR_WORK_ITEM *);

typedef struct _IR_WORK_ITEM
{
    PASSIVE_PRIMITIVE   Prim;
    PIR_DEVICE          pIrDevice;
    WORK_PROC           Callback;
    PVOID               InfoBuf;
    ULONG               InfoBufLen;
    LIST_ENTRY          ListEntry;
} IR_WORK_ITEM, *PIR_WORK_ITEM;


//
// We use a pointer to the IR_DEVICE structure as the miniport's device context.
//

#define CONTEXT_TO_DEV(__deviceContext) ((PIR_DEVICE)(__deviceContext))
#define DEV_TO_CONTEXT(__irdev) ((NDIS_HANDLE)(__irdev))

#define IRSIR_TAG ' RIS'
#define DEVICE_PREFIX L"\\DEVICE\\"

#include "externs.h"

#endif // _IRSIR_H