📄 comm.c
字号:
/*
************************************************************************
*
* COMM.c
*
* Portions Copyright (C) 1996-2001 National Semiconductor Corp.
* All rights reserved.
* Copyright (C) 1996-2001 Microsoft Corporation. All Rights Reserved.
*
*
*
*************************************************************************
*/
#include "nsc.h"
#include "comm.tmh"
#define MEDIA_BUSY_THRESHOLD (16)
#define SYNC_SET_COMM_PORT(_intobj,_port,_index,_value) SyncWriteBankReg(_intobj,_port,0,_index,_value);
#define SYNC_GET_COMM_PORT(_intobj,_port,_index) SyncReadBankReg(_intobj,_port,0,_index)
/*
*************************************************************************
* SetCOMInterrupts
*************************************************************************
*/
VOID SetCOMInterrupts(IrDevice *thisDev, BOOLEAN enable)
{
UCHAR newMask;
if (enable){
if (thisDev->portInfo.SirWritePending){
if (thisDev->currentSpeed > MAX_SIR_SPEED){
newMask = thisDev->FirIntMask;
}
else {
newMask = XMIT_MODE_INTS_ENABLE;
}
}
else {
if (thisDev->currentSpeed > MAX_SIR_SPEED){
newMask = thisDev->FirIntMask;
}
else {
newMask = RCV_MODE_INTS_ENABLE;
}
}
}
else {
newMask = ALL_INTS_DISABLE;
}
SetCOMPort(thisDev->portInfo.ioBase, INT_ENABLE_REG_OFFSET, newMask);
}
VOID
SyncSetInterruptMask(
IrDevice *thisDev,
BOOLEAN enable
)
{
UCHAR newMask;
if (enable){
if (thisDev->portInfo.SirWritePending){
if (thisDev->currentSpeed > MAX_SIR_SPEED){
newMask = thisDev->FirIntMask;
}
else {
newMask = XMIT_MODE_INTS_ENABLE;
}
}
else {
if (thisDev->currentSpeed > MAX_SIR_SPEED){
newMask = thisDev->FirIntMask;
}
else {
newMask = RCV_MODE_INTS_ENABLE;
}
}
}
else {
newMask = ALL_INTS_DISABLE;
}
SYNC_SET_COMM_PORT(&thisDev->interruptObj,thisDev->portInfo.ioBase, INT_ENABLE_REG_OFFSET, newMask);
}
/*
*************************************************************************
* DoOpen
*************************************************************************
*
* Open COMM port
*
*/
BOOLEAN DoOpen(IrDevice *thisDev)
{
BOOLEAN result;
DBGOUT(("DoOpen(%d)", thisDev->portInfo.ioBase));
/*
* This buffer gets swapped with the rcvBuffer data pointer
* and must be the same size.
*/
thisDev->portInfo.readBuf = LIST_ENTRY_TO_RCV_BUF(NscMemAlloc(RCV_BUFFER_SIZE)); // Was FALSE -SWA
if (!thisDev->portInfo.readBuf){
return FALSE;
}
/*
* The write buffer is also used as a DMA buffer.
*/
thisDev->portInfo.writeComBuffer = NscMemAlloc(MAX_IRDA_DATA_SIZE );
if (!thisDev->portInfo.writeComBuffer){
return FALSE;
}
/*
* Initialize send/receive FSMs before OpenCOM(), which enables rcv interrupts.
*/
thisDev->portInfo.rcvState = STATE_INIT;
thisDev->portInfo.SirWritePending = FALSE;
//
// the sir recieve will start automatically
//
thisDev->TransmitIsIdle= TRUE;
NdisInitializeEvent(&thisDev->ReceiveStopped);
NdisResetEvent(&thisDev->ReceiveStopped);
NdisInitializeEvent(&thisDev->SendStoppedOnHalt);
NdisResetEvent(&thisDev->SendStoppedOnHalt);
result = OpenCOM(thisDev);
DBGOUT(("DoOpen %s", (CHAR *)(result ? "succeeded" : "failed")));
return result;
}
/*
*************************************************************************
* DoClose
*************************************************************************
*
* Close COMM port
*
*/
VOID DoClose(IrDevice *thisDev)
{
DBGOUT(("DoClose(COM%d)", thisDev->portInfo.ioBase));
if (thisDev->portInfo.readBuf){
NscMemFree(RCV_BUF_TO_LIST_ENTRY(thisDev->portInfo.readBuf));
thisDev->portInfo.readBuf = NULL;
}
if (thisDev->portInfo.writeComBuffer){
NscMemFree(thisDev->portInfo.writeComBuffer);
thisDev->portInfo.writeComBuffer = NULL;
}
#if 0
CloseCOM(thisDev);
#endif
}
typedef struct _SYNC_SET_SPEED {
PUCHAR PortBase;
UINT BitsPerSecond;
} SYNC_SET_SPEED, *PSYNC_SET_SPEED;
VOID
SyncSetUARTSpeed(
PVOID Context
)
{
PSYNC_SET_SPEED SyncContext=(PSYNC_SET_SPEED)Context;
NdisRawWritePortUchar(SyncContext->PortBase+LINE_CONTROL_REG_OFFSET,0x83);
NdisRawWritePortUchar(SyncContext->PortBase+XFER_REG_OFFSET, (UCHAR)(115200/SyncContext->BitsPerSecond));
NdisRawWritePortUchar(SyncContext->PortBase+INT_ENABLE_REG_OFFSET, (UCHAR)((115200/SyncContext->BitsPerSecond)>>8));
NdisRawWritePortUchar(SyncContext->PortBase+LINE_CONTROL_REG_OFFSET, 0x03);
return;
}
/*
*************************************************************************
* SetUARTSpeed
*************************************************************************
*
*
*/
VOID SetUARTSpeed(IrDevice *thisDev, UINT bitsPerSec)
{
if (bitsPerSec <= MAX_SIR_SPEED){
/*
* Set speed in the standard UART divisor latch
*
* 1. Set up to access the divisor latch.
*
* 2. In divisor-latch mode:
* the transfer register doubles as the low divisor latch
* the int-enable register doubles as the hi divisor latch
*
* Set the divisor for the given speed.
* The divisor divides the maximum Slow IR speed of 115200 bits/sec.
*
* 3. Take the transfer register out of divisor-latch mode.
*
*/
SYNC_SET_SPEED SyncContext;
if (!bitsPerSec){
bitsPerSec = 9600;
}
SyncContext.PortBase=thisDev->portInfo.ioBase;
SyncContext.BitsPerSecond=bitsPerSec;
//
// since we are changeing the port bank, sync with the interrupt
//
NdisMSynchronizeWithInterrupt(
&thisDev->interruptObj,
SyncSetUARTSpeed,
&SyncContext
);
NdisStallExecution(5000);
}
}
/*
*************************************************************************
* SetSpeed
*************************************************************************
*
*
*/
BOOLEAN SetSpeed(IrDevice *thisDev)
{
UINT bitsPerSec = thisDev->linkSpeedInfo->bitsPerSec;
BOOLEAN dongleSet, result = TRUE;
// DbgPrint("nsc: setspeed %d\n",bitsPerSec);
DBGOUT((" **** SetSpeed(%xh, %d bps) ***************************", thisDev->portInfo.ioBase, bitsPerSec));
/*
* Disable interrupts while changing speed.
* (This is especially important for the ADAPTEC dongle;
* we may get interrupted while setting command mode
* between writing 0xff and reading 0xc3).
*/
SyncSetInterruptMask(thisDev, FALSE);
/*
* First, set the UART's speed to 9600 baud.
* Some of the dongles need to receive their command sequences at this speed.
*/
SetUARTSpeed(thisDev, 9600);
dongleSet = NSC_DEMO_SetSpeed(thisDev, thisDev->portInfo.ioBase, bitsPerSec, thisDev->portInfo.dongleContext);
//
// debug info.
//
thisDev->portInfo.PacketsReceived_DEBUG = 0;
if (!dongleSet){
DBGERR(("Dongle set-speed failed"));
result = FALSE;
}
/*
* Now set the speed for the COM port
*/
SetUARTSpeed(thisDev, bitsPerSec);
thisDev->currentSpeed = bitsPerSec;
DebugSpeed=bitsPerSec;
SyncSetInterruptMask(thisDev, TRUE);
return result;
}
/*
*************************************************************************
* StepSendFSM
*************************************************************************
*
*
* Step the send fsm to send a few more bytes of an IR frame.
* Return TRUE only after an entire frame has been sent.
*
*/
BOOLEAN StepSendFSM(IrDevice *thisDev)
{
UINT i, bytesAtATime, startPos = thisDev->portInfo.writeComBufferPos;
UCHAR lineStatReg;
BOOLEAN result;
UINT maxLoops;
/*
* Ordinarily, we want to fill the send FIFO once per interrupt.
* However, at high speeds the interrupt latency is too slow and
* we need to poll inside the ISR to send the whole packet during
* the first interrupt.
*/
if (thisDev->currentSpeed > 115200){
maxLoops = REG_TIMEOUT_LOOPS;
}
else {
maxLoops = REG_POLL_LOOPS;
}
/*
* Write databytes as long as we have them and the UART's FIFO hasn't filled up.
*/
while (thisDev->portInfo.writeComBufferPos < thisDev->portInfo.writeComBufferLen){
/*
* If this COM port has a FIFO, we'll send up to the FIFO size (16 bytes).
* Otherwise, we can only send one byte at a time.
*/
if (thisDev->portInfo.haveFIFO){
bytesAtATime = MIN(FIFO_SIZE, (thisDev->portInfo.writeComBufferLen - thisDev->portInfo.writeComBufferPos));
}
else {
bytesAtATime = 1;
}
/*
* Wait for ready-to-send.
*/
i = 0;
do {
lineStatReg = GetCOMPort(thisDev->portInfo.ioBase, LINE_STAT_REG_OFFSET);
} while (!(lineStatReg & LINESTAT_XMIT_HOLDING_REG_EMPTY) && (++i < maxLoops));
if (!(lineStatReg & LINESTAT_XMIT_HOLDING_REG_EMPTY)){
break;
}
/*
* Send the next byte or FIFO-volume of bytes.
*/
for (i = 0; i < bytesAtATime; i++){
SetCOMPort( thisDev->portInfo.ioBase,
XFER_REG_OFFSET,
thisDev->portInfo.writeComBuffer[thisDev->portInfo.writeComBufferPos++]);
}
}
/*
* The return value will indicate whether we've sent the entire frame.
*/
if (thisDev->portInfo.writeComBufferPos >= thisDev->portInfo.writeComBufferLen){
if (thisDev->setSpeedAfterCurrentSendPacket){
/*
* We'll be changing speeds after this packet,
* so poll until the packet bytes have been completely sent out the FIFO.
* After the 16550 says that it is empty, there may still be one remaining
* byte in the FIFO, so flush it out by sending one more BOF.
*/
i = 0;
do {
lineStatReg = GetCOMPort(thisDev->portInfo.ioBase, LINE_STAT_REG_OFFSET);
} while (!(lineStatReg & 0x20) && (++i < REG_TIMEOUT_LOOPS));
SetCOMPort(thisDev->portInfo.ioBase, XFER_REG_OFFSET, (UCHAR)SLOW_IR_EXTRA_BOF);
i = 0;
do {
lineStatReg = GetCOMPort(thisDev->portInfo.ioBase, LINE_STAT_REG_OFFSET);
} while (!(lineStatReg & 0x20) && (++i < REG_TIMEOUT_LOOPS));
}
result = TRUE;
}
else {
result = FALSE;
}
DBGOUT(("StepSendFSM wrote %d bytes (%s):", (UINT)(thisDev->portInfo.writeComBufferPos-startPos), (PUCHAR)(result ? "DONE" : "not done")));
// DBGPRINTBUF(thisDev->portInfo.writeComBuffer+startPos, thisDev->portInfo.writeComBufferPos-startPos);
return result;
}
/*
*************************************************************************
* StepReceiveFSM
*************************************************************************
*
*
* Step the receive fsm to read in a piece of an IR frame;
* strip the BOFs and EOF, and eliminate escape sequences.
* Return TRUE only after an entire frame has been read in.
*
*/
BOOLEAN StepReceiveFSM(IrDevice *thisDev)
{
UINT rawBufPos=0, rawBytesRead=0;
BOOLEAN result;
UCHAR thisch;
PLIST_ENTRY pListEntry;
DBGOUT(("StepReceiveFSM(%xh)", thisDev->portInfo.ioBase));
/*
* Read in and process groups of incoming bytes from the FIFO.
* NOTE: We have to loop once more after getting MAX_RCV_DATA_SIZE
* bytes so that we can see the 'EOF'; hence <= and not <.
*/
while ((thisDev->portInfo.rcvState != STATE_SAW_EOF) && (thisDev->portInfo.readBufPos <= MAX_RCV_DATA_SIZE)){
if (thisDev->portInfo.rcvState == STATE_CLEANUP){
/*
* We returned a complete packet last time, but we had read some
* extra bytes, which we stored into the rawBuf after returning
* the previous complete buffer to the user.
* So instead of calling DoRcvDirect in this first execution of this loop,
* we just use these previously-read bytes.
* (This is typically only 1 or 2 bytes).
*/
rawBytesRead = thisDev->portInfo.readBufPos;
thisDev->portInfo.rcvState = STATE_INIT;
thisDev->portInfo.readBufPos = 0;
}
else {
rawBytesRead = DoRcvDirect(thisDev->portInfo.ioBase, thisDev->portInfo.rawBuf, FIFO_SIZE);
if (rawBytesRead == (UINT)-1){
/*
* Receive error occurred. Go back to INIT state.
*/
thisDev->portInfo.rcvState = STATE_INIT;
thisDev->portInfo.readBufPos = 0;
continue;
}
else if (rawBytesRead == 0){
/*
* No more receive bytes. Break out.
*/
break;
}
}
/*
* Let the receive state machine process this group of characters
* we got from the FIFO.
*
* NOTE: We have to loop once more after getting MAX_RCV_DATA_SIZE
* bytes so that we can see the 'EOF'; hence <= and not <.
*/
for (rawBufPos = 0;
((thisDev->portInfo.rcvState != STATE_SAW_EOF) &&
(rawBufPos < rawBytesRead) &&
(thisDev->portInfo.readBufPos <= MAX_RCV_DATA_SIZE));
rawBufPos++){
thisch = thisDev->portInfo.rawBuf[rawBufPos];
switch (thisDev->portInfo.rcvState){
case STATE_INIT:
switch (thisch){
case SLOW_IR_BOF:
thisDev->portInfo.rcvState = STATE_GOT_BOF;
break;
case SLOW_IR_EOF:
case SLOW_IR_ESC:
default:
/*
* This is meaningless garbage. Scan past it.
*/
break;
}
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -