linbpq/bpqhdlc.c

1288 lines
27 KiB
C

/*
Copyright 2001-2018 John Wiseman G8BPQ
This file is part of LinBPQ/BPQ32.
LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
LinBPQ/BPQ32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
*/
//
// Module to provide HDLC Card (DRSI, Baycom etc) support for
// G8BPQ switch in a 32bit environment
//
// Win95 - Uses BPQHDLC.VXD to drive card
// NT -Uses BPQHDLC.DRV to drive card
//
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "CHeaders.h"
#include "bpq32.h"
extern VOID * TRACE_Q;
#ifdef WIN32
_CRT_OBSOLETE(GetVersionEx) errno_t __cdecl _get_winmajor(__out unsigned int * _Value);
_CRT_OBSOLETE(GetVersionEx) errno_t __cdecl _get_winminor(__out unsigned int * _Value);
#define FILE_DEVICE_BPQHDLC 0x00008421
#define IOCTL_BPQHDLC_SEND CTL_CODE(FILE_DEVICE_BPQHDLC,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_BPQHDLC_POLL CTL_CODE(FILE_DEVICE_BPQHDLC,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_BPQHDLC_TIMER CTL_CODE(FILE_DEVICE_BPQHDLC,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_BPQHDLC_ADDCHANNEL CTL_CODE(FILE_DEVICE_BPQHDLC,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_BPQHDLC_CHECKTX CTL_CODE(FILE_DEVICE_BPQHDLC,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_BPQHDLC_IOREAD CTL_CODE(FILE_DEVICE_BPQHDLC,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_BPQHDLC_IOWRITE CTL_CODE(FILE_DEVICE_BPQHDLC,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS)
VOID __cdecl Debugprintf(const char * format, ...);
// Info to pass to Kernel HDLC Driver to define an SCC Subchannel
typedef struct _BPQHDLC_ADDCHANNEL_INPUT {
ULONG IOBASE; // IO Base Address
ULONG IOLEN; // Number of Addresses
UCHAR Interrupt; // Interrupt
UCHAR Channel;
ULONG ASIOC; // A CHAN ADDRESSES
ULONG SIO; // OUR ADDRESSES (COULD BE A OR B)
ULONG SIOC; // Our Control Channel
ULONG BSIOC; // B CHAN CONTROL
VOID * OtherChannel; // Kernel Channel record for first channel if this is 2nd channel
UCHAR SOFTDCDFLAG; // Use SoftDCD flag
int TXBRG; // FOR CARDS WITHOUT /32 DIVIDER
int RXBRG;
UCHAR WR10; // NRZ/NRZI FLAG
USHORT TXDELAY; //TX KEYUP DELAY TIMER
UCHAR PERSISTANCE;
} BPQHDLC_ADDCHANNEL_INPUT, *PBPQHDLC_ADDCHANNEL_INPUT;
DWORD n;
HANDLE hDevice=0;
BYTE bOutput[4]=" ";
DWORD cb=0;
int fResult=0;
BOOL Win98 = FALSE;
extern int QCOUNT;
int Init98(HDLCDATA * PORTVEC);
int Init2K(HDLCDATA * PORTVEC);
int INITPORT(PHDLCDATA PORTVEC);
int HDLCRX2K(PHDLCDATA PORTVEC, UCHAR * buff)
{
ULONG Param;
DWORD len=0;
if (hDevice == 0)
return (0);
if (PORTVEC->DRIVERPORTTABLE == 0) return 0;
memcpy(&Param, &PORTVEC->DRIVERPORTTABLE,4);
fResult = DeviceIoControl(
hDevice, // device handle
(Win98) ? 'G' : IOCTL_BPQHDLC_POLL, // control code
&Param, (Win98) ? (rand() & 0xff) : 4, //Input Params
buff,360,&len, // output parameters
0);
return (len);
}
int HDLCTIMER2K(PHDLCDATA PORTVEC)
{
DWORD len=0;
if (hDevice == 0)
return (0);
if (PORTVEC->DRIVERPORTTABLE == 0) return 0;
fResult = DeviceIoControl(
hDevice, // device handle
(Win98) ? 'T' : IOCTL_BPQHDLC_TIMER, // control code
&PORTVEC->DRIVERPORTTABLE,4, //Input Params
0,0,&len, // output parameters
0);
return (0);
}
int HDLCTXCHECK2K(PHDLCDATA PORTVEC)
{
DWORD Buff;
DWORD len=0;
if (hDevice == 0)
return (0);
if (Win98)
return 0;
if (PORTVEC->DRIVERPORTTABLE == 0) return 0;
fResult = DeviceIoControl(
hDevice, // device handle
IOCTL_BPQHDLC_CHECKTX, // control code
&PORTVEC->DRIVERPORTTABLE,4, //Input Params
&Buff,4,&len, // output parameters
0);
return (Buff);
}
int HDLCTX2K(PHDLCDATA PORTVEC,UCHAR * buff)
{
DWORD txlen=0;
if (hDevice == 0)
return (0);
txlen=(buff[6]<<8) + buff[5];
memcpy(buff,&PORTVEC->DRIVERPORTTABLE,4);
fResult = DeviceIoControl(
hDevice, // device handle
(Win98) ? 'S' : IOCTL_BPQHDLC_SEND, // control code
// control code
buff,txlen, // input parameters
NULL,0,&cb, // output parameters
0);
return (0);
}
int HDLCCLOSE(PHDLCDATA PORTVEC)
{
if (hDevice)
{
CloseHandle(hDevice);
hDevice = 0;
}
return 0;
}
int HDLCRX98(PHDLCDATA PORTVEC, UCHAR * buff)
{
DWORD len=0;
if (hDevice == 0)
return (0);
fResult = DeviceIoControl(
hDevice, // device handle
'G', // control code
PORTVEC->DRIVERPORTTABLE,rand() & 0xff, //Input Params
buff,360,&len, // output parameters
0);
return (len);
}
int HDLCTIMER98(PHDLCDATA PORTVEC)
{
DWORD len=0;
if (hDevice == 0)
return (0);
fResult = DeviceIoControl(
hDevice, // device handle
'T', // control code
PORTVEC->DRIVERPORTTABLE,4, //Input Params
0,0,&len, // output parameters
0);
return (0);
}
int HDLCTXCHECK98(PHDLCDATA PORTVEC)
{
return 0;
}
int HDLCTX98(PHDLCDATA PORTVEC,UCHAR * buff)
{
DWORD txlen=0;
if (hDevice == 0)
return (0);
txlen=(buff[6]<<8) + buff[5];
memcpy(buff,&PORTVEC->DRIVERPORTTABLE,4);
fResult = DeviceIoControl(
hDevice, // device handle
'S', // control code
buff,txlen,// input parameters
NULL,0,&cb, // output parameters
0);
return (0);
}
int IntHDLCRX(PHDLCDATA PORTVEC, UCHAR * buff)
{
if (Win98)
return HDLCRX98(PORTVEC, buff);
else
return HDLCRX2K(PORTVEC, buff);
}
VOID HDLCRX(PHDLCDATA PORTVEC)
{
struct _MESSAGE * Message;
int Len;
struct PORTCONTROL * PORT = (struct PORTCONTROL *)PORTVEC;
if (QCOUNT < 10)
return;
Message = GetBuff();
if (Message == NULL)
return;
Len = IntHDLCRX(PORTVEC, (UCHAR *)Message);
if (Len == 0)
{
ReleaseBuffer((UINT *)Message);
return;
}
C_Q_ADD(&PORT->PORTRX_Q, (UINT *)Message);
return;
}
int HDLCTIMER(PHDLCDATA PORTVEC)
{
if (Win98)
return HDLCTIMER98(PORTVEC);
else
return HDLCTIMER2K(PORTVEC);
}
int HDLCTXCHECK(PHDLCDATA PORTVEC)
{
if (Win98)
return HDLCTXCHECK98(PORTVEC);
else
return HDLCTXCHECK2K(PORTVEC);
}
VOID HDLCTX(PHDLCDATA PORTVEC, PMESSAGE Buffer)
{
struct _LINKTABLE * LINK;
LINK = Buffer->Linkptr;
if (LINK)
{
if (LINK->L2TIMER)
LINK->L2TIMER = LINK->L2TIME;
Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER
}
if (Win98)
HDLCTX98(PORTVEC, (UCHAR *)Buffer);
else
HDLCTX2K(PORTVEC, (UCHAR *)Buffer);
C_Q_ADD(&TRACE_Q, (UINT *)Buffer);
}
int HDLCINIT(HDLCDATA * PORTVEC)
{
int WinVer = 0x0602, WinMinor = 0x02;
WritetoConsole("HDLC\n");
#pragma warning(push)
#pragma warning(disable : 4996)
#ifndef _winver
#define _winmajor 6
#define _winminor 0
#endif
#pragma warning(pop)
if (WinVer >= 5) // Win 2000 or above
return Init2K(PORTVEC);
else
{
Init98(PORTVEC);
OutputDebugString("HDLC Win98 Return from Init98\n");
return 0;
}
}
int Init98(HDLCDATA * PORTVEC)
{
char msg[255];
int err;
Win98 = TRUE;
OutputDebugString("Init HDLC 98\n");
//
// Open HDLC Driver, send send config params
//
if (hDevice == 0) // Not already loaded
{
//
// Load VXD
//
hDevice = CreateFile("\\\\.\\BPQHDLC.VXD",
0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
hDevice=0;
err=GetLastError();
sprintf(msg,"Error loading Driver \\\\.\\BPQHDLC.VXD - Error code %d\n",err);
OutputDebugString(msg);
MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP);
WritetoConsole("Initialisation Failed");
return (FALSE);
}
// OutputDebugString("Calling GetVersion\n");
// fResult = DeviceIoControl(
// hDevice, // device handle
// 10,//DIOC_GETVERSION, // control code
// NULL,0,// input parameters
// bOutput, 4, &cb, // output parameters
// 0);
srand( (unsigned)time( NULL ) ); //Prime random no generator
}
OutputDebugString("Calling Initialize\n");
//
// Initialize Driver for this card and channel
//
fResult = DeviceIoControl(
hDevice, // device handle
'I', // control code
PORTVEC, sizeof (struct PORTCONTROL), // input parameters
bOutput, 4, &cb, // output parameters
0);
memcpy(&PORTVEC->DRIVERPORTTABLE,bOutput,4);
Debugprintf("BPQ32 HDLC Driver Table ADDR %X", PORTVEC->DRIVERPORTTABLE);
OutputDebugString("Initialize Returned\n");
return (TRUE);
}
int PC120INIT(PHDLCDATA PORTVEC)
{
return (HDLCINIT(PORTVEC));
}
int DRSIINIT(PHDLCDATA PORTVEC)
{
return (HDLCINIT(PORTVEC));
}
int TOSHINIT(PHDLCDATA PORTVEC)
{
return (HDLCINIT(PORTVEC));
}
int RLC100INIT(PHDLCDATA PORTVEC)
{
return (HDLCINIT(PORTVEC));
}
int BAYCOMINIT(PHDLCDATA PORTVEC)
{
return (HDLCINIT(PORTVEC));
}
int PA0INIT(PHDLCDATA PORTVEC) // 14 PA0HZP OPTO-SCC
{
return (HDLCINIT(PORTVEC));
}
// W2K/XP Routines
#define IOTXCA VECTOR[0]
#define IOTXEA VECTOR[1]
#define IORXCA VECTOR[2]
#define IORXEA VECTOR[3]
#define SIOR READ_PORT_UCHAR(PORTVEC->SIO)
#define SIOW(A) WRITE_PORT_UCHAR(PORTVEC->SIO,A)
#define SIOCR READ_PORT_UCHAR(PORTVEC->SIOC)
#define SIOCW(A) WRITE_PORT_UCHAR(PORTVEC->SIOC, A)
//#define SETRVEC PORTVEC->IORXCA =
//#define SETTVEC PORTVEC->IOTXCA =
int CLOCKFREQ = 76800; // 4,915,200 HZ /(32*2)
int TOSHCLOCKFREQ = 57600;
UCHAR SDLCCMD[] = {
0,0,
2,0, // BASE VECTOR
4,0x20, // SDLC MODE
3,0xc8, // 8BIT, CRC ENABLE, RX DISABLED
7,0x7e, // STANDARD FLAGS
1,0x13, // INT ON ALL RX, TX INT EN, EXT INT EN
5,0xe1, // DTR, 8BIT, SDLC CRC,TX CRC EN
10,0xa0, // CRC PRESET TO 1
9,0x09, // ENABLE INTS
11,0x66, // NO XTAL, RXC = DPLL, TXC = RTXC, TRXC = BRG (NEEDS /32 BETWEEN TRXC AND RTXC)
14,0x83,
14,0x23,
15,0xc0 // EXT INT ONLY ON TX UND AND ABORT RX
};
#define SDLCLEN 26
UCHAR TOSHR11 = 0x68; // NO XTAL, RXC = DPLL, TXC = DPLL, NO CLK OUTPUT
UCHAR CIOPARAMS[] = {
0x2B,0xFF, // B DIRECTION - ALL IN
0x23,0xFF, // A DIRECTION - ALL IN
0x1D,0x0E2, // C/T 2 MODE - CONT, EXT IN, EXT O, SQUARE
0x1C,0x0E2, // C/T 1 MODE ""
0x19,0x10, // C/T 2 LSB - 16 = /32 FOR SQUARE WAVE
0x18,0, // MSB
0x17,0x10, // C/T 1 LSB
0x16,0, // MSB
0x0B,0x04, // CT2 "" - GATE
0x0A,0x04, // CT1 "" - GATE
0x06,0x0F, // PORT C DIRECTION - INPUTS
1,0x84, // ENABLE PORTS A AND B
0,0 // INTERRUPT CONTROL
};
#define CIOLEN 26
VOID WRITE_PORT_UCHAR(UINT Port, UINT Value)
{
ULONG buff[3];
buff[0] = Port;
buff[1] = Value;
fResult = DeviceIoControl(
hDevice, // device handle
IOCTL_BPQHDLC_IOWRITE, // control code
buff, 8, // input parameters
NULL,0,&cb, // output parameters
0);
}
UCHAR READ_PORT_UCHAR(ULONG Port)
{
ULONG buff[3];
buff[0] = Port;
fResult = DeviceIoControl(
hDevice, // device handle
IOCTL_BPQHDLC_IOREAD, // control code
buff, 4, // input parameters
buff, 4,&cb, // output parameters
0);
Debugprintf("BPQ32 HDLC READ_PORT_UCHAR Returned %X", LOBYTE(buff[0]));
return LOBYTE(buff[0]);
}
int Init2K(HDLCDATA * PORTVEC)
{
char msg[255];
int err;
if (hDevice == 0) // Not already loaded
{
//
// Open HDLC Driver
//
hDevice = CreateFile(
"\\\\.\\BPQHDLC", // Open the Device "file"
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
hDevice=0;
err=GetLastError();
sprintf(msg,"Error Opening Driver \\device\\BPQHDLC - Error code %d\n", err);
OutputDebugString(msg);
WritetoConsole(msg);
return (FALSE);
}
}
INITPORT(PORTVEC);
return 0;
}
PHDLCDATA See_if_First_On_Card(PHDLCDATA PORTVEC)
{
// SEE IF ANOTHER PORT IS ALREADY USING THE OTHER CHANNEL ON THIS CARD
int i;
PHDLCDATA PreviousPort = (PHDLCDATA)PORTTABLE;
for (i = 0; i < NUMBEROFPORTS; i++)
{
if (PORTVEC == PreviousPort)
{
// NONE BEFORE OURS
return NULL;
}
if (PORTVEC->PORTCONTROL.IOBASE == PreviousPort->PORTCONTROL.IOBASE)
{
// ENSURE ENTRIES ARE FOR DIFFERENT CHANNELS
if (PORTVEC->PORTCONTROL.CHANNELNUM == PreviousPort->PORTCONTROL.CHANNELNUM)
// CHANNEL DEFINITION ERROR
return (PHDLCDATA) -1;
else
return PreviousPort;
}
PreviousPort = (PHDLCDATA)PreviousPort->PORTCONTROL.PORTPOINTER;
}
return NULL; // FLAG NOT FOUND
}
VOID INITPART2(PHDLCDATA PORTVEC, USHORT SCCOffset, PHDLCDATA PreviousPort)
{
// SCCOffset is address of SCC relative to Card Base Address
int i;
USHORT SCCBase=PORTVEC->PORTCONTROL.IOBASE + SCCOffset;
int BRG;
// SET UP ADDRESS LIST - THIS PATH FOR CARDS WITH 'NORMAL'
// ADDRESSING - C/D=A0, A/B=A1, SO ORDER IS BCTRL BDATA ACTRL ADATA
// OR DE, WHICH USES WORD ADDRESSES C/D=A1, A/B=A2
PORTVEC->BSIOC = SCCBase; // B CHAN ADDR
PORTVEC->ASIOC = SCCBase+2; // A CHAN ADDR
// SEE WHICH CHANNEL TO USE
if (PORTVEC->PORTCONTROL.CHANNELNUM == 'A')
{
PORTVEC->A_PTR = PORTVEC; // POINT TO OUR ENTRY
PORTVEC->SIOC = SCCBase+2;
PORTVEC->SIO = SCCBase+3; // DATA 1 ABOVE CONTROL
if (PreviousPort) // Another Channel is first on Card
PORTVEC->B_PTR = PreviousPort; // CROSSLINK CHANNELS
}
else
{
// MUST BE B - CHECKED EARLIER
PORTVEC->B_PTR = PORTVEC; // POINT TO OUR ENTRY
PORTVEC->SIOC = SCCBase;
PORTVEC->SIO = SCCBase+1; // DATA 1 ABOVE CONTROL
if (PreviousPort) // Another Channel is first on Card
PORTVEC->A_PTR = PreviousPort; // CROSSLINK CHANNELS
}
// INITIALISE COMMS CHIP
if (PreviousPort == 0) // OTHER CHAN ALREADY SET UP?
{
// DO A HARD RESET OF THE SCC
WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0); // Make Sure WR0
WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0);
WRITE_PORT_UCHAR(PORTVEC->ASIOC, 9); // WR9
WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0xC0); // Hard Reset
Sleep(2);
}
for (i=0; i< SDLCLEN; i++)
{
WRITE_PORT_UCHAR(PORTVEC->SIOC, SDLCCMD[i]);
}
PORTVEC->WR10 = 0x20; // NRZI
// SET UP BRG FOR REQUIRED SPEED
if (PORTVEC->PORTCONTROL.BAUDRATE == 0)
{
// SET EXTERNAL CLOCK
SIOCW(11); // WR11
SIOCW(0x20); // RX = TRXC TX = RTXC
return;
}
if (PORTVEC->PORTCONTROL.PORTTYPE == 12) // RLC 400 USES SAME CLOCK AS TOSH
BRG = TOSHCLOCKFREQ;
else
BRG = CLOCKFREQ;
BRG=(BRG/PORTVEC->PORTCONTROL.BAUDRATE)-2;
SIOCW(12); // Select WR12
SIOCW(BRG & 0xff); // SET LSB
SIOCW(13); // Select WR13
SIOCW(BRG >> 8); // SET MSB
return;
}
VOID INITCIO(PHDLCDATA PORTVEC)
{
// INITIALISE CIO - DRSI ONLY
int i;
ULONG CIOAddr = PORTVEC->PORTCONTROL.IOBASE + 7; // TO CIO PORT
READ_PORT_UCHAR(CIOAddr);
WRITE_PORT_UCHAR(CIOAddr, 0);
READ_PORT_UCHAR(CIOAddr);
WRITE_PORT_UCHAR(CIOAddr, 0);
WRITE_PORT_UCHAR(CIOAddr, 1); // FORCE RESET
WRITE_PORT_UCHAR(CIOAddr, 0); // CLEAR RESET
for (i=0; i< CIOLEN; i++)
{
WRITE_PORT_UCHAR(CIOAddr, CIOPARAMS[i] );
}
return;
}
VOID STARTCIO(PHDLCDATA PORTVEC)
{
USHORT CIOAddr = PORTVEC->PORTCONTROL.IOBASE + 7; // TO CIO PORT
UCHAR Reg;
// B CHANNEL
// SET COUNTER OUTPUT BIT ACTIVE
WRITE_PORT_UCHAR(CIOAddr, 0x2B); // PORT B DIRECTION
Reg = READ_PORT_UCHAR(CIOAddr);
if (PORTVEC->PORTCONTROL.CHANNELNUM == 'B')
{
Reg &= 0xEF; // SET BIT 4 AS OUTPUT
WRITE_PORT_UCHAR(CIOAddr, 0x2B); // PORT B DIRECTION
WRITE_PORT_UCHAR(CIOAddr, Reg); // UPDATE PORT B DIRECTION
// ENABLE COUNTER
WRITE_PORT_UCHAR(CIOAddr,1); // MASTER CONFIG
Reg = READ_PORT_UCHAR(CIOAddr); // GET IT
Reg |= 0x40; // ENABLE CT1
WRITE_PORT_UCHAR(CIOAddr,1); // MASTER CONFIG
WRITE_PORT_UCHAR(CIOAddr, Reg); // Set it
// START COUNTER
WRITE_PORT_UCHAR(CIOAddr,0x0A); // CT1 CONTROL
WRITE_PORT_UCHAR(CIOAddr,6); // START CT1
return;
}
Reg &= 0xFE; // SET BIT 0 AS OUTPUT
WRITE_PORT_UCHAR(CIOAddr, 0x2B); // PORT B DIRECTION
WRITE_PORT_UCHAR(CIOAddr, Reg); // UPDATE PORT B DIRECTION
// ENABLE COUNTER
WRITE_PORT_UCHAR(CIOAddr,1); // MASTER CONFIG
Reg = READ_PORT_UCHAR(CIOAddr); // GET IT
Reg |= 0x20; // ENABLE CT2
WRITE_PORT_UCHAR(CIOAddr,1); // MASTER CONFIG
WRITE_PORT_UCHAR(CIOAddr, Reg); // Set it
// START COUNTER
WRITE_PORT_UCHAR(CIOAddr,0x0B); // CT2 CONTROL
WRITE_PORT_UCHAR(CIOAddr,6); // START CT2
return;
}
VOID INITMODEM(PHDLCDATA PORTVEC)
{
// SETUP MODEM - PC120 ONLY
WRITE_PORT_UCHAR(PORTVEC->PORTCONTROL.IOBASE, 0x0a);
}
VOID CHECKCHAN(PHDLCDATA PORTVEC, USHORT CDOffset)
{
// CDoffset contains offset to Second SCC
// IF CHANNEL = C OR D SET TO SECOND SCC ADDRESS, AND CHANGE TO A OR B
if (PORTVEC->PORTCONTROL.CHANNELNUM > 'B')
{
// SECOND SCC
PORTVEC->PORTCONTROL.CHANNELNUM -=2;
PORTVEC->PORTCONTROL.IOBASE+=CDOffset;
}
}
// BAYCOM CARD
VOID BINITPART2(PHDLCDATA PORTVEC, USHORT SCCOffset, PHDLCDATA PreviousPort)
{
// ORDER IS 0 1 2 3 4 5 6 7
// ADATA BDATA CDATA DDATA ACTRL BCTRL CCTRL DCTRL
// Before entering here IOBASE and Chan have been updated if Chan were C or D
// SET UP ADDRESS LIST
int i;
USHORT SCCBase=PORTVEC->PORTCONTROL.IOBASE + SCCOffset;
int BRG;
// SET UP ADDRESS LIST - THIS PATH FOR CARDS WITH 'NORMAL'
// ADDRESSING - C/D=A0, A/B=A1, SO ORDER IS BCTRL BDATA ACTRL ADATA
// OR DE, WHICH USES WORD ADDRESSES C/D=A1, A/B=A2
PORTVEC->ASIOC = SCCBase+4; // A CHAN ADDR
PORTVEC->BSIOC = SCCBase+5; // B CHAN ADDR
// SEE WHICH CHANNEL TO USE
if (PORTVEC->PORTCONTROL.CHANNELNUM == 'A')
{
PORTVEC->A_PTR = PORTVEC; // POINT TO OUR ENTRY
PORTVEC->SIOC = SCCBase+4;
PORTVEC->SIO = SCCBase;
if (PreviousPort) // Another Channel is first on Card
PORTVEC->B_PTR = PreviousPort; // CROSSLINK CHANNELS
}
else
{
// MUST BE B - CHECKED EARLIER
PORTVEC->B_PTR = PORTVEC; // POINT TO OUR ENTRY
PORTVEC->SIOC = SCCBase+5;
PORTVEC->SIO = SCCBase+1;
if (PreviousPort) // Another Channel is first on Card
PORTVEC->A_PTR = PreviousPort; // CROSSLINK CHANNELS
}
// INITIALISE COMMS CHIP
if (PreviousPort == 0) // OTHER CHAN ALREADY SET UP?
{
// DO A HARD RESET OF THE SCC
WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0); // Make Sure WR0
WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0);
WRITE_PORT_UCHAR(PORTVEC->ASIOC, 9); // WR9
WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0xC0); // Hard Reset
Sleep(2);
}
for (i=0; i< SDLCLEN; i++)
{
WRITE_PORT_UCHAR(PORTVEC->SIOC, SDLCCMD[i]);
}
// SET UP BRG FOR REQUIRED SPEED
if (PORTVEC->PORTCONTROL.BAUDRATE == 0)
{
// SET EXTERNAL CLOCK
SIOCW(11); // WR11
SIOCW(0x20); // RX = TRXC TX = RTXC
// BAYCOM RUH PORT USES NRZ
PORTVEC->WR10 = 0x0; // NRZ
return;
}
PORTVEC->WR10 = 0x20; // NRZI
// THERE IS NO /32 ON THE BAYCOM BOARD, SO FOR THE MOMENT WILL USE BRG
// FOR TRANSMIT. THIS REQUIRES IT TO BE REPROGRAMMED BETWEEN TX AND RX,
// AND SO PREVENTS LOOPBACK OR FULLDUP OPERATION
BRG=(CLOCKFREQ/PORTVEC->PORTCONTROL.BAUDRATE)-2;
SIOCW(12); // Select WR12
SIOCW(BRG & 0xff); // SET LSB
SIOCW(13); // Select WR13
SIOCW(BRG >> 8); // SET MSB
SIOCW(11); // WR11
SIOCW(0x70); // RXC=DPLL, TXC=BRG
PORTVEC->RXBRG = BRG;
// CALC TX RATE
PORTVEC->TXBRG = ((BRG+2)/32)-2;
SIOCW(12); // Select WR12
SIOCW(BRG & 0xff); // SET LSB
SIOCW(13); // Select WR13
SIOCW(BRG >> 8); // SET MSB
// IF 7910/3105 PORTS, SET TXC=BRG, RXC=DPLL
// IT SEEMS THE 3RD PORT IS MORE LIKELY TO BE USED WITH A SIMPLE
// MODEM WITHOUT CLOCK GERERATION (EG BAYCOM MODEM), SO SET ALL
// PORTS THE SAME
SIOCW(11); // WR11
SIOCW(0x70); // RXC=DPLL, TXC=BRG
}
BOOLEAN INITREST(PHDLCDATA PORTVEC, PHDLCDATA PrevPort)
{
BPQHDLC_ADDCHANNEL_INPUT AddParams;
VOID * Return = NULL;
int cb;
/* mov IRQHand[EBX],0 ; in case already hooked by another port
MOV PORTINTERRUPT[EBX],OFFSET32 SIOINT
CMP EDI,0
JNE SHORT INTDONE ; ALREADY SET UP
CALL HOOKINT ; INTERRUPT
INTDONE:
CALL RXAINIT
;
*/
// Pass Params to the driver
AddParams.IOBASE = PORTVEC->PORTCONTROL.IOBASE;
AddParams.IOLEN = PORTVEC->IOLEN;
AddParams.Interrupt = PORTVEC->PORTCONTROL.INTLEVEL;
AddParams.ASIOC = PORTVEC->ASIOC;
AddParams.BSIOC = PORTVEC->BSIOC;
AddParams.SIOC = PORTVEC->SIOC;
AddParams.SIO = PORTVEC->SIO;
AddParams.TXBRG = PORTVEC->TXBRG;
AddParams.RXBRG = PORTVEC->RXBRG;
AddParams.WR10 = PORTVEC->WR10;
AddParams.Channel = PORTVEC->PORTCONTROL.CHANNELNUM;
AddParams.SOFTDCDFLAG = PORTVEC->PORTCONTROL.SOFTDCDFLAG;
AddParams.TXDELAY = PORTVEC->PORTCONTROL.PORTTXDELAY;
AddParams.PERSISTANCE = PORTVEC->PORTCONTROL.PORTPERSISTANCE;
if (PrevPort)
AddParams.OtherChannel = PrevPort->DRIVERPORTTABLE;
else
AddParams.OtherChannel = 0;
fResult = DeviceIoControl(
hDevice, // device handle
IOCTL_BPQHDLC_ADDCHANNEL, // control code
&AddParams,sizeof(BPQHDLC_ADDCHANNEL_INPUT), // input parameters
&Return,4,&cb, // output parameters
0);
PORTVEC->DRIVERPORTTABLE = Return;
if (Return == NULL)
{
// Init Failed, probably because resources were not alllocated to driver
WritetoConsole("Kernel Driver Init Failed - Check Resource Allocation");
return (FALSE);
}
PORTVEC->RR0 = SIOCR; //GET INITIAL RR0
return TRUE;
}
int INITPORT(PHDLCDATA PORTVEC)
{
PHDLCDATA PreviousPort;
// SEE IF C OR D. If so, Adjust IOBASE and change to A/B
switch (PORTVEC->PORTCONTROL.PORTTYPE)
{
case 10: // RLC100
case 12: // RLC400
case 20: // PA0HZP OPTO-SCC
CHECKCHAN(PORTVEC, 4); // Channels are 4 apart
break;
case 18: // Baycom
CHECKCHAN(PORTVEC, 2); //Channels are 2 apart
break;
}
// By now Channel Should be only A or B
if ((PORTVEC->PORTCONTROL.CHANNELNUM != 'A') && (PORTVEC->PORTCONTROL.CHANNELNUM != 'B'))
{
WritetoConsole("Invalid Channel\n");
return FALSE;
}
PreviousPort = See_if_First_On_Card(PORTVEC);
if (PreviousPort == (PHDLCDATA)-1)
{
// Two ports on same card have same Channel
WritetoConsole("Duplicate Channels\n");
return FALSE;
}
switch (PORTVEC->PORTCONTROL.PORTTYPE)
{
case 2: // PC120
PORTVEC->IOLEN = 8; // I think! - Modem is at offfset 0, SCC at 4
INITPART2(PORTVEC, 4, PreviousPort); // SCC ADDRESS 4 Above Base Address
if (PreviousPort == NULL) INITMODEM(PORTVEC);
INITREST(PORTVEC, PreviousPort);
return 0;
case 4: // DRSIINIT
PORTVEC->IOLEN = 8; // SCC at 0, CIO at 7
if (PreviousPort == NULL) INITCIO(PORTVEC); // SET UP CIO FOR /32 UNLESS Already Done
INITPART2(PORTVEC, 0, PreviousPort);
if (PORTVEC->PORTCONTROL.BAUDRATE) STARTCIO(PORTVEC);
INITREST(PORTVEC, PreviousPort);
return TRUE;
case 6:
WritetoConsole("TYPE=TOSH Not Supported\n");
return FALSE;
case 10: // RLC100
case 12: // RLC400
PORTVEC->IOLEN = 4; // 2 * SCC, but each SCC can be on it's own
INITPART2(PORTVEC, 0, PreviousPort);
INITREST(PORTVEC, PreviousPort);
return TRUE;
case 18: // Baycom
PORTVEC->IOLEN = 8; // 2 * SCC, but Need at least 6 Addresses
BINITPART2(PORTVEC, 0, PreviousPort);
INITREST(PORTVEC, PreviousPort);
return 0;
case 20: // PA0HZP OPTO-SCC
PORTVEC->IOLEN = 4; // 2 * SCC, but each SCC can be on it's own
INITPART2(PORTVEC, 0, PreviousPort);
INITREST(PORTVEC, PreviousPort);
return TRUE;
}
return FALSE;
}
#endif
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef WIN32
#include <io.h>
#define read _read
#define write _write
#define close _close
#define open _open
#else
#endif
// Linux HDLC Kernel Module Support
#define TIOCMGET 0x5415
PHDLCDATA FIRSTHDLCPORT = 0;
int KHDLCINIT(PHDLCDATA PORTVEC)
{
int ret, status = 65;
char Msg[64] = "HDLC Params";
// Only open device for first port - all ports share a kernel driver
if (FIRSTHDLCPORT == 0)
{
// for now just open /dev/bpqhdlc. Needs to be a param somewhere
PORTVEC->fd = open("/dev/bpqhdlc", O_RDWR); // Open the device with read/write access
FIRSTHDLCPORT = PORTVEC;
if (PORTVEC->fd < 0)
{
WritetoConsole("HDLC - Failed to open /dev/bpqhdlc\n");
return errno;
}
}
ret = ioctl(FIRSTHDLCPORT->fd, 1, Msg);
Consoleprintf("HDLC Channel %c", PORTVEC->PORTCONTROL.CHANNELNUM);
return 0;
}
void KHDLCTX(PHDLCDATA PORTVEC, PMESSAGE Buffer)
{
int fd = FIRSTHDLCPORT->fd;
struct _LINKTABLE * LINK;
unsigned char Message[512];
if (fd != -1)
{
int len = GetLengthfromBuffer((PDATAMESSAGE)Buffer) - (3 + sizeof(void *));
int ret;
Message[0] = (PORTVEC->PORTCONTROL.CHANNELNUM - ('A' << 4)); // KISS Control
memcpy(&Message[1], Buffer->DEST, len);
ret = write(fd, Message, len + 1);
if (ret < 0)
{
Debugprintf("Failed to write the message to the device.");
return;
}
}
LINK = Buffer->Linkptr;
if (LINK)
{
if (LINK->L2TIMER)
LINK->L2TIMER = LINK->L2TIME;
Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER
}
// Pass buffer to trace routines
C_Q_ADD(&TRACE_Q, Buffer);
}
int KHDLCRX(PHDLCDATA PORTVEC)
{
int len;
PMESSAGE Buffer;
unsigned char packet[512] = "";
int fd = FIRSTHDLCPORT->fd;
if (fd == -1)
return 0;
packet[0] = (PORTVEC->PORTCONTROL.CHANNELNUM - ('A' << 4)); // KISS Control
len = read(fd, packet, 512); // Read the response from the LKM
while (len)
{
if (len < 0)
{
Debugprintf("bpqhdlc read failed");
return errno;
}
Buffer = GetBuff();
if (Buffer)
{
memcpy(&Buffer->DEST, packet + 1, len - 1); // Has KISS control on front
len += (3 + sizeof(void *));
PutLengthinBuffer((PDATAMESSAGE)Buffer, len - 1); // Needed for arm5 portability
C_Q_ADD(&PORTVEC->PORTCONTROL.PORTRX_Q, Buffer);
}
len = read(fd, packet, 512); // Read the response from the LKM
}
return 0;
}
void KHDLCTIMER(PHDLCDATA PORTVEC)
{}
void KHDLCCLOSE(PHDLCDATA PORTVEC)
{
int fd = PORTVEC->fd;
if (fd != -1)
close(fd);
}
BOOL KHDLCTXCHECK(PHDLCDATA PORTVEC)
{
return 0;
}