// // 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 //#include #define VERSION_MAJOR 1 #define VERSION_MINOR 0 typedef unsigned char byte; #include "CHeaders.h" #include "bpqvkiss.h" #include //#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; struct PORTCONTROL * PORTVEC[MaxBPQPortNo + 1]; 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); }