#include "mungal4.h" #include #include "L4SERIAL.h" #include "L4PCSPAK.h" #include //Win32 Serial support: ADB 02/10/07 //------------------------------------------------ // COM_INIT macro // Initializes COMBUF structure //------------------------------------------------ void PCSerialPacket::COM_INIT(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); buffer->head = 0; buffer->tempHead = 0; buffer->tempTail = 0; buffer->tail = 0; buffer->count = 0; buffer->tempIn = 0; buffer->tempOut = 0; ReleaseMutex(hMutex); } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_PUT_AL macro // 'Temporarily' puts character in AL into COMBUF //------------------------------------------------ void PCSerialPacket::COM_PUT_AL(PCSerPackBuf* buffer, BYTE al) { WaitForSingleObject(hMutex, INFINITE); if(buffer->tempIn > COMBUFSIZE) { #if DIE_ON_OVERFLOW DISABLE_AND_DIE("Serial Packet Buffer Overflow!"); #else ReleaseMutex(hMutex); return; #endif } buffer->tempIn++; //increment 'tempIn' count buffer->data[buffer->tempHead] = al; //write the data buffer->tempHead++; //bump 'tempHead' buffer->tempHead &= (COMBUFSIZE-1); //limit it ReleaseMutex(hMutex); } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_RESTORE_IN macro // Discards characters 'temporarily' placed in COMBUF //------------------------------------------------ void PCSerialPacket::COM_RESTORE_IN(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); buffer->tempIn = 0; //clear the 'tempIn' count buffer->tempHead = buffer->head; //restore 'tempHead' index ReleaseMutex(hMutex); } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_ACCEPT_IN macro // Accepts characters 'temporarily' placed in COMBUF //------------------------------------------------ void PCSerialPacket::COM_ACCEPT_IN(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); buffer->count += buffer->tempIn; //add to current count buffer->tempIn = 0; buffer->head = buffer->tempHead; //set the new head offset ReleaseMutex(hMutex); } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_GET_AL macro // 'Temporarily' gets character into AL from COMBUF //------------------------------------------------ int PCSerialPacket::COM_GET_AL(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); buffer->tempOut++; //inc 'tempOut' count int al = buffer->data[buffer->tempTail]; //read the data buffer->tempTail ++; //bump 'tempTail' buffer->tempTail &= (COMBUFSIZE-1); //limit it ReleaseMutex(hMutex); return al; } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_RESTORE_OUT macro // Restores characters 'temporarily' removed from COMBUF //------------------------------------------------ void PCSerialPacket::COM_RESTORE_OUT(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); buffer->tempOut = 0; //clear the 'tempOut' count buffer->tempTail = buffer->tail; //restore 'tempTail' index ReleaseMutex(hMutex); } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_ACCEPT_OUT macro // Discards characters 'temporarily' removed from COMBUF //------------------------------------------------ void PCSerialPacket::COM_ACCEPT_OUT(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); buffer->count -= buffer->tempOut; //subtract from current count buffer->tempOut = 0; //clear 'tempOut' count buffer->tail = buffer->tempTail; //set the new tail offset ReleaseMutex(hMutex); } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_EAT_PACKET macro // Discards entire buffer item from COMBUF //------------------------------------------------ void PCSerialPacket::COM_EAT_PACKET(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); BYTE command = buffer->data[buffer->tail]; //read the packet command byte command &= 0x07; //remove the MSB int length = this->rxLengthPtr[command]; //convert packet command to length length++; //include the command byte buffer->count -= length; //subtract from count buffer->tail += length; //move the tail buffer->tail &= (COMBUFSIZE-1); //limit it buffer->tempTail = buffer->tail; //set the new tempTail index buffer->tempOut = 0; ReleaseMutex(hMutex); } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_SPACE_IN macro // Returns input bytes available in COMBUF //------------------------------------------------ int PCSerialPacket::COM_SPACE_IN(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); int available = COMBUFSIZE; available -= buffer->count; available -= buffer->tempIn; ReleaseMutex(hMutex); return available; } //Win32 Serial support: ADB 02/13/07 //------------------------------------------------ // COM_SPACE_OUT macro // Returns output bytes available in COMBUF //------------------------------------------------ int PCSerialPacket::COM_SPACE_OUT(PCSerPackBuf* buffer) { WaitForSingleObject(hMutex, INFINITE); int output = buffer->count; output -= buffer->tempOut; ReleaseMutex(hMutex); return output; } //Win32 Serial support: ADB 06/23/07 PCSerialPacket::PCSerialPacket() { } void PCSerialPacket::ShutdownRxThread() { //Win32 Serial support: ADB 06/23/07 enabled = false; bThreadAbort = TRUE; if (overlapSend.hEvent != INVALID_HANDLE_VALUE) { CloseHandle(overlapSend.hEvent); overlapSend.hEvent = INVALID_HANDLE_VALUE; } if(hRxAbortEvent != INVALID_HANDLE_VALUE) { SetEvent(hRxAbortEvent); CloseHandle(hRxAbortEvent); hRxAbortEvent = INVALID_HANDLE_VALUE; } if(hRxThread != INVALID_HANDLE_VALUE) { WaitForSingleObject(hRxThread, INFINITE); CloseHandle(hRxThread); hRxThread = INVALID_HANDLE_VALUE; } if(hMutex != INVALID_HANDLE_VALUE) { CloseHandle(hMutex); hMutex = INVALID_HANDLE_VALUE; } if(hComm != INVALID_HANDLE_VALUE) { CloseHandle(hComm); hComm = INVALID_HANDLE_VALUE; } } PCSerialPacket::~PCSerialPacket() { if (this == NULL) { std::cout << "~PCSerialPacket() has been passed a NULL pointer\n" << std::flush; } else { ShutdownRxThread(); } } //Win32 Serial support: ADB 01/15/07 // Only call Initialize(...) ONCE during the lifetime of the object! int PCSerialPacket::Initialize(WORD rate, WORD format, const char* port, BYTE* rxLengths, BYTE rxCount, BYTE* txLengths, BYTE txCount) { //;------------------------------------------------------------------------- //; PCSPAKInit() //; Constructor for serial packet driver interface //;------------------------------------------------------------------------- //; Enter: //; init_this = pointer to object //; init_port = address of serial port //; init_irq_num = interrupt number //; init_rx_len = pointer to (byte) receive length table //; init_tx_len = pointer to (byte) transmit length table //;------------------------------------------------------------------------- //; Returns: //; 1 if ok, //; -1 if IRQ's all used, //; -2 if DPMI error, //; -3 if really weird bogus error, //; else IIR value //;------------------------------------------------------------------------- //DWORD return_status = 0; //;-------------------------------------------------------------- //; Clear the data buffers //;-------------------------------------------------------------- COM_INIT(&rxBuf); //assumes esi points to object COM_INIT(&txBuf); //assumes esi points to object //;-------------------------------------------------------------- //; Initialize values //;-------------------------------------------------------------- enabled = 0; txHoldOff = 0; txReply = 0; rxFlags = 0; Error = 0; intDataPtr = 0; historyIndex = 0; txState = TX_IDLE_STATE; //initialize to idle state rxState = RX_IDLE_STATE; //initialize to idle state txLengthPtr = txLengths; //set dialect transmit table pointer txMaximum = txCount; //set maximum dialect transmit index rxLengthPtr = rxLengths; //set dialect receive table pointer rxMaximum = rxCount; //set maximum dialect receive index hComm = INVALID_HANDLE_VALUE; hRxAbortEvent = INVALID_HANDLE_VALUE; hRxThread = INVALID_HANDLE_VALUE; hMutex = INVALID_HANDLE_VALUE; bThreadAbort = FALSE; overlapSend.hEvent = INVALID_HANDLE_VALUE; hComm = InitSerialPort(rate, format, port, true); if(hComm == INVALID_HANDLE_VALUE) { enabled = 0; DWORD err = GetLastError(); DEBUG_STREAM << "ERROR: InitSerialPort() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush; return PCSPAKWin32Err; } enabled = 1; //Setup multi-threading for rx thread hMutex = CreateMutex(NULL, false, NULL); if(hMutex == NULL) { DEBUG_STREAM << "ERROR: CreateMutex() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush; enabled = 0; return PCSPAKWin32Err; } if(!SetCommMask(hComm, EV_RXCHAR)) { DEBUG_STREAM << "ERROR: SetCommMask() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush; enabled = 0; return PCSPAKWin32Err; } hRxAbortEvent = CreateEvent(NULL, FALSE, FALSE, NULL); hRxThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(&RxThreadProc), this, NULL, NULL); if(hRxThread == NULL) { DEBUG_STREAM << "ERROR: CreateThread() failed [GetLastError() = " << GetLastError() << "]!\n" << std::flush; enabled = 0; return PCSPAKWin32Err; } SetThreadPriority(hRxThread, THREAD_PRIORITY_TIME_CRITICAL); overlapSend.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); overlapSend.Internal = 0; overlapSend.InternalHigh = 0; overlapSend.Offset = 0; overlapSend.OffsetHigh = 0; return PCSPAKInitOk; } //Win32 Serial support: ADB 01/15/07 Logical PCSerialPacket::TestInstance() { //return PCSPAKTestInstance(this); return 1; } //Win32 Serial support: ADB 02/10/07 //------------------------------------------------------------------------- // PCSPAKReceive() // Returns packet if available //------------------------------------------------------------------------- // Enter: // BYTE *destPtr = pointer to 128-byte destination buffer //------------------------------------------------------------------------- // Returns: // int packetSize (or zero if no packet available) //------------------------------------------------------------------------- int PCSerialPacket::ReceivePacket(BYTE *destPtr) // returns len (zero if none) { if(!enabled) return 0; //-------------------------------------------------------------- // Check character count: non-zero indicates entire packet //-------------------------------------------------------------- if(rxBuf.count == 0) return 0; //-------------------------------------------------------------- // Get buffer length //-------------------------------------------------------------- BYTE c = COM_GET_AL(&rxBuf); //get character into al //-------------------------------------------------------------- // Verify first char is a valid command character //-------------------------------------------------------------- if((c & 0x80) == 0) { DISABLE_AND_DIE("Character was not a command character!"); return 0; } BYTE *overflow = destPtr + COMBUFSIZE - 1; *destPtr = c; //save it destPtr++; BYTE length = rxLengthPtr[c & 0x7F]; //translate into length int count = length; //save in edx count++; //return at least one character assert(length < 256); //-------------------------------------------------------------- // Transfer data from buffer //-------------------------------------------------------------- do { c = COM_GET_AL(&rxBuf); //get character into al *destPtr = c; //save it destPtr++; assert(destPtr < overflow); }while(--length > 0); COM_ACCEPT_OUT(&rxBuf); //accept all 'taken' characters return count; } //Win32 Serial Support //------------------------------------------------------------------------- // PCSPAKSend() // Transmits packet //------------------------------------------------------------------------- // Enter: // BYTE *sp_src = pointer to (up to) 128-byte source buffer //------------------------------------------------------------------------- // Returns: // void //------------------------------------------------------------------------- void PCSerialPacket::SendPacket(BYTE *srcPtr) //sends packet { //-------------------------------------------------------------- // Wait for sufficient room //-------------------------------------------------------------- if(!enabled) //if disabled, don't even try return; BYTE cmd = *srcPtr; //get the command byte cmd &= 0x7F; //remove MSB int length = txLengthPtr[cmd]; //get the length in al length++; //add one for command byte //This code has been replaced for your sanity: //while(COM_SPACE_IN(&txBuf) < length) //enough room? // DISABLE_AND_DIE("Not enough space in serial packet buffer"); ////-------------------------------------------------------------- //// Copy data into tx buffer ////-------------------------------------------------------------- //do //{ // COM_PUT_AL(&txBuf, *srcPtr); //place it in the buffer // srcPtr++; //increment the source pointer //} //while(--length > 0); //COM_ACCEPT_IN(&txBuf); //accept all 'placed' characters // ////call StartSending ;attempt to start tx interrupt //Sane code: BYTE *buffer = new BYTE[length + 1];//add one for the checksum memcpy(&buffer[0], srcPtr, length);//copy packet buffer[length] = 0;//initialize checksum for(int b=0; brxMaximum<<"\n"; //initialize com port event OVERLAPPED o; o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); o.Internal = 0; o.InternalHigh = 0; o.Offset = 0; o.OffsetHigh = 0; HANDLE events[] = {pcPack->hRxAbortEvent, o.hEvent}; while(true) { DWORD dwEvent; //DEBUG_STREAM<<"Waiting for Data...\n"<hComm, &dwEvent, &o); if(WaitForMultipleObjects(2, &events[0], FALSE, INFINITE) == WAIT_OBJECT_0)//indicates that the abort event was triggered if(pcPack->bThreadAbort)//if we really are supposed to abort break;//then abort //perform read BYTE buffer[COMBUFSIZE]; memset(&buffer, 0, COMBUFSIZE); DWORD bytesRead = 0; //DEBUG_STREAM << "Reading Data\n" << std::flush; ReadFile(pcPack->hComm, &buffer[0], COMBUFSIZE, NULL, &o); GetOverlappedResult(pcPack->hComm, &o, &bytesRead, TRUE); //process data //DEBUG_STREAM<<"Received "<rxFlags |= RX_ACK_BIT;//yes, flag for transmitter goto recvDone; rcNotAck: //------------------------------ // NAK //------------------------------ if((unsigned int)buffer[b] != NAK_CHAR)//NAK character? goto rcNotNak;//no pcPack->rxFlags |= RX_NAK_BIT;//yes, flag for transmitter goto recvDone; rcNotNak: rcNotSpecial: //-------------------------------------------------------------- // Process state-dependent characters //-------------------------------------------------------------- if(pcPack->rxState != 0)//test the state goto rxActive;//non-zero, so active //-------------------------------------------------------------- // Receiver "idle" state // Wait for command character //-------------------------------------------------------------- if(((unsigned int)buffer[b] & 0x0080) != 0)//is it a control character? goto rxIdleSpecial;//yes, process it pcPack->Error |= RX_SEQ_ERR;//Not expecting data, so error. goto recvDone; rxIdleSpecial: if((unsigned int)buffer[b] == RESTART_CHAR)//restart? goto recvDone;//yes, discard it unsigned int bx = pcPack->COM_SPACE_IN(&pcPack->rxBuf);//get remaining space if(bx != 0)//zero? goto rxIdleSpace;//no, continue DEBUG_STREAM<<"RX Buffer Full!"<Error |= RX_COM_ERR;//error. Set flag. goto recvDone; rxIdleSpace: unsigned int ah = (unsigned int)buffer[b];//move to ah for testing ah &= 0x007F;//remove MSB if(ah < pcPack->rxMaximum)//legitimate command? goto rxIdleOk;//yes, continue DEBUG_STREAM<<"RX Unknown Command!"<Error |= RX_COM_ERR;//no. goto recvDone; rxIdleOk: pcPack->COM_PUT_AL(&pcPack->rxBuf, buffer[b]);//save command character in buffer pcPack->rxChecksum = buffer[b];//start a new receive checksum pcPack->rxCount = pcPack->rxLengthPtr[ah] + 1;//save the length pcPack->rxState = RX_ACTIVE_STATE;//change to "active" state goto recvDone; //-------------------------------------------------------------- // Receiver "active" state // Place N characters into buffer, test checksum, and reply //-------------------------------------------------------------- rxActive: if(((unsigned int)buffer[b] & 0x0080) == 0)//control character? goto rxActiveData;//no, therefore data pcPack->Error |= RX_SEQ_ERR;//yes, error. Set error flag pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data //------------------------------ // Process control char //------------------------------ if((unsigned int)buffer[b] == RESTART_CHAR)//restart character? goto rxActiveAbandon;//yes, abandon received data pcPack->txReply = NAK_CHAR;//error, tell transmitter to send NAK goto rxActiveReply; //------------------------------ // Process data //------------------------------ rxActiveData: pcPack->rxCount--;//check the receive count if(pcPack->rxCount <= 0) goto rxActiveCheck;//if end of message, process checksum //------------------------------ // Enough space in buffer? //------------------------------ bx = pcPack->COM_SPACE_IN(&pcPack->rxBuf);//get remaining space if(bx != 0)//zero? goto rxActiveSpace;//no, continue DEBUG_STREAM<<"RX Buffer Full @ 2!"<Error |= RX_COM_ERR;//error. Set flag. pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data goto rxActiveAbandon;//give up on packet rxActiveSpace: //------------------------------ // Save data in buffer //------------------------------ pcPack->rxChecksum += buffer[b];//update the checksum pcPack->COM_PUT_AL(&pcPack->rxBuf, buffer[b]);//save the character goto recvDone; //------------------------------ // Test checksum //------------------------------ rxActiveCheck: unsigned int bl = pcPack->rxChecksum;//get the checksum bl &= 0x007F;//remove MSB if(bl == buffer[b])//checksum OK? goto rxActiveOk;//yes! //------------------------------ // Buffer error //------------------------------ pcPack->Error |= RX_BAD_CHECKSUM;//error. Set flag. pcPack->COM_RESTORE_IN(&pcPack->rxBuf);//Discard received data pcPack->txReply = NAK_CHAR;//tell transmitter to send NAK TransmitCommChar(pcPack->hComm, (char)NAK_CHAR); goto rxActiveReply; //------------------------------ // Buffer OK //------------------------------ rxActiveOk: pcPack->COM_ACCEPT_IN(&pcPack->rxBuf);//accept the received data pcPack->txReply = ACK_CHAR;//tell transmitter to send ACK TransmitCommChar(pcPack->hComm, (char)ACK_CHAR); rxActiveReply: //call StartSending ;restart transmitter if needed //------------------------------ // Return to inactive state //------------------------------ rxActiveAbandon: pcPack->rxState = RX_IDLE_STATE; recvDone: ; } //DEBUG_STREAM<<"}\n"<