linbpq/bpqvkiss.c

579 lines
11 KiB
C
Raw Normal View History

2022-08-28 09:35:46 +01:00
//
// DLL to inteface the BPQ Virtual COM emulator to BPQ32 switch
//
// Uses BPQ EXTERNAL interface
//
// Version 1.0 November 2005
//
// Version 1.1 October 2006
// Write diagmnostics to BPQ console window instead of STDOUT
// Version 1.2 February 2008
// Changes for dynamic unload of bpq32.dll
// Version 1.2.1 May 2008
// Correct RX length (was 1 byte too long)
// Version 1.3.1 Jan 2009
// Support Win98 VirtualCOM Driver
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_DEPRECATE
//#include <process.h>
//#include <time.h>
#define VERSION_MAJOR 1
#define VERSION_MINOR 0
typedef unsigned char byte;
#include "CHeaders.h"
#include "bpqvkiss.h"
#include <stdlib.h>
//#define DYNLOADBPQ // Dynamically Load BPQ32.dll
//#define EXTDLL // Use GetMuduleHandle instead of LoadLibrary
#include "bpq32.h"
static int ASYINIT(int comport, int speed, int bpqport, BOOL Report);
int kissencode(UCHAR * inbuff, UCHAR * outbuff, int len);
int GetRXMessage(int port, PMESSAGE buff);
void CheckReceivedData(PVCOMINFO pVCOMInfo);
static int ReadCommBlock(PVCOMINFO pVCOMInfo, LPSTR lpszBlock, DWORD nMaxLength );
static BOOL WriteCommBlock(int port, UCHAR * lpByte , DWORD dwBytesToWrite);
PVCOMINFO CreateInfo( int port,int speed, int bpqport ) ;
#define FEND 0xC0 // KISS CONTROL CODES
#define FESC 0xDB
#define TFEND 0xDC
#define TFESC 0xDD
static BOOL Win98 = FALSE;
2023-05-25 14:17:53 +01:00
struct PORTCONTROL * PORTVEC[MaxBPQPortNo + 1];
2022-08-28 09:35:46 +01:00
static size_t ExtProc(int fn, int port, PMESSAGE buff)
{
int len,txlen=0;
char txbuff[1000];
if (VCOMInfo[port]->ComDev == (HANDLE) -1)
{
// Try to reopen every 30 secs
VCOMInfo[port]->ReopenTimer++;
if (VCOMInfo[port]->ReopenTimer < 300)
return 0;
VCOMInfo[port]->ReopenTimer = 0;
ASYINIT(PORTVEC[port]->IOBASE, 9600, port, FALSE);
if (VCOMInfo[port]->ComDev == (HANDLE) -1)
return 0;
}
switch (fn)
{
case 1: // poll
len = GetRXMessage(port,buff);
// if (len > 0)
// {
// // Randomly drop packets
// if ((rand() % 7) > 5)
// {
// Debugprintf("VKISS Test Drop packet");
// return 0;
// }
// }
return len;
case 2: // send
txlen = GetLengthfromBuffer((PDATAMESSAGE)buff);
txlen = kissencode(&buff->DEST[0], (char *) &txbuff, txlen - MSGHDDRLEN);
WriteCommBlock(port, txbuff, txlen);
return (0);
case 3: // CHECK IF OK TO SEND
return (0); // OK
break;
case 4: // reinit
CloseHandle(VCOMInfo[port]->ComDev);
VCOMInfo[port]->ComDev =(HANDLE) -1;
VCOMInfo[port]->ReopenTimer = 250;
return (0);
case 5: // Close
CloseHandle(VCOMInfo[port]->ComDev);
return (0);
}
return 0;
}
VOID * VCOMExtInit(struct PORTCONTROL * PortEntry)
{
char msg[80];
//
// Will be called once for each port to be mapped to a BPQ Virtual COM Port
// The VCOM port number is in IOBASE
//
sprintf(msg,"VKISS COM%d", PortEntry->IOBASE);
WritetoConsole(msg);
PORTVEC[PortEntry->PORTNUMBER] = PortEntry;
CreateInfo(PortEntry->IOBASE, 9600, PortEntry->PORTNUMBER);
// Open File
ASYINIT(PortEntry->IOBASE, 9600, PortEntry->PORTNUMBER, TRUE);
WritetoConsole("\n");
return ExtProc;
}
static int kissencode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
int i, txptr = 0;
UCHAR c;
outbuff[0] = FEND;
outbuff[1] = 0;
txptr = 2;
for (i = 0; i < len; i++)
{
c = inbuff[i];
switch (c)
{
case FEND:
outbuff[txptr++] = FESC;
outbuff[txptr++] = TFEND;
break;
case FESC:
outbuff[txptr++] = FESC;
outbuff[txptr++] = TFESC;
break;
default:
outbuff[txptr++] = c;
}
}
outbuff[txptr++] = FEND;
return txptr;
}
int ASYINIT(int comport, int speed, int bpqport, BOOL Report)
{
char szPort[ 30 ];
char buf[256];
int n, Err;
#pragma warning( push )
#pragma warning( disable : 4996 )
#ifndef _winver
#define _winver 0x0600
#endif
if (HIBYTE(_winver) < 5)
Win98 = TRUE;
#pragma warning( pop )
if (Win98)
{
VCOMInfo[bpqport]->ComDev = CreateFile( "\\\\.\\BPQVCOMM.VXD", GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
}
else{
sprintf( szPort, "\\\\.\\pipe\\BPQCOM%d", comport ) ;
VCOMInfo[bpqport]->ComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
//Handle = CreateFile(Value, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
Err = GetLastError();
if (VCOMInfo[bpqport]->ComDev != (HANDLE) -1)
{
VCOMInfo[bpqport]->NewVCOM = TRUE;
Err = GetFileType(VCOMInfo[bpqport]->ComDev);
}
else
{
// Try old style
sprintf( szPort, "\\\\.\\BPQ%d", comport ) ;
VCOMInfo[bpqport]->ComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
}
}
if (VCOMInfo[bpqport]->ComDev == (HANDLE) -1 && Report)
{
n=sprintf(buf,"Virtual COM Port %d could not be opened ",comport);
WritetoConsole(buf);
return (FALSE) ;
}
return (TRUE) ;
}
static int GetRXMessage(int port, PMESSAGE buff)
{
int len;
PVCOMINFO pVCOMInfo ;
if (NULL == (pVCOMInfo = VCOMInfo[port]))
return 0;
if (!pVCOMInfo->MSGREADY)
CheckReceivedData(pVCOMInfo); // Look for data in RXBUFFER and COM port
if (pVCOMInfo->MSGREADY)
{
len = (int)(pVCOMInfo->RXMPTR- &pVCOMInfo->RXMSG[1]); // Don't need KISS Control Byte
if (pVCOMInfo->RXMSG[0] != 0 && pVCOMInfo->RXMSG[0] != 12)
{
pVCOMInfo->MSGREADY=FALSE;
pVCOMInfo->RXMPTR=(UCHAR *)&pVCOMInfo->RXMSG;
return 0; // Not KISS Data
}
//
// Remove KISS control byte
//
if (pVCOMInfo->RXMSG[0] == 12)
{
// AckMode Frame. Return the next 2 bytes, but don't pass them to Host
UCHAR AckResp[8];
AckResp[0] = FEND;
memcpy(&AckResp[1], &pVCOMInfo->RXMSG[0], 3); //Copy Opcode and Ack Bytes
AckResp[4] = FEND;
WriteCommBlock(port, AckResp, 5);
len -= 2;
memcpy(&buff->DEST[0], &pVCOMInfo->RXMSG[3], len);
// Debugprintf("VKISS Ackmode Frame");
}
else
memcpy(&buff->DEST[0], &pVCOMInfo->RXMSG[1], len);
len += MSGHDDRLEN;
PutLengthinBuffer((PDATAMESSAGE)buff, len);
//
// reset pointers
//
pVCOMInfo->MSGREADY=FALSE;
pVCOMInfo->RXMPTR=(UCHAR *)&pVCOMInfo->RXMSG;
return len;
}
else
return 0; // nothing doing
}
static void CheckReceivedData(PVCOMINFO pVCOMInfo)
{
UCHAR c;
if (pVCOMInfo->RXBCOUNT == 0)
{
//
// Check com buffer
//
pVCOMInfo->RXBCOUNT = ReadCommBlock(pVCOMInfo, (LPSTR) &pVCOMInfo->RXBUFFER, MAXBLOCK-1 );
pVCOMInfo->RXBPTR=(UCHAR *)&pVCOMInfo->RXBUFFER;
}
if (pVCOMInfo->RXBCOUNT == 0)
return;
while (pVCOMInfo->RXBCOUNT != 0)
{
pVCOMInfo->RXBCOUNT--;
c = *(pVCOMInfo->RXBPTR++);
if (pVCOMInfo->ESCFLAG)
{
//
// FESC received - next should be TFESC or TFEND
pVCOMInfo->ESCFLAG = FALSE;
if (c == TFESC)
c=FESC;
if (c == TFEND)
c=FEND;
}
else
{
switch (c)
{
case FEND:
//
// Either start of message or message complete
//
if (pVCOMInfo->RXMPTR == (UCHAR *)&pVCOMInfo->RXMSG)
continue;
pVCOMInfo->MSGREADY=TRUE;
return;
case FESC:
pVCOMInfo->ESCFLAG = TRUE;
continue;
}
}
//
// Ok, a normal char
//
*(pVCOMInfo->RXMPTR++) = c;
}
if (pVCOMInfo->RXMPTR - (UCHAR *)&pVCOMInfo->RXMSG > 500)
pVCOMInfo->RXMPTR=(UCHAR *)&pVCOMInfo->RXMSG;
return;
}
static PVCOMINFO CreateInfo( int port,int speed, int bpqport )
{
PVCOMINFO pVCOMInfo ;
if (NULL == (pVCOMInfo =
(PVCOMINFO) LocalAlloc( LPTR, sizeof( VCOMINFO ) )))
return ( (PVCOMINFO) -1 ) ;
pVCOMInfo->RXBCOUNT=0;
pVCOMInfo->MSGREADY=FALSE;
pVCOMInfo->RXBPTR=(UCHAR *)&pVCOMInfo->RXBUFFER;
pVCOMInfo->RXMPTR=(UCHAR *)&pVCOMInfo->RXMSG;
pVCOMInfo->ComDev = 0 ;
pVCOMInfo->Connected = FALSE ;
pVCOMInfo->Port = port;
VCOMInfo[bpqport]=pVCOMInfo;
return (pVCOMInfo);
}
static BOOL NEAR DestroyTTYInfo( int port )
{
PVCOMINFO pVCOMInfo ;
if (NULL == (pVCOMInfo = VCOMInfo[port]))
return ( FALSE ) ;
LocalFree( pVCOMInfo ) ;
VCOMInfo[port] = 0;
return ( TRUE ) ;
}
static int ReadCommBlock(PVCOMINFO pVCOMInfo, LPSTR lpszBlock, DWORD nMaxLength)
{
DWORD dwLength = 0;
DWORD Available = 0;
if (Win98)
DeviceIoControl(pVCOMInfo->ComDev, (pVCOMInfo->Port << 16) | W98_SERIAL_GETDATA,
NULL,0,lpszBlock,nMaxLength, &dwLength,NULL);
else if (pVCOMInfo->NewVCOM)
{
int ret = PeekNamedPipe(pVCOMInfo->ComDev, NULL, 0, NULL, &Available, NULL);
if (ret == 0)
{
ret = GetLastError();
if (ret == ERROR_BROKEN_PIPE)
{
CloseHandle(pVCOMInfo->ComDev);
pVCOMInfo->ComDev = INVALID_HANDLE_VALUE;
return 0;
}
}
if (Available > nMaxLength)
Available = nMaxLength;
if (Available)
{
UCHAR * ptr1 = lpszBlock;
UCHAR * ptr2 = lpszBlock;
UCHAR c;
int Length;
ReadFile(pVCOMInfo->ComDev, lpszBlock, Available, &dwLength, NULL);
// Have to look foro FF escape chars
Length = dwLength;
while (Length != 0)
{
c = *(ptr1++);
Length--;
if (c == 0xff)
{
c = c = *(ptr1++);
Length--;
if (c == 0xff) // ff ff means ff
{
dwLength--;
}
else
{
// This is connection statua from other end
dwLength -= 2;
pVCOMInfo->NewVCOMConnected = c;
continue;
}
}
*(ptr2++) = c;
}
}
}
else
DeviceIoControl(
pVCOMInfo->ComDev,IOCTL_SERIAL_GETDATA,NULL,0,lpszBlock,nMaxLength, &dwLength,NULL);
return (dwLength);
}
static BOOL WriteCommBlock(int port, UCHAR * Message, DWORD MsgLen)
{
ULONG bytesReturned;
// if ((rand() % 100) > 80)
// return 0;
if (Win98)
return DeviceIoControl(
VCOMInfo[port]->ComDev,(VCOMInfo[port]->Port << 16) | W98_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL);
else if (VCOMInfo[port]->NewVCOM)
{
// Have to escape all oxff chars, as these are used to get status info
UCHAR NewMessage[1000];
UCHAR * ptr1 = Message;
UCHAR * ptr2 = NewMessage;
UCHAR c;
int Length = MsgLen;
while (Length != 0)
{
c = *(ptr1++);
*(ptr2++) = c;
if (c == 0xff)
{
*(ptr2++) = c;
MsgLen++;
}
Length--;
}
return WriteFile(VCOMInfo[port]->ComDev, NewMessage, MsgLen, &bytesReturned, NULL);
}
else
return DeviceIoControl(
VCOMInfo[port]->ComDev,IOCTL_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL);
}