📄 serial.c
字号:
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 + -