⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serial.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 3 页
字号:
    case CYG_IO_SET_CONFIG_SERIAL_STATUS_CALLBACK:
        {
            cyg_serial_line_status_callback_fn_t newfn;
            CYG_ADDRWORD newpriv;
            cyg_serial_line_status_callback_t *tmp = 
                (cyg_serial_line_status_callback_t *)xbuf;
            
            if ( *len < sizeof(*tmp) )
                return -EINVAL;

            newfn = tmp->fn;
            newpriv = tmp->priv;

            // prevent callbacks while we do this
            cyg_drv_dsr_lock();
            // store old callbacks in same structure
            tmp->fn = chan->status_callback;
            tmp->priv = chan->status_callback_priv;
            chan->status_callback = newfn;
            chan->status_callback_priv = newpriv;
            cyg_drv_dsr_unlock();
            *len = sizeof(*tmp);
        }  
        break;
#endif

    default:
        // pass down to lower layers
        return (funs->set_config)(chan, key, xbuf, len);
    }
    return res;
}

// ---------------------------------------------------------------------------

static void
serial_xmt_char(serial_channel *chan)
{
    cbuf_t *cbuf = &chan->out_cbuf;
    serial_funs *funs = chan->funs;
    unsigned char c;
    int space;

#if CYGINT_IO_SERIAL_BLOCK_TRANSFER
    CYG_ASSERT(false == cbuf->block_mode_xfer_running,
               "Attempting char xmt while block transfer is running");
#endif
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
    // if we are required to send an XON/XOFF char, send it before
    // anything else
    // FIXME: what if XON gets corrupted in transit to the other end?
    // Should we resend XON even though the other end may not be wanting
    // to send us stuff at this point?
    if ( chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_RX ) {
        if ( chan->flow_desc.xchar ) {
            if ( (funs->putc)(chan, chan->flow_desc.xchar) ) {
                chan->flow_desc.xchar = '\0';
            } else {  // otherwise there's no space and we have to wait
                return;
            }
        }
    }
#endif
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
    // if we're meant to be throttled, just stop and leave
    if ( chan->flow_desc.flags & CYG_SERIAL_FLOW_OUT_THROTTLED ) {
        (funs->stop_xmit)(chan);  // Stop transmitting for now
        return;
    }
#endif
    while (cbuf->nb > 0) {
        c = cbuf->data[cbuf->get];
        if ((funs->putc)(chan, c)) {
            cbuf->get++;
            if (cbuf->get == cbuf->len) cbuf->get = 0;
            cbuf->nb--;
        } else {
            // See if there is now enough room to restart writer
            space = cbuf->len - cbuf->nb;
            if (space >= cbuf->low_water) {
                if (cbuf->waiting) {
                    cbuf->waiting = false;
                    cyg_drv_cond_broadcast(&cbuf->wait);
                }
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
                cyg_selwakeup( &cbuf->selinfo );
#endif                    
            }
            return;  // Need to wait for more space
        }
    }
    (funs->stop_xmit)(chan);  // Done with transmit

    // must signal waiters, and wake up selecters for the case when
    // this was the last char to be sent and they hadn't been signalled
    // before (e.g. because of flow control)
    if (cbuf->waiting) {
        cbuf->waiting = false;
        cyg_drv_cond_broadcast(&cbuf->wait);
    }
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
    cyg_selwakeup( &cbuf->selinfo );
#endif                    
}

// ---------------------------------------------------------------------------

static void
serial_rcv_char(serial_channel *chan, unsigned char c)
{
    cbuf_t *cbuf = &chan->in_cbuf;

#if CYGINT_IO_SERIAL_BLOCK_TRANSFER
    CYG_ASSERT(false == cbuf->block_mode_xfer_running,
               "Attempting char rcv while block transfer is running");
#endif
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
    // for software flow control, if the driver returns one of the characters
    // we act on it and then drop it (the app must not see it)
    if ( chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_TX ) {
        if ( c == CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR ) {
            throttle_tx( chan );
            return; // it wasn't a "real" character
        } else if ( c == CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR ) {
            restart_tx( chan );
            return; // it wasn't a "real" character
        }
    }
#endif    
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
    // If we've hit the high water mark, tell the other side to stop
    if ( cbuf->nb >= cbuf->high_water ) {
        throttle_rx( chan, false );
    }
#endif
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
    // Wake up any pending selectors if we are about to
    // put some data into a previously empty buffer.
    if( cbuf->nb == 0 )
        cyg_selwakeup( &cbuf->selinfo );
#endif

    // If the flow control is not enabled/sufficient and the buffer is
    // already full, just throw new characters away.

    if ( cbuf->nb < cbuf->len ) {
        cbuf->data[cbuf->put++] = c;
        if (cbuf->put == cbuf->len) cbuf->put = 0;
        cbuf->nb++;
    } // note trailing else
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
    else {
        // Overrun. Report the error.
        cyg_serial_line_status_t stat;
        stat.which = CYGNUM_SERIAL_STATUS_OVERRUNERR;
        serial_indicate_status(chan, &stat);
    }
#endif

    if (cbuf->waiting) {
#ifdef XX_CYGDBG_DIAG_BUF
            extern int enable_diag_uart;
            int _enable = enable_diag_uart;
            int _time, _stime;
            externC cyg_tick_count_t cyg_current_time(void);
            enable_diag_uart = 0;
            HAL_CLOCK_READ(&_time);
            _stime = (int)cyg_current_time();
            diag_printf("Signal reader - time: %x.%x\n", _stime, _time);
            enable_diag_uart = _enable;
#endif // CYGDBG_DIAG_BUF
        cbuf->waiting = false;
        cyg_drv_cond_broadcast(&cbuf->wait);
    }
}

//----------------------------------------------------------------------------
// Flow control indication callback

#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
static void
serial_indicate_status(serial_channel *chan, cyg_serial_line_status_t *s )
{
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
    if ( CYGNUM_SERIAL_STATUS_FLOW == s->which ) {
        if ( s->value )
            restart_tx( chan );
        else
            throttle_tx( chan );
    }
#endif
    if ( chan->status_callback )
        (*chan->status_callback)(s, chan->status_callback_priv);
}
#endif // ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS

//----------------------------------------------------------------------------
// Block transfer functions. Not all drivers require these. Those that
// do must follow the required semantics:
//
// Attempt to transfer as much via the block transfer function as
// possible, _but_ if that fails, do the remaining bytes via the
// single-char function. That ensures that all policy decisions can be
// made in this driver, and not in the device driver.
//
// Note: if the driver uses DMA for transmission, an initial failing
// call to the xmt_req function must cause the start_xmit function to
// fall-back to regular CPU-interrupt based single-character
// transmission.

#if CYGINT_IO_SERIAL_BLOCK_TRANSFER

static rcv_req_reply_t
serial_data_rcv_req(serial_channel *chan, int avail, 
                    int* space_avail, unsigned char** space)
{
    cbuf_t *cbuf = &chan->in_cbuf;
    int gap;

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
    // When there is software flow-control, force the serial device
    // driver to use the single-char xmt/rcv functions, since these
    // have to make policy decision based on the data. Rcv function
    // may also have to transmit data to throttle the xmitter.
    if (chan->config.flags & (CYGNUM_SERIAL_FLOW_XONXOFF_TX|CYGNUM_SERIAL_FLOW_XONXOFF_RX))
        return CYG_RCV_DISABLED;
#endif

    CYG_ASSERT(false == cbuf->block_mode_xfer_running,
               "Attempting new block transfer while another is running");
    // Check for space
    gap = cbuf->nb;
    if (gap == cbuf->len)
        return CYG_RCV_FULL;

#ifdef CYGDBG_USE_ASSERTS
    cbuf->block_mode_xfer_running = true;
#endif

    if (0 == gap) {
        // Buffer is empty. Reset put/get indexes to get max transfer in
        // one chunk.
        cbuf->get = 0;
        cbuf->put = 0;
        gap = cbuf->len;
    } else {
        // Free space (G = get, P = put, x = data, . = empty)
        //  positive: xxxxP.....Gxxx
        //  negative: ..GxxxxxP.....        [offer last chunk only]

        // First try for a gap between put and get locations
        gap = cbuf->get - cbuf->put;
        if (gap < 0) {
            // If failed, the gap is between put and the end of buffer
            gap = cbuf->len - cbuf->put;
        }
    }

    if (avail < gap) gap = avail;   // bound by what's available from hw
    
    *space_avail = gap;
    *space = &cbuf->data[cbuf->put];

    CYG_ASSERT((gap+cbuf->nb) <= cbuf->len, "Buffer will overflow");
    CYG_ASSERT(cbuf->put < cbuf->len, "Invalid put ptr");
    CYG_ASSERT(cbuf->get < cbuf->len, "Invalid get ptr");

    return CYG_RCV_OK;
}

static void
serial_data_rcv_done(serial_channel *chan, int chars_rcvd)
{
    cbuf_t *cbuf = &chan->in_cbuf;

    cbuf->put += chars_rcvd;
    cbuf->nb += chars_rcvd;

    if (cbuf->put == cbuf->len) cbuf->put = 0;

    CYG_ASSERT(cbuf->nb <= cbuf->len, "Buffer overflow");
    CYG_ASSERT(cbuf->put < cbuf->len, "Invalid put ptr");
    CYG_ASSERT(cbuf->get < cbuf->len, "Invalid get ptr");

    if (cbuf->waiting) {
        cbuf->waiting = false;
        cyg_drv_cond_broadcast(&cbuf->wait);
    }
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
    // If we've hit the high water mark, tell the other side to stop
    if ( cbuf->nb >= cbuf->high_water ) {
        throttle_rx( chan, false );
    }
#endif
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
    // Wake up any pending selectors if we have
    // put some data into a previously empty buffer.
    if (chars_rcvd == cbuf->nb)
        cyg_selwakeup( &cbuf->selinfo );
#endif

#ifdef CYGDBG_USE_ASSERTS
    cbuf->block_mode_xfer_running = false;
#endif
}

static xmt_req_reply_t
serial_data_xmt_req(serial_channel *chan, int space,
                    int* chars_avail, unsigned char** chars)
{
    cbuf_t *cbuf = &chan->out_cbuf;
    int avail;

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
    // When there is software flow-control, force the serial device
    // driver to use the single-char xmt/rcv functions, since these
    // have to make policy decision based on the data. Rcv function
    // may also have to transmit data to throttle the xmitter.
    if (chan->config.flags & (CYGNUM_SERIAL_FLOW_XONXOFF_TX|CYGNUM_SERIAL_FLOW_XONXOFF_RX))
        return CYG_XMT_DISABLED;
#endif

    CYG_ASSERT(false == cbuf->block_mode_xfer_running,
               "Attempting new block transfer while another is running");

#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
    // if we're meant to be throttled, just stop and leave
    if ( chan->flow_desc.flags & CYG_SERIAL_FLOW_OUT_THROTTLED ) {
        (chan->funs->stop_xmit)(chan);  // Stop transmitting for now
        return CYG_XMT_EMPTY;
    }
#endif

    // Available data (G = get, P = put, x = data, . = empty)
    //  0:        no data
    //  negative: xxxxP.....Gxxx        [offer last chunk only]
    //  positive: ..GxxxxxP.....
    if (0 == cbuf->nb)
        return CYG_XMT_EMPTY;

#ifdef CYGDBG_USE_ASSERTS
    cbuf->block_mode_xfer_running = true;
#endif

    if (cbuf->get >= cbuf->put) {
        avail = cbuf->len - cbuf->get;
    } else {
        avail = cbuf->put - cbuf->get;
    }

    if (avail > space) avail = space;   // bound by space in hardware
    
    *chars_avail = avail;
    *chars = &cbuf->data[cbuf->get];

    CYG_ASSERT(avail <= cbuf->len, "Avail overflow");
    CYG_ASSERT(cbuf->nb <= cbuf->len, "Buffer overflow");
    CYG_ASSERT(cbuf->put < cbuf->len, "Invalid put ptr");
    CYG_ASSERT(cbuf->get < cbuf->len, "Invalid get ptr");

    return CYG_XMT_OK;
}

static void
serial_data_xmt_done(serial_channel *chan, int chars_sent)
{
    cbuf_t *cbuf = &chan->out_cbuf;
    serial_funs *funs = chan->funs;
    int space;

    cbuf->get += chars_sent;
    cbuf->nb -= chars_sent;

    if (cbuf->get == cbuf->len) cbuf->get = 0;

    CYG_ASSERT(cbuf->nb <= cbuf->len, "Buffer overflow");
    CYG_ASSERT(cbuf->nb >= 0, "Buffer underflow");
    CYG_ASSERT(cbuf->put < cbuf->len, "Invalid put ptr");
    CYG_ASSERT(cbuf->get < cbuf->len, "Invalid get ptr");

    if (0 == cbuf->nb) {
        (funs->stop_xmit)(chan);  // Done with transmit
        cbuf->get = cbuf->put = 0; // reset ptrs if empty
    }

    // See if there is now enough room to restart writer
    space = cbuf->len - cbuf->nb;
    if (space >= cbuf->low_water) {
        if (cbuf->waiting) {
            cbuf->waiting = false;
            cyg_drv_cond_broadcast(&cbuf->wait);
        }
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
        cyg_selwakeup( &cbuf->selinfo );
#endif                    
    }

#ifdef CYGDBG_USE_ASSERTS
    cbuf->block_mode_xfer_running = false;
#endif
}

#endif // CYGINT_IO_SERIAL_BLOCK_TRANSFER

// ---------------------------------------------------------------------------

// EOF serial.c

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -