📄 serial.c
字号:
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 voidserial_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 voidserial_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_STATUSstatic voidserial_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_TRANSFERstatic rcv_req_reply_tserial_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 voidserial_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_tserial_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 voidserial_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 + -