/* Copyright 2001-2022 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 #include #include #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 #include #include #ifdef WIN32 #include #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; }