/* | |
This serial port driver is borrowed heavily from DZComm. I have | |
simplified it by removing a lot of the functionality (hardware | |
flow control, etc.). For more details and the full version see | |
http://dzcomm.sourceforge.net | |
*/ | |
/* | |
FreeRTOS V8.0.1 - Copyright (C) 2014 Real Time Engineers Ltd. | |
All rights reserved | |
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. | |
*************************************************************************** | |
* * | |
* FreeRTOS provides completely free yet professionally developed, * | |
* robust, strictly quality controlled, supported, and cross * | |
* platform software that has become a de facto standard. * | |
* * | |
* Help yourself get started quickly and support the FreeRTOS * | |
* project by purchasing a FreeRTOS tutorial book, reference * | |
* manual, or both from: http://www.FreeRTOS.org/Documentation * | |
* * | |
* Thank you! * | |
* * | |
*************************************************************************** | |
This file is part of the FreeRTOS distribution. | |
FreeRTOS is free software; you can redistribute it and/or modify it under | |
the terms of the GNU General Public License (version 2) as published by the | |
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. | |
>>! NOTE: The modification to the GPL is included to allow you to !<< | |
>>! distribute a combined work that includes FreeRTOS without being !<< | |
>>! obliged to provide the source code for proprietary components !<< | |
>>! outside of the FreeRTOS kernel. !<< | |
FreeRTOS 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. Full license text is available from the following | |
link: http://www.freertos.org/a00114.html | |
1 tab == 4 spaces! | |
*************************************************************************** | |
* * | |
* Having a problem? Start by reading the FAQ "My application does * | |
* not run, what could be wrong?" * | |
* * | |
* http://www.FreeRTOS.org/FAQHelp.html * | |
* * | |
*************************************************************************** | |
http://www.FreeRTOS.org - Documentation, books, training, latest versions, | |
license and Real Time Engineers Ltd. contact details. | |
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, | |
including FreeRTOS+Trace - an indispensable productivity tool, a DOS | |
compatible FAT file system, and our tiny thread aware UDP/IP stack. | |
http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High | |
Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS | |
licenses offer ticketed support, indemnification and middleware. | |
http://www.SafeRTOS.com - High Integrity Systems also provide a safety | |
engineered and independently SIL3 certified version for use in safety and | |
mission critical applications that require provable dependability. | |
1 tab == 4 spaces! | |
*/ | |
#include <stdlib.h> | |
#include <dos.h> | |
#include "FreeRTOS.h" | |
#include "queue.h" | |
#include "task.h" | |
#include "semphr.h" | |
#include "portasm.h" | |
#define serMAX_IRQs ( 16 ) | |
#define serTRANSMIT_HOLD_EMPTY_INT ( 0x02 ) | |
#define serCOM1_STANDARD_IRQ ( ( unsigned char ) 4 ) | |
#define serCOM2_STANDARD_IRQ ( ( unsigned char ) 3 ) | |
#define serIMR_8259_0 ( ( unsigned char ) 0x21 ) | |
#define serIMR_8259_1 ( ( unsigned char ) 0xa1 ) | |
#define serISR_8259_0 ( ( unsigned char ) 0x20 ) | |
#define serISR_8259_1 ( ( unsigned char ) 0xa0 ) | |
#define serALL_COMS_INTERRUPTS ( ( unsigned char ) 0x0f ) | |
#define serALL_MODEM_CTRL_INTERRUPTS ( ( unsigned char ) 0x0f ) | |
#define serTRANSMIT_HOLD_OFFSET ( 0 ) | |
#define serRECEIVE_DATA_OFFSET ( 0 ) | |
#define serBAUD_RATE_DIVISOR_LOW_OFFSET ( 0 ) | |
#define serBAUD_RATE_DIVISOR_HIGH_OFFSET ( 1 ) | |
#define serINTERRUPT_ENABLE_OFFSET ( 1 ) | |
#define serINTERRUPT_ID_OFFSET ( 2 ) | |
#define serFIFO_CTRL_OFFSET ( 2 ) | |
#define serLINE_CTRL_OFFSET ( 3 ) | |
#define serMODEM_CTRL_OFFSET ( 4 ) | |
#define serLINE_STATUS_OFFSET ( 5 ) | |
#define serMODEM_STATUS_OFFSET ( 6 ) | |
#define serSCR_OFFSET ( 7 ) | |
#define serMAX_BAUD ( ( unsigned long ) 115200UL ) | |
#define serNO_INTERRUPTS ( 0x00 ) | |
#define vInterruptOn( pxPort, ucInterrupt ) \ | |
{ \ | |
unsigned char ucIn = portINPUT_BYTE( pxPort->usInterruptEnableReg ); \ | |
if( !( ucIn & ucInterrupt ) ) \ | |
{ \ | |
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, ucIn | ucInterrupt ); \ | |
} \ | |
} | |
/*-----------------------------------------------------------*/ | |
#define vInterruptOff( pxPort, ucInterrupt ) \ | |
{ \ | |
unsigned char ucIn = portINPUT_BYTE( pxPort->usInterruptEnableReg ); \ | |
if( ucIn & ucInterrupt ) \ | |
{ \ | |
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, ucIn & ~ucInterrupt); \ | |
} \ | |
} | |
/*-----------------------------------------------------------*/ | |
typedef enum | |
{ | |
serCOM1, | |
serCOM2, | |
serCOM3, | |
serCOM4, | |
serCOM5, | |
serCOM6, | |
serCOM7, | |
serCOM8 | |
} eCOMPort; | |
typedef enum | |
{ | |
serNO_PARITY, | |
serODD_PARITY, | |
serEVEN_PARITY, | |
serMARK_PARITY, | |
serSPACE_PARITY | |
} eParity; | |
typedef enum | |
{ | |
serSTOP_1, | |
serSTOP_2 | |
} eStopBits; | |
typedef enum | |
{ | |
serBITS_5, | |
serBITS_6, | |
serBITS_7, | |
serBITS_8 | |
} eDataBits; | |
typedef enum | |
{ | |
ser50, | |
ser75, | |
ser110, | |
ser134, | |
ser150, | |
ser200, | |
ser300, | |
ser600, | |
ser1200, | |
ser1800, | |
ser2400, | |
ser4800, | |
ser9600, | |
ser19200, | |
ser38400, | |
ser57600, | |
ser115200 | |
} eBaud; | |
/* This *MUST* match the order in the eBaud definition. */ | |
unsigned long ulBaudFromEnum[] = | |
{ | |
( unsigned long ) 50, | |
( unsigned long ) 75, | |
( unsigned long ) 110, | |
( unsigned long ) 134, | |
( unsigned long ) 150, | |
( unsigned long ) 200, | |
( unsigned long ) 300, | |
( unsigned long ) 600, | |
( unsigned long ) 1200, | |
( unsigned long ) 1800, | |
( unsigned long ) 2400, | |
( unsigned long ) 4800, | |
( unsigned long ) 9600, | |
( unsigned long ) 19200, | |
( unsigned long ) 38400UL, | |
( unsigned long ) 57600UL, | |
( unsigned long ) 115200UL | |
}; | |
typedef struct xCOM_PORT | |
{ | |
unsigned short sPort; /* comm port address eg. 0x3f8 */ | |
unsigned char ucIRQ; /* comm IRQ eg. 3 */ | |
/* Next two fields used for setting up the IRQ routine and | |
* (un)masking the interrupt in certain circumstances. | |
*/ | |
unsigned short usIRQVector; | |
unsigned char ucInterruptEnableMast; | |
/* Read/Write buffers. */ | |
QueueHandle_t xRxedChars; | |
QueueHandle_t xCharsForTx; | |
/* This lot are set up to minimise CPU time where accessing the comm | |
* port's registers. | |
*/ | |
unsigned short usTransmitHoldReg; | |
unsigned short usReceiveDataRegister; | |
unsigned short usBaudRateDivisorLow; | |
unsigned short usBaudRateDivisorHigh; | |
unsigned short usInterruptEnableReg; | |
unsigned short usInterruptIDReg; | |
unsigned short usFIFOCtrlReg; | |
unsigned short usLineCtrlReg; | |
unsigned short usModemCtrlReg; | |
unsigned short usLineStatusReg; | |
unsigned short usModemStatusReg; | |
unsigned short usSCRReg; | |
unsigned short us8259InterruptServiceReg; | |
unsigned short us8259InterruptMaskReg; | |
/* This semaphore does nothing useful except test a feature of the | |
scheduler. */ | |
SemaphoreHandle_t xTestSem; | |
} xComPort; | |
typedef xComPort *xComPortHandle; | |
/* A xComPort structure can be associated with each IRQ. Initially none | |
are create/installed. */ | |
xComPort *xPortStatus[ serMAX_IRQs ] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; | |
/*-----------------------------------------------------------*/ | |
/* These prototypes are repeated here so we don't have to include the serial header. This allows | |
the xComPortHandle structure details to be private to this file. */ | |
xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength ); | |
portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, TickType_t xBlockTime ); | |
portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, TickType_t xBlockTime ); | |
portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort ); | |
static void prvSetupPortHardware( xComPort *pxPort, eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits ); | |
static short sComPortISR( const xComPort * const pxPort ); | |
/*-----------------------------------------------------------*/ | |
/* Define an interrupt handler for each slot in the xPortStatus array. */ | |
#define COM_IRQ_WRAPPER(N) \ | |
static void __interrupt COM_IRQ##N##_WRAPPER( void ) \ | |
{ \ | |
portDISABLE_INTERRUPTS(); \ | |
if( sComPortISR( xPortStatus[##N##] ) ) \ | |
{ \ | |
portSWITCH_CONTEXT(); \ | |
} \ | |
} | |
COM_IRQ_WRAPPER( 0 ) | |
COM_IRQ_WRAPPER( 1 ) | |
COM_IRQ_WRAPPER( 2 ) | |
COM_IRQ_WRAPPER( 3 ) | |
COM_IRQ_WRAPPER( 4 ) | |
COM_IRQ_WRAPPER( 5 ) | |
COM_IRQ_WRAPPER( 6 ) | |
COM_IRQ_WRAPPER( 7 ) | |
COM_IRQ_WRAPPER( 8 ) | |
COM_IRQ_WRAPPER( 9 ) | |
COM_IRQ_WRAPPER( 10 ) | |
COM_IRQ_WRAPPER( 11 ) | |
COM_IRQ_WRAPPER( 12 ) | |
COM_IRQ_WRAPPER( 13 ) | |
COM_IRQ_WRAPPER( 14 ) | |
COM_IRQ_WRAPPER( 15 ) | |
static pxISR xISRs[ serMAX_IRQs ] = | |
{ | |
COM_IRQ0_WRAPPER, | |
COM_IRQ1_WRAPPER, | |
COM_IRQ2_WRAPPER, | |
COM_IRQ3_WRAPPER, | |
COM_IRQ4_WRAPPER, | |
COM_IRQ5_WRAPPER, | |
COM_IRQ6_WRAPPER, | |
COM_IRQ7_WRAPPER, | |
COM_IRQ8_WRAPPER, | |
COM_IRQ9_WRAPPER, | |
COM_IRQ10_WRAPPER, | |
COM_IRQ11_WRAPPER, | |
COM_IRQ12_WRAPPER, | |
COM_IRQ13_WRAPPER, | |
COM_IRQ14_WRAPPER, | |
COM_IRQ15_WRAPPER | |
}; | |
static pxISR xOldISRs[ serMAX_IRQs ] = { NULL }; | |
/*-----------------------------------------------------------*/ | |
xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength ) | |
{ | |
xComPort *pxPort; | |
/* Create a structure to handle this port. */ | |
pxPort = ( xComPort * ) pvPortMalloc( sizeof( xComPort ) ); | |
if( pxPort != NULL ) | |
{ | |
/* Create the queues used by the comtest task. */ | |
pxPort->xRxedChars = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) ); | |
pxPort->xCharsForTx = xQueueCreate( uxBufferLength, ( unsigned portBASE_TYPE ) sizeof( char ) ); | |
/* Create the test semaphore. This does nothing useful except test a feature of the scheduler. */ | |
vSemaphoreCreateBinary( pxPort->xTestSem ); | |
prvSetupPortHardware( pxPort, ePort, eWantedBaud, eWantedParity, eWantedDataBits, eWantedStopBits ); | |
return pxPort; | |
} | |
return NULL; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvSetupPortHardware( xComPort *pxPort, eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits ) | |
{ | |
short sIn; | |
unsigned long ulDivisor; | |
unsigned char ucDivisorLow; | |
unsigned char ucDivisorHigh; | |
unsigned char ucCommParam; | |
/* IRQ numbers - standard */ | |
if( ( ePort == serCOM1 ) || ( ePort == serCOM3 ) || ( ePort == serCOM5 ) || ( ePort == serCOM7 ) ) | |
{ | |
pxPort->ucIRQ = serCOM1_STANDARD_IRQ; | |
pxPort->sPort = 0x3f8; | |
} | |
else | |
{ | |
pxPort->ucIRQ = serCOM2_STANDARD_IRQ; | |
pxPort->sPort = 0x2f8; | |
} | |
/* Set up variables in port making it easy to see which sIn/o address is which */ | |
pxPort->usTransmitHoldReg = pxPort->sPort + serTRANSMIT_HOLD_OFFSET; | |
pxPort->usReceiveDataRegister = pxPort->sPort + serRECEIVE_DATA_OFFSET; | |
pxPort->usBaudRateDivisorLow = pxPort->sPort + serBAUD_RATE_DIVISOR_LOW_OFFSET; | |
pxPort->usBaudRateDivisorHigh = pxPort->sPort + serBAUD_RATE_DIVISOR_HIGH_OFFSET; | |
pxPort->usInterruptEnableReg = pxPort->sPort + serINTERRUPT_ENABLE_OFFSET; | |
pxPort->usInterruptIDReg = pxPort->sPort + serINTERRUPT_ID_OFFSET; | |
pxPort->usFIFOCtrlReg = pxPort->sPort + serFIFO_CTRL_OFFSET; | |
pxPort->usLineCtrlReg = pxPort->sPort + serLINE_CTRL_OFFSET; | |
pxPort->usModemCtrlReg = pxPort->sPort + serMODEM_CTRL_OFFSET; | |
pxPort->usLineStatusReg = pxPort->sPort + serLINE_STATUS_OFFSET; | |
pxPort->usModemStatusReg = pxPort->sPort + serMODEM_STATUS_OFFSET; | |
pxPort->usSCRReg = pxPort->sPort + serSCR_OFFSET; | |
/* Set communication parameters. */ | |
ulDivisor = serMAX_BAUD / ulBaudFromEnum[ eWantedBaud ]; | |
ucDivisorLow = ( unsigned char ) ulDivisor & ( unsigned char ) 0xff; | |
ucDivisorHigh = ( unsigned char ) ( ( ( unsigned short ) ulDivisor >> 8 ) & 0xff ); | |
switch( eWantedParity ) | |
{ | |
case serNO_PARITY: ucCommParam = 0x00; | |
break; | |
case serODD_PARITY: ucCommParam = 0x08; | |
break; | |
case serEVEN_PARITY: ucCommParam = 0x18; | |
break; | |
case serMARK_PARITY: ucCommParam = 0x28; | |
break; | |
case serSPACE_PARITY: ucCommParam = 0x38; | |
break; | |
default: ucCommParam = 0x00; | |
break; | |
} | |
switch ( eWantedDataBits ) | |
{ | |
case serBITS_5: ucCommParam |= 0x00; | |
break; | |
case serBITS_6: ucCommParam |= 0x01; | |
break; | |
case serBITS_7: ucCommParam |= 0x02; | |
break; | |
case serBITS_8: ucCommParam |= 0x03; | |
break; | |
default: ucCommParam |= 0x03; | |
break; | |
} | |
if( eWantedStopBits == serSTOP_2 ) | |
{ | |
ucCommParam |= 0x04; | |
} | |
/* Reset UART into known state - Thanks to Bradley Town */ | |
portOUTPUT_BYTE( pxPort->usLineCtrlReg, 0x00 ); /* Access usTransmitHoldReg/RBR/usInterruptEnableReg */ | |
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, 0x00 ); /* Disable interrupts from UART */ | |
portOUTPUT_BYTE( pxPort->usModemCtrlReg, 0x04 ); /* Enable some multi-port cards */ | |
/* Code based on stuff from SVAsync lib. Clear UART Status and data registers | |
setting up FIFO if possible */ | |
sIn = portINPUT_BYTE( pxPort->usSCRReg ); | |
portOUTPUT_BYTE( pxPort->usSCRReg, 0x55 ); | |
if( portINPUT_BYTE( pxPort->usSCRReg ) == 0x55 ) | |
{ | |
/* The chip is better than an 8250 */ | |
portOUTPUT_BYTE( pxPort->usSCRReg, sIn ); /* Set usSCRReg back to what it was before */ | |
portINPUT_BYTE( pxPort->usSCRReg); /* Give slow motherboards a chance */ | |
/* Try and start the FIFO. It appears that some chips need a two call | |
protocol, but those that don't seem to work even if you do start it twice. | |
The first call is simply to start it, the second starts it and sets an 8 | |
byte FIFO trigger level. */ | |
portOUTPUT_BYTE( pxPort->usFIFOCtrlReg, 0x01 ); | |
portINPUT_BYTE( pxPort->usFIFOCtrlReg ); /* Give slow motherboards a chance to catch up */ | |
portOUTPUT_BYTE( pxPort->usFIFOCtrlReg, 0x87 ); | |
/* Check that the FIFO initialised */ | |
if( ( portINPUT_BYTE( pxPort->usInterruptIDReg ) & 0xc0 ) != 0xc0 ) | |
{ | |
/* It didn't so we assume it isn't there but disable it to be on the | |
safe side. */ | |
portOUTPUT_BYTE( pxPort->usInterruptIDReg, 0xfe ); | |
} | |
} | |
/* End of (modified) SVAsync code. | |
Set interrupt parameters calculating mask for 8259 controller's | |
IMR and number of interrupt handler for given irq level */ | |
if (pxPort->ucIRQ <= 7) | |
{ | |
/* if 0<=irq<=7 first IMR address used */ | |
pxPort->ucInterruptEnableMast = ~(0x01 << pxPort->ucIRQ); | |
pxPort->usIRQVector = pxPort->ucIRQ + 8; | |
pxPort->us8259InterruptMaskReg = serIMR_8259_0; | |
pxPort->us8259InterruptServiceReg = serISR_8259_0; | |
} | |
else | |
{ | |
pxPort->ucInterruptEnableMast = ~( 0x01 << ( pxPort->ucIRQ % 8 ) ); | |
pxPort->usIRQVector = 0x70 + ( pxPort->ucIRQ - 8) ; | |
pxPort->us8259InterruptMaskReg = serIMR_8259_1; | |
pxPort->us8259InterruptServiceReg = serISR_8259_1; | |
} | |
/* Set Port Toggle to usBaudRateDivisorLow/usBaudRateDivisorHigh registers | |
to set baud rate */ | |
portOUTPUT_BYTE( pxPort->usLineCtrlReg, ucCommParam | 0x80 ); | |
portOUTPUT_BYTE( pxPort->usBaudRateDivisorLow, ucDivisorLow ); | |
portOUTPUT_BYTE( pxPort->usBaudRateDivisorHigh, ucDivisorHigh ); | |
/* reset usLineCtrlReg and Port Toggleout */ | |
portOUTPUT_BYTE( pxPort->usLineCtrlReg, ucCommParam & 0x7F ); | |
portENTER_CRITICAL(); | |
if( xPortStatus[ pxPort->ucIRQ ] == NULL ) | |
{ | |
xPortStatus[ pxPort->ucIRQ ] = pxPort; | |
} | |
xOldISRs[ pxPort->ucIRQ ] = _dos_getvect( pxPort->usIRQVector ); | |
_dos_setvect( pxPort->usIRQVector, xISRs[ pxPort->ucIRQ ] ); | |
/* enable interrupt pxPort->ucIRQ level */ | |
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, portINPUT_BYTE( pxPort->us8259InterruptMaskReg ) & pxPort->ucInterruptEnableMast ); | |
/* And allow interrupts again now the hairy bit's done */ | |
portEXIT_CRITICAL(); | |
/* This version does not allow flow control. */ | |
portOUTPUT_BYTE( pxPort->usModemCtrlReg, serALL_MODEM_CTRL_INTERRUPTS ); | |
/* enable all communication's interrupts */ | |
portOUTPUT_BYTE( pxPort->usInterruptEnableReg, serALL_COMS_INTERRUPTS ); | |
} | |
/*-----------------------------------------------------------*/ | |
static short sComPortISR( const xComPort * const pxPort ) | |
{ | |
short sInterruptID; | |
char cIn, cOut; | |
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; | |
extern void vComTestUnsuspendTask( void ); | |
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, ( portINPUT_BYTE( pxPort->us8259InterruptMaskReg) | ~pxPort->ucInterruptEnableMast ) ); | |
/* Decide which UART has issued the interrupt */ | |
sInterruptID = portINPUT_BYTE( pxPort->usInterruptIDReg ); | |
/* service whatever requests the calling UART may have. The top 4 bits are | |
either unused or indicate the presence of a functioning FIFO, which we don't | |
need to know. So trim them off to simplify the switch statement below. */ | |
sInterruptID &= 0x0f; | |
do | |
{ | |
switch( sInterruptID ) | |
{ | |
case 0x0c: /* Timeout | |
Called when FIFO not up to trigger level but no activity for | |
a while. Handled exactly as RDAINT, see below for | |
description. */ | |
do | |
{ | |
cIn = ( char ) portINPUT_BYTE( pxPort->usReceiveDataRegister ); | |
xQueueSendFromISR( pxPort->xRxedChars, &cIn, &xHigherPriorityTaskWoken ); | |
/* Also release the semaphore - this does nothing interesting and is just a test. | |
We first attempt to unsuspend the task to check the scheduler correctely detects | |
this as an invalid call, then give the semaphore for real. */ | |
vComTestUnsuspendTask(); | |
xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken ); | |
} while( portINPUT_BYTE( pxPort->usLineStatusReg ) & 0x01 ); | |
break; | |
case 0x06: /* LSINT */ | |
portINPUT_BYTE( pxPort->usLineStatusReg ); | |
break; | |
case 0x04: /* RDAINT */ | |
/* The usInterruptIDReg flag tested above stops when the | |
FIFO is below the trigger level rather than empty, whereas | |
this flag allows one to empty it: (do loop because there | |
must be at least one to read by virtue of having got here.) */ | |
do | |
{ | |
cIn = ( char ) portINPUT_BYTE( pxPort->usReceiveDataRegister ); | |
xQueueSendFromISR( pxPort->xRxedChars, &cIn, &xHigherPriorityTaskWoken ); | |
/* Also release the semaphore - this does nothing interesting and is just a test. | |
We first attempt to unsuspend the task to check the scheduler correctely detects | |
this as an invalid call, then give the semaphore for real. */ | |
vComTestUnsuspendTask(); | |
xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken ); | |
} while( portINPUT_BYTE( pxPort->usLineStatusReg ) & 0x01 ); | |
break; | |
case 0x02: /* serTRANSMIT_HOLD_EMPTY_INT */ | |
if( xQueueReceiveFromISR( pxPort->xCharsForTx, &cOut, &xHigherPriorityTaskWoken ) != pdTRUE ) | |
{ | |
/* Queue empty, nothing to send */ | |
vInterruptOff( pxPort, serTRANSMIT_HOLD_EMPTY_INT); | |
} | |
else | |
{ | |
portOUTPUT_BYTE( pxPort->usTransmitHoldReg, ( short ) cOut ); | |
} | |
break; | |
case 0x00: /* MSINT */ | |
portINPUT_BYTE( pxPort->usModemStatusReg ); | |
break; | |
} | |
/* Get the next instruction, trimming as above */ | |
sInterruptID = portINPUT_BYTE( pxPort->usInterruptIDReg ) & 0x0f; | |
} while( !( sInterruptID & 0x01 ) ); | |
if( pxPort->ucIRQ > 7 ) | |
{ | |
portOUTPUT_BYTE( 0xA0, 0x60 + ( pxPort->ucIRQ & 0x07 ) ); | |
portOUTPUT_BYTE( 0x20, 0x62); | |
} | |
else | |
{ | |
portOUTPUT_BYTE( 0x20, 0x60 + pxPort->ucIRQ ); | |
} | |
portOUTPUT_BYTE( pxPort->us8259InterruptMaskReg, portINPUT_BYTE( pxPort->us8259InterruptMaskReg ) & pxPort->ucInterruptEnableMast ); | |
/* If posting any of the characters to a queue woke a task that was blocked on | |
the queue we may want to return to the task just woken (depending on its | |
priority relative to the task this ISR interrupted. */ | |
return xHigherPriorityTaskWoken; | |
} | |
/*-----------------------------------------------------------*/ | |
portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, char *pcRxedChar, TickType_t xBlockTime ) | |
{ | |
/* Get the next character from the buffer, note that this routine is only | |
called having checked that the is (at least) one to get */ | |
if( xQueueReceive( pxPort->xRxedChars, pcRxedChar, xBlockTime ) ) | |
{ | |
return pdTRUE; | |
} | |
else | |
{ | |
return pdFALSE; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, char cOutChar, TickType_t xBlockTime ) | |
{ | |
if( xQueueSend( pxPort->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS ) | |
{ | |
return pdFAIL; | |
} | |
vInterruptOn( pxPort, serTRANSMIT_HOLD_EMPTY_INT ); | |
return pdPASS; | |
} | |
/*-----------------------------------------------------------*/ | |
void vSerialPutString( xComPortHandle pxPort, const char * const pcString, unsigned short usStringLength ) | |
{ | |
char * pcNextChar; | |
const TickType_t xNoBlock = ( TickType_t ) 0; | |
/* Stop warnings. */ | |
( void ) usStringLength; | |
pcNextChar = ( char * ) pcString; | |
while( *pcNextChar ) | |
{ | |
xSerialPutChar( pxPort, *pcNextChar, xNoBlock ); | |
pcNextChar++; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort ) | |
{ | |
const TickType_t xBlockTime = ( TickType_t ) 0xffff; | |
/* This function does nothing interesting, but test the | |
semaphore from ISR mechanism. */ | |
return xSemaphoreTake( xPort->xTestSem, xBlockTime ); | |
} | |
/*-----------------------------------------------------------*/ | |
void vSerialClose( xComPortHandle xPort ) | |
{ | |
portENTER_CRITICAL(); | |
/* Turn off the interrupts. */ | |
portOUTPUT_BYTE( xPort->usModemCtrlReg, serNO_INTERRUPTS ); | |
portOUTPUT_BYTE( xPort->usInterruptEnableReg, serNO_INTERRUPTS ); | |
/* Put back the original ISR. */ | |
_dos_setvect( xPort->usIRQVector, xOldISRs[ xPort->ucIRQ ] ); | |
/* Remove the reference in the array of xComPort structures. */ | |
xPortStatus[ xPort->ucIRQ ] = NULL; | |
/* Delete the queues. */ | |
vQueueDelete( xPort->xRxedChars ); | |
vQueueDelete( xPort->xCharsForTx ); | |
vPortFree( ( void * ) xPort ); | |
portEXIT_CRITICAL(); | |
} | |