uartmd.c
上传用户:dahaojd
上传日期:2008-01-29
资源大小:14357k
文件大小:22k
- /*
- * Copyright 2003 by Texas Instruments Incorporated.
- * All rights reserved. Property of Texas Instruments Incorporated.
- * Restricted rights to use, duplicate or disclose this code are
- * granted through contract.
- *
- */
- /* "@(#) DDK 1.11.00.00 11-04-03 (ddk-b13)" */
- /*
- * ======== uartmd.c ========
- */
- #include <std.h>
- #include <stdlib.h>
- #include <string.h>
- #include <atm.h>
- #include <hwi.h>
- #include <iom.h>
- #include <que.h>
- #include <uartmd.h>
- #include <uarthw.h>
- #include <circ.h>
- #define INPUT 0
- #define OUTPUT 1
- #define NUMCHANS 2
- /*
- * SUPPORTPACKEDCHARS is used for devices that are not byte-addressable
- * where you want to use the UART to transfer full 16-bits of data. A
- * character is 16-bits on the 54x and 55x. SUPPORTPACKEDCHARS and the
- * UARTMD_DevParams.packedChars flag are used to specify that full 16-bits
- * of word should be output. This is useful when using UART to transfer
- * data (not ASCII). UARTs typically support only 8-bit transfers so we
- * need to do 2 transfers per 16-bit word.
- */
- #if defined(_54_) || defined(_55_)
- #define SUPPORTPACKEDCHARS 1
- #else
- #define SUPPORTPACKEDCHARS 0
- #endif
- /*
- * There is one UartChanObj per direction. This mini-driver must be
- * opened for input and output separately (does not support IOM_INOUT).
- */
- typedef struct UartChanObj {
- Uns inUse; /* TRUE if channel is in use */
- Int mode; /* INPUT or OUTPUT */
- struct UartPortObj *port; /* to support multiple UART ports */
-
- IOM_Packet *dataPacket; /* current active I/O packet */
- Char *bufptr; /* pointer within current buf */
- Uns bufcnt; /* size of remaining I/O job */
- QUE_Obj pendList; /* IOM_Packets pending I/O go here */
- CIRC_Obj circ; /* circular buffer */
- IOM_TiomCallback cbFxn; /* to notify client when I/O complete */
- Ptr cbArg; /* argument for cbFxn() */
- #if SUPPORTPACKEDCHARS
- Bool packedChars; /* TRUE => output all 16 bits */
- Bool halfWay; /* TRUE if we're between 1/2 words */
- Char halfWord; /* holds 1/2 word */
- #endif
-
- } UartChanObj, *UartChanHandle;
- /*
- * There is one UartPortObj per UART.
- * This mini-driver supports 'NUMPORTS' UART.
- */
- typedef struct UartPortObj {
- UARTHW_Handle hUart;
- UARTMD_TnotifyHandler notifyFunc;
- Uns evtMask;
- UartChanObj chans[NUMCHANS];
- } UartPortObj, *UartPortHandle;
- /*
- * Forward declaration of IOM mini driver interface functions. These
- * are only exposed via the IOM function table to avoid namespace pollution.
- */
- static Int mdBindDev(Ptr *devp, Int devid, Ptr bindParams);
- static Int mdControlChan(Ptr chanp, Uns cmd, Ptr arg);
- static Int mdCreateChan(Ptr *mdChan, Ptr drvhandle, String name, Int mode,
- Ptr optArgs, IOM_TiomCallback cbFxn, Ptr cbArg);
- static Int mdDeleteChan(Ptr chanp);
- static Int mdSubmitChan(Ptr chanp, IOM_Packet *packet);
- /*
- * Control functions. These functions are called by mdControl() for
- * assorted control commmands.
- */
- static Int controlNotify(UartChanHandle chan, UARTMD_NotifyStruct* notify);
- /*
- * Submit functions. These functions are called by mdSubmitChan() for
- * assorted submit commands.
- */
- static Int submitAbort(UartChanHandle chan, IOM_Packet *packet);
- static Int submitFlush(UartChanHandle chan, IOM_Packet *packet);
- static Int submitRead(UartChanHandle chan, IOM_Packet *packet);
- static Int submitWrite(UartChanHandle chan, IOM_Packet *packet);
- /*
- * Callback functions. These functions are called by the low-level UARTHW
- * ISR for different event types.
- */
- static Void cbLineStatus(UartPortHandle port, Int lsrVal);
- static Void cbModemStatus(UartPortHandle port, Int msrVal);
- static Void cbRxHandler(UartPortHandle port, Int rxVal);
- static Void cbTxHandler(UartPortHandle port);
- /*
- * Support functions.
- */
- static Void getNextPacket(UartChanHandle chan);
- /*
- * Public Mini Driver interface table.
- */
- IOM_Fxns UARTMD_FXNS =
- {
- &mdBindDev,
- IOM_UNBINDDEVNOTIMPL,
- &mdControlChan,
- &mdCreateChan,
- &mdDeleteChan,
- &mdSubmitChan,
- };
- /*
- * These functions are called by the UARTHW code.
- */
- static UARTHW_Tcallback cbFxns[4] = {
- (UARTHW_Tcallback)cbModemStatus,
- (UARTHW_Tcallback)cbTxHandler,
- (UARTHW_Tcallback)cbRxHandler,
- (UARTHW_Tcallback)cbLineStatus
- };
- /* This mini-driver supports 'NUMPORTS' UARTs. */
- #ifdef _64_
- #define NUMPORTS 2 /* 2 ports for dual UART on EVMDM642 board */
- #else
- #define NUMPORTS 1
- #endif
- static UartPortObj ports[NUMPORTS];
- /*
- * ======== UARTMD_init ========
- * UARTMD_init() initializes the data structures used by this mini-driver.
- */
- Void UARTMD_init(Void)
- {
- Int i, j;
- /* initialize all uartPortObj fields to '0' */
- memset(ports, 0, sizeof(ports));
- for (i=0; i < NUMPORTS; i++) {
- for (j=0; j < NUMCHANS; j++) {
- /* initialize port->chans */
- QUE_new(&ports[i].chans[j].pendList);
- CIRC_new(&ports[i].chans[j].circ);
- ports[i].chans[j].port = &ports[i];
- }
- }
- }
- /*
- * ======== mdBindDev ========
- * mdBindDev() is called by DEV_init() to bind and initialize the hardware.
- */
- static Int mdBindDev(Ptr *devp, Int devid, Ptr devParams)
- {
- UARTMD_DevParams *params = (UARTMD_DevParams *)devParams;
- UARTMD_DevParams defaultParams = UARTMD_DEVPARAMS_DEFAULT;
- UARTHW_Handle hUart;
- if ((Uns)devid > NUMPORTS-1) {
- return (IOM_EBADARGS);
- }
- if (params == NULL) {
- params = &defaultParams;
- }
- /* Check if the version number is supported */
- if (params->versionId != UARTMD_VERSION_1){
- /* Unsupported version */
- return(IOM_EBADARGS);
- }
- #if SUPPORTPACKEDCHARS
- ports[devid].chans[INPUT].packedChars = params->packedChars;
- ports[devid].chans[OUTPUT].packedChars = params->packedChars;
- #endif
- hUart = UARTHW_open(devid, params->uarthwParams, &ports[devid], cbFxns);
- if (hUart != NULL) {
- ports[devid].hUart = hUart;
- *devp = &ports[devid];
- return (IOM_COMPLETED);
- }
- else {
- return (IOM_EBADIO);
- }
- }
- /*
- * ======== mdControlChan ========
- * The Mini driver ctrl function. Catch all for adding device or vendor
- * specific functionality to a mini driver.
- */
- static Int mdControlChan(Ptr chanp, Uns cmd, Ptr arg)
- {
- UartChanHandle chan = (UartChanHandle)chanp;
- UARTHW_Handle hUart = chan->port->hUart;
- Int status;
- if (cmd == UARTMD_REGISTER_NOTIFY) {
- status = controlNotify(chan, arg);
- }
- else if (cmd == UARTMD_SETBREAK) {
- status = UARTHW_setBreak(hUart, ArgToInt(arg));
- }
- else if (cmd == UARTMD_GETMODEMSTATUS) {
- status = UARTHW_getModemStatus(hUart, (char*)arg);
- }
- else if (cmd == UARTMD_SETRTS) {
- status = UARTHW_setRTS(hUart, ArgToInt(arg));
- }
- else if (cmd == UARTMD_SETDTR) {
- status = UARTHW_setDTR(hUart, ArgToInt(arg));
- }
- else {
- status = IOM_ENOTIMPL;
- }
-
- return (status);
- }
- /*
- * ======== mdCreateChan ========
- */
- static Int mdCreateChan(Ptr *chanp, Ptr devp, String name, Int mode,
- Ptr chanParams, IOM_TiomCallback cbFxn, Ptr cbArg)
- {
- UartChanHandle chan;
- UartPortHandle port = (UartPortHandle)devp;
-
- if (mode == IOM_INPUT) {
- chan = &port->chans[INPUT];
- chan->mode = INPUT;
- }
- else if (mode == IOM_OUTPUT) {
- chan = &port->chans[OUTPUT];
- chan->mode = OUTPUT;
- }
- else {
- return (IOM_EBADMODE);
- }
-
- if (ATM_setu(&chan->inUse, TRUE)) {
- return (IOM_EINUSE);
- }
- /*
- * Save the callback function and argument. cbFxn() is called every
- * time an I/O job completes.
- */
- chan->cbFxn = cbFxn;
- chan->cbArg = cbArg;
- /* chanp will be passed to subsequent mini-driver calls */
- *chanp = chan;
-
- return (IOM_COMPLETED);
- }
- /*
- * ======== mdDeleteChan ========
- * Deletes an instance of the UART channel.
- * All I/O jobs must be completed prior to calling mdDelete().
- */
- static Int mdDeleteChan(Ptr chanp)
- {
- UartChanHandle chan = (UartChanHandle)chanp;
-
- chan->inUse = FALSE;
-
- return (IOM_COMPLETED);
- }
- /*
- * ======== mdSubmitChan ========
- * The main entry point to the mini driver for read / write operations.
- * mdSubmitChan() also handles the flush and abort operations.
- */
- static Int mdSubmitChan(Ptr chanp, IOM_Packet *packet)
- {
- Uns cmd = (packet->cmd);
- Int status;
-
- if (cmd == IOM_READ){
- status = submitRead(chanp, packet);
- }
- else if (cmd == IOM_WRITE){
- status = submitWrite(chanp, packet);
- }
- else if (cmd == IOM_ABORT){
- status = submitAbort(chanp, packet);
- }
- else if (cmd == IOM_FLUSH){
- status = submitFlush(chanp, packet);
- }
- else {
- status = IOM_ENOTIMPL;
- }
-
- return (status);
- }
- /*
- * -------- control functions --------
- */
- /*
- * ======== controlNotify ========
- * The local routine to handle application call to set the
- * notification callback and events for which it desires
- * notifications.
- */
- static Int controlNotify(UartChanHandle chan, UARTMD_NotifyStruct * notify)
- {
- chan->port->evtMask = notify->evtMask;
- chan->port->notifyFunc = notify->notifyFunc;
-
- return (IOM_COMPLETED);
- }
- /*
- * -------- submit functions --------
- */
- /*
- * ======== submitAbort ========
- * The local routine to handle an IOM_ABORT command.
- */
- static Int submitAbort(UartChanHandle chan, IOM_Packet *packet)
- {
- IOM_Packet *dataPacket;
- Uns imask;
- CIRC_Handle circ = &chan->circ;
- /*
- * Atomically save dataPacket and set chan->dataPacket to NULL.
- * 'chan->dataPacket' is used to synchronize with the ISR. If the
- * ISR sees chan->dataPacket == NULL, it will not attempt to
- * reload any packets from the pendList.
- */
- imask = HWI_disable();
- CIRC_reset(circ);
- dataPacket = chan->dataPacket;
- chan->dataPacket = NULL; /* stop all active I/O */
- #if SUPPORTPACKEDCHARS
- chan->halfWay = FALSE;
- #endif
-
- HWI_restore(imask);
- /*
- * Return all packets in order with their status tagged as aborted.
- * Since chan->dataPacket was set to NULL above, we don't need to
- * worry about synchronizing with the ISR.
- */
- /*
- * Return active dataPacket first.
- */
- if (dataPacket != NULL) {
- dataPacket->status = IOM_ABORTED;
- chan->cbFxn(chan->cbArg, dataPacket);
- }
- /*
- * Now remove remaining packets from the pending list and call
- * the callback one at a time. We use QUE_get() here for code size
- * savings, but we could use QUE_dequeue() since ISR will not
- * reference this queue.
- */
- dataPacket = QUE_get(&chan->pendList);
- while (dataPacket != (IOM_Packet *)&chan->pendList) {
- dataPacket->status = IOM_ABORTED;
- chan->cbFxn(chan->cbArg, dataPacket);
- dataPacket = QUE_get(&chan->pendList);
- }
- packet->status = IOM_COMPLETED;
- return (IOM_COMPLETED);
- }
- /*
- * ======== submitFlush ========
- * The local routine to handle an IOM_FLUSH command.
- */
- static Int submitFlush(UartChanHandle chan, IOM_Packet *packet)
- {
- Uns imask;
- Int status;
-
- /*
- * Abort the current read operation.
- * Wait for all output operations to complete in order.
- */
-
- /*
- * Abort the current read operations
- */
- if (chan->mode == IOM_INPUT) {
- /* abort and flush are equivalent for input channels */
- return (submitAbort(chan, packet));
- }
- else {
- imask = HWI_disable();
- /*
- * If output is in process, add 'flush' packet to pendList.
- * txIsr will handle flush packet when all output is complete.
- */
- if (chan->dataPacket) {
- packet->status = IOM_PENDING;
- QUE_enqueue(&chan->pendList, packet);
- status = IOM_PENDING;
- }
- else {
- status = IOM_COMPLETED;
- }
- HWI_restore(imask);
- }
- return (status);
- }
- /*
- * ======== submitRead ========
- * The local routine to handle application reads.
- */
- static Int submitRead(UartChanHandle chan, IOM_Packet *packet)
- {
- CIRC_Handle circ = &chan->circ;
- UARTHW_Handle hUart = chan->port->hUart;
- Uns imask;
-
- /*
- * Disable interrupts since several of these variables are
- * shared with the ISR.
- */
- imask = HWI_disable();
- /*
- * If chan->dataPacket is non-NULL, then there is already a packet
- * in process. Simply enqueue the new packet and return IOM_PENDING.
- */
- if (chan->dataPacket) {
- QUE_enqueue(&chan->pendList, packet);
- HWI_restore(imask);
- return (IOM_PENDING);
- }
- /*
- * Update local bufptr and bufcnt variables from new packet. We
- * don't set 'chan->dataPacket' until we are sure that no other
- * characters exist in the circular buffer. The ISR will then
- * put characters directly in 'chan->dataPacket'.
- */
- chan->bufptr = packet->addr;
- chan->bufcnt = packet->size;
- /*
- * This loop will copy characters from circular buffer one at a
- * time until the read is satisfied or there are no more characters
- * in the circular buffer. Interrupts are disabled while this
- * loop executes, but window is opened at bottom of loop to minimize
- * interrupt latency.
- */
- for (;;) {
- /*
- * If enough characters were available in the circular buffer
- * to satisfy read then return IOM_COMPLETED.
- */
- if (chan->bufcnt == 0) {
- HWI_restore(imask);
- return (IOM_COMPLETED);
- }
- /*
- * If no more characters exist in the CIRC, set chan->dataPacket
- * and return IOM_PENDING. Remember that chan->dataPacket is
- * used to synchronize with the ISR.
- */
- if (CIRC_fullCount(circ) == 0) {
- chan->dataPacket = packet;
- HWI_restore(imask);
- return (IOM_PENDING);
- }
- /*
- * Read a character from the circular buffer and decrement the count.
- */
- *chan->bufptr++ = CIRC_readChar(circ);
- chan->bufcnt--;
- UARTHW_enableRx(hUart);
- /* open window for interrupt(s) */
- HWI_restore(imask);
- imask = HWI_disable();
- }
- }
- /*
- * ======== submitWrite ========
- * The local routine to handle application writes.
- */
- static Int submitWrite(UartChanHandle chan, IOM_Packet *packet)
- {
- Int status;
- Uns imask;
- CIRC_Handle circ = &chan->circ;
- UARTHW_Handle hUart = chan->port->hUart;
-
- imask = HWI_disable();
- /*
- * If there's a packet in progress, 'chan->dataPacket' will be non-NULL.
- * Just queue up the new job and return IOM_PENDING.
- */
- if (chan->dataPacket) {
- QUE_enqueue(&chan->pendList, packet);
- HWI_restore(imask);
- return (IOM_PENDING);
- }
- /*
- * Update local bufptr and bufcnt variables from new packet. We
- * don't set 'chan->dataPacket' until we are sure that there's no
- * more room in the circular buffer. Unlike the rxIsr, the txIsr
- * always takes characters from the circular buffer.
- */
- chan->bufptr = packet->addr;
- chan->bufcnt = packet->size;
- for (;;) {
- /*
- * 'chan->bufcnt' will reach 0 if we were able to copy all characters
- * to the circular buffer. Return IOM_COMPLETED in this case.
- */
- if (chan->bufcnt == 0) {
- status = IOM_COMPLETED;
- break;
- }
- /*
- * Set 'chan->dataPacket' and return IOM_PENDING if there is
- * no more room for characters in the the circular buffer. The
- * txIsr will copy the remaining characters to the circular buffer
- * as room becomes available.
- */
- if (CIRC_emptyCount(circ) == 0) {
- chan->dataPacket = packet;
- status = IOM_PENDING;
- break;
- }
- /*
- * Put character into circular buffer and decrement count.
- */
- CIRC_writeChar(circ, *chan->bufptr++);
- chan->bufcnt--;
- /* Open the window for interrupt(s) */
- HWI_restore(imask);
- imask = HWI_disable();
- }
- /*
- * Interrupts must be disabled here since UART may have only
- * one ISR for both rx and tx. An rx ISR may call cbTxHandler()
- * and cbTxHandler() is not reentrant.
- */
- if (UARTHW_txEmpty(hUart)) {
- cbTxHandler(chan->port);
- }
- HWI_restore(imask);
- return (status);
- }
- /*
- * -------- callback functions --------
- */
- /*
- * ======== cbLineStatus ========
- * The interrupt handler routine for a line status change.
- */
- static Void cbLineStatus(UartPortHandle port, Int lsrVal)
- {
- Uns evtMask = port->evtMask;
- Uns evtWord = 0;
-
- if (!port->notifyFunc) {
- return;
- }
-
- if ((evtMask & UARTMD_EVT_BREAK) && (UARTMD_LSR_BRKMASK & lsrVal)) {
- evtWord |= UARTMD_EVT_BREAK;
- }
-
- if ((evtMask & UARTMD_EVT_PERR) && (UARTMD_LSR_PEMASK & lsrVal)) {
- evtWord |= UARTMD_EVT_PERR;
- }
-
- if ((evtMask & UARTMD_EVT_FERR) && (UARTMD_LSR_FEMASK & lsrVal)) {
- evtWord |= UARTMD_EVT_FERR;
- }
-
- if ((evtMask & UARTMD_EVT_OERR) && (UARTMD_LSR_OEMASK & lsrVal)) {
- evtWord |= UARTMD_EVT_OERR;
- }
-
- if (evtWord) {
- (*port->notifyFunc)(evtWord, 0);
- }
- }
- /*
- * ======== cbModemStatus ========
- * The interrupt handler routine for a modem status change.
- */
- static Void cbModemStatus (UartPortHandle port, Int msrVal)
- {
- Uns evtMask = port->evtMask;
- Uns evtWord = 0;
-
- if (!port->notifyFunc) {
- return;
- }
-
- if ((evtMask & UARTMD_EVT_CTSCHANGE) && (UARTMD_MSR_CTSCHGMASK & msrVal)) {
- evtWord |= UARTMD_EVT_CTSCHANGE;
- }
-
- if ((evtMask & UARTMD_EVT_DSRCHANGE) && (UARTMD_MSR_DSRCHGMASK & msrVal)) {
- evtWord |= UARTMD_EVT_DSRCHANGE;
- }
-
- if (evtWord) {
- (*port->notifyFunc)(evtWord, msrVal);
- }
- }
- /*
- * ======== cbRxHandler ========
- * The interrupt handler routine for a receive data ready.
- */
- static Void cbRxHandler(UartPortHandle port, Int c)
- {
- UartChanHandle chan = &port->chans[INPUT];
- CIRC_Handle circ = &chan->circ;
- UARTHW_Handle hUart = port->hUart;
- #if SUPPORTPACKEDCHARS
- if (chan->packedChars) {
- if (chan->halfWay) {
- c = (c << 8) | chan->halfWord;
- chan->halfWay = FALSE;
- }
- else {
- chan->halfWord = c;
- chan->halfWay = TRUE;
- return; /* only 1/2 way there, just return */
- }
- }
- #endif
- if (chan->dataPacket) {
- *chan->bufptr++ = (Char)c;
- chan->bufcnt--;
- if (chan->bufcnt == 0) {
- getNextPacket(chan);
- }
- }
- else if (CIRC_emptyCount(circ)) {
- CIRC_writeChar(circ, (Char)c);
- }
- else {
- /* SYS_abort("cbRxHandler: Rx overrun"); */
- }
- if (CIRC_isFull(circ)) {
- UARTHW_disableRx(hUart);
- }
- }
- /*
- * ======== cbTxHandler ========
- * The interrupt handler routine for a transmit buffer empty.
- * This function is also called from submitWrite() to get the data flowing.
- */
- static Void cbTxHandler(UartPortHandle port)
- {
- Char c;
- UartChanHandle chan = &port->chans[OUTPUT];
- CIRC_Handle circ = &chan->circ;
- UARTHW_Handle hUart = port->hUart;
-
- #if SUPPORTPACKEDCHARS
- if (chan->packedChars && chan->halfWay) {
- UARTHW_writeChar(hUart, chan->halfWord);
- chan->halfWay = FALSE;
- }
- else {
- #else
- {
- #endif
- if (CIRC_fullCount(circ) > 0) {
- c = CIRC_readChar(circ);
- UARTHW_writeChar(hUart, c);
- #if SUPPORTPACKEDCHARS
- if (chan->packedChars) {
- chan->halfWay = TRUE;
- chan->halfWord = (c >> 8);
- }
- #endif
- }
- if (chan->dataPacket) {
- CIRC_writeChar(circ, *chan->bufptr++);
- chan->bufcnt--;
- if (chan->bufcnt == 0) {
- getNextPacket(chan);
- }
- }
- }
- }
- /*
- * -------- support functions --------
- */
- /*
- * ======== getNextPacket ========
- */
- static Void getNextPacket(UartChanHandle chan)
- {
- IOM_Packet *donePacket, *nextPacket, *flushPacket;
- /* Intialize flushPacket */
- flushPacket = NULL;
- /* Save complete packet for callback */
- donePacket = chan->dataPacket;
- /* get next data packet */
- nextPacket = QUE_get(&chan->pendList);
- /*
- * If nextPacket points to head of the queue, then the list is
- * empty so there's no pending I/O packets.
- */
- if (nextPacket != (IOM_Packet *)&chan->pendList) {
- /* Check if packet is a FLUSH packet */
- if (nextPacket->cmd != IOM_FLUSH) {
- /* Set address and size of next packet */
- chan->bufptr = nextPacket->addr;
- chan->bufcnt = nextPacket->size;
- chan->dataPacket = nextPacket;
- }
- else {
- /* Set flushPacket and clear dataPacket */
- flushPacket = nextPacket;
- chan->dataPacket = NULL;
- }
- }
- else {
- chan->dataPacket = NULL;
- }
- /* Call the callback for the completed packet */
- donePacket->status = IOM_COMPLETED;
- chan->cbFxn(chan->cbArg, donePacket);
-
- /*
- * Call the callback for flushPacket *after* the callback for
- * the completed packet.
- */
- if (flushPacket != NULL) {
- flushPacket->status = IOM_COMPLETED;
- chan->cbFxn(chan->cbArg, flushPacket);
- }
- }