sh_scif_serial.c
来自「eCos操作系统源码」· C语言 代码 · 共 1,079 行 · 第 1/3 页
C
1,079 行
sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv;#ifdef CYGDBG_IO_INIT diag_printf("SH SERIAL init - dev: %x.%d\n", sh_chan->ctrl_base, sh_chan->rx_int_num);#endif // Really only required for interrupt driven devices (chan->callbacks->serial_init)(chan); if (chan->out_cbuf.len != 0) { cyg_drv_interrupt_create(sh_chan->tx_int_num, 3, (cyg_addrword_t)chan, // Data item passed to interrupt handler sh_scif_tx_ISR, sh_scif_tx_DSR, &sh_chan->serial_tx_interrupt_handle, &sh_chan->serial_tx_interrupt); cyg_drv_interrupt_attach(sh_chan->serial_tx_interrupt_handle); cyg_drv_interrupt_unmask(sh_chan->tx_int_num); sh_chan->tx_enabled = false; } if (chan->in_cbuf.len != 0) { // Receive interrupt cyg_drv_interrupt_create(sh_chan->rx_int_num, 3, (cyg_addrword_t)chan, // Data item passed to interrupt handler sh_scif_rx_ISR, sh_scif_rx_DSR, &sh_chan->serial_rx_interrupt_handle, &sh_chan->serial_rx_interrupt); cyg_drv_interrupt_attach(sh_chan->serial_rx_interrupt_handle); // Receive error interrupt cyg_drv_interrupt_create(sh_chan->er_int_num, 3, (cyg_addrword_t)chan, // Data item passed to interrupt handler sh_scif_er_ISR, sh_scif_er_DSR, &sh_chan->serial_er_interrupt_handle, &sh_chan->serial_er_interrupt); cyg_drv_interrupt_attach(sh_chan->serial_er_interrupt_handle);#ifdef CYGINT_IO_SERIAL_SH_SCIF_BR_INTERRUPT // Break error interrupt cyg_drv_interrupt_create(sh_chan->br_int_num, 3, (cyg_addrword_t)chan, // Data item passed to interrupt handler sh_scif_er_ISR, sh_scif_er_DSR, &sh_chan->serial_br_interrupt_handle, &sh_chan->serial_br_interrupt); cyg_drv_interrupt_attach(sh_chan->serial_br_interrupt_handle);#endif // This unmasks all interrupt sources. cyg_drv_interrupt_unmask(sh_chan->rx_int_num); }#ifdef CYGINT_IO_SERIAL_SH_SCIF_DMA // Assign DMA channel and interrupt if requested if (sh_chan->dma_enable) { // FIXME: Need a cleaner way to assign DMA channels static int dma_channel = 0;#if defined(CYGPKG_HAL_SH_SH2) sh_chan->dma_xmt_int_num = dma_channel+CYGNUM_HAL_INTERRUPT_DMAC0_TE;#elif defined(CYGPKG_HAL_SH_SH3) sh_chan->dma_xmt_int_num = dma_channel+CYGNUM_HAL_INTERRUPT_DMAC_DEI0;#else# error "No interrupt defined for variant"#endif sh_chan->dma_xmt_base = (dma_channel*0x10)+CYGARC_REG_SAR0; dma_channel++; // Enable the DMA engines. HAL_WRITE_UINT16(CYGARC_REG_DMAOR, CYGARC_REG_DMAOR_DME); cyg_drv_interrupt_create(sh_chan->dma_xmt_int_num, 3, (cyg_addrword_t)chan, // Data item passed to interrupt handler sh_dma_xmt_ISR, sh_dma_xmt_DSR, &sh_chan->dma_xmt_interrupt_handle, &sh_chan->dma_xmt_interrupt); cyg_drv_interrupt_attach(sh_chan->dma_xmt_interrupt_handle); cyg_drv_interrupt_unmask(sh_chan->dma_xmt_int_num); } sh_chan->dma_xmt_running = false;#endif // CYGINT_IO_SERIAL_SH_SCIF_DMA sh_scif_config_port(chan, &chan->config, true); return true;}// This routine is called when the device is "looked" up (i.e. attached)static Cyg_ErrNo sh_scif_lookup(struct cyg_devtab_entry **tab, struct cyg_devtab_entry *sub_tab, const char *name){ serial_channel *chan = (serial_channel *)(*tab)->priv; // Really only required for interrupt driven devices (chan->callbacks->serial_init)(chan); return ENOERR;}// Send a character to the device output buffer.// Return 'true' if character is sent to devicestatic boolsh_scif_putc(serial_channel *chan, unsigned char c){ cyg_uint16 _fdr, _sr; sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv; HAL_READ_UINT16(sh_chan->ctrl_base+SCIF_SCFDR, _fdr); if (((_fdr & CYGARC_REG_SCIF_SCFDR_TCOUNT_MASK) >> CYGARC_REG_SCIF_SCFDR_TCOUNT_shift) < 15) {// Transmit FIFO has room for another char HAL_WRITE_UINT8(sh_chan->ctrl_base+SCIF_SCFTDR, c); // Clear FIFO-empty/transmit end flags (read back sr first) HAL_READ_UINT16(sh_chan->ctrl_base+SCIF_SCSSR, _sr); HAL_WRITE_UINT16(sh_chan->ctrl_base+SCIF_SCSSR, CYGARC_REG_SCIF_SCSSR_CLEARMASK & ~CYGARC_REG_SCIF_SCSSR_TDFE); return true; } else {// No space return false; }}// Fetch a character from the device input buffer, waiting if necessary// Note: Input is running wo FIFO enabled, so the counter is not checked here.static unsigned char sh_scif_getc(serial_channel *chan){ sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv; unsigned char c; cyg_uint16 _sr; do { HAL_READ_UINT16(sh_chan->ctrl_base+SCIF_SCSSR, _sr); } while ((_sr & CYGARC_REG_SCIF_SCSSR_RDF) == 0); HAL_READ_UINT8(sh_chan->ctrl_base+SCIF_SCFRDR, c); // Clear buffer full flag (read back first) HAL_READ_UINT16(sh_chan->ctrl_base+SCIF_SCSSR, _sr); HAL_WRITE_UINT16(sh_chan->ctrl_base+SCIF_SCSSR, CYGARC_REG_SCIF_SCSSR_CLEARMASK & ~CYGARC_REG_SCIF_SCSSR_RDF); return c;}// Set up the device characteristics; baud rate, etc.static Cyg_ErrNosh_scif_set_config(serial_channel *chan, cyg_uint32 key, const void *xbuf, cyg_uint32 *len){ switch (key) { case CYG_IO_SET_CONFIG_SERIAL_INFO: { cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf; if ( *len < sizeof(cyg_serial_info_t) ) { return -EINVAL; } *len = sizeof(cyg_serial_info_t); if ( true != sh_scif_config_port(chan, config, false) ) return -EINVAL; } break;#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW case CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE: { sh_scif_info *ser_chan = (sh_scif_info *)chan->dev_priv; cyg_addrword_t base = ser_chan->ctrl_base; cyg_uint32 *f = (cyg_uint32 *)xbuf; if ( *len < *f ) return -EINVAL; if ( chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX ) { // Control RX RTC/CTS flow control by disabling/enabling // RX interrupt. When disabled, FIFO will fill up and // clear RTS. cyg_uint8 _scscr; HAL_READ_UINT8(base+SCIF_SCSCR, _scscr); if (*f) // we should throttle _scscr &= ~CYGARC_REG_SCIF_SCSCR_RIE; else // we should no longer throttle _scscr |= CYGARC_REG_SCIF_SCSCR_RIE; HAL_WRITE_UINT8(base+SCIF_SCSCR, _scscr); }#ifdef CYGHWR_SH_SCIF_FLOW_DSRDTR if ( chan->config.flags & CYGNUM_SERIAL_FLOW_DSRDTR_RX ) { // Control RX DSR/DTR flow control via platform specific macro CYGHWR_SH_SCIF_FLOW_DSRDTR_RX(chan, *f); }#endif } break; case CYG_IO_SET_CONFIG_SERIAL_HW_FLOW_CONFIG: { // Handle CTS/RTS flag if ( chan->config.flags & (CYGNUM_SERIAL_FLOW_RTSCTS_RX | CYGNUM_SERIAL_FLOW_RTSCTS_TX )){ cyg_uint8 _scfcr; sh_scif_info *ser_chan = (sh_scif_info *)chan->dev_priv; cyg_addrword_t base = ser_chan->ctrl_base; cyg_uint8 *f = (cyg_uint8 *)xbuf; HAL_READ_UINT8(base+SCIF_SCFCR, _scfcr); if (*f) // enable RTS/CTS flow control _scfcr |= CYGARC_REG_SCIF_SCFCR_MCE; else // disable RTS/CTS flow control _scfcr &= ~CYGARC_REG_SCIF_SCFCR_MCE; HAL_WRITE_UINT8(base+SCIF_SCFCR, _scfcr); }#ifndef CYGHWR_SH_SCIF_FLOW_DSRDTR // Clear DSR/DTR flag as it's not supported. if (chan->config.flags & (CYGNUM_SERIAL_FLOW_DSRDTR_RX|CYGNUM_SERIAL_FLOW_DSRDTR_TX)) { chan->config.flags &= ~(CYGNUM_SERIAL_FLOW_DSRDTR_RX| CYGNUM_SERIAL_FLOW_DSRDTR_TX); return -EINVAL; }#else return CYGHWR_SH_SCIF_FLOW_DSRDTR_CONFIG(chan);#endif } break;#endif CYGPRI_DEVS_SH_SCIF_SET_CONFIG_PLF default: return -EINVAL; } return ENOERR;}#ifdef CYGINT_IO_SERIAL_SH_SCIF_DMA// Must be called with serial interrupts disabledstatic xmt_req_reply_tsh_scif_start_dma_xmt(serial_channel *chan){ int chars_avail; unsigned char* chars; xmt_req_reply_t res; // We can transfer the full buffer - ask how much to transfer res = (chan->callbacks->data_xmt_req)(chan, chan->out_cbuf.len, &chars_avail, &chars); if (CYG_XMT_OK == res) { sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv; cyg_uint32 dma_base = sh_chan->dma_xmt_base; cyg_uint32 scr; // Save the length so it can be used in the DMA DSR sh_chan->dma_xmt_len = chars_avail; // Flush cache for the area HAL_DCACHE_FLUSH((cyg_haladdress)chars, chars_avail); // Program DMA HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR, 0); // disable and clear HAL_WRITE_UINT32(dma_base+CYGARC_REG_SAR, (cyg_uint32)chars); HAL_WRITE_UINT32(dma_base+CYGARC_REG_DAR, (sh_chan->ctrl_base+SCIF_SCFTDR) & 0x0fffffff); HAL_WRITE_UINT32(dma_base+CYGARC_REG_DMATCR, chars_avail); // Source increments, dest static, byte transfer, enable // interrupt on completion. HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR, sh_chan->dma_xmt_cr_flags | CYGARC_REG_CHCR_SM0 \ | CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE); // Enable serial interrupts HAL_READ_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, scr); scr |= CYGARC_REG_SCIF_SCSCR_TIE; HAL_WRITE_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, scr); } return res;}// must be called with serial interrupts maskedstatic voidsh_scif_stop_dma_xmt(serial_channel *chan){ sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv; cyg_uint32 dma_base = sh_chan->dma_xmt_base; cyg_uint32 cr; // Disable DMA engine and interrupt enable flag. Should be safe // to do since it's triggered by the serial interrupt which has // already been disabled. HAL_READ_UINT32(dma_base+CYGARC_REG_CHCR, cr); cr &= ~(CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE); HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR, cr); // Did transfer complete? HAL_READ_UINT32(dma_base+CYGARC_REG_CHCR, cr); if (0 == (cr & CYGARC_REG_CHCR_TE)) { // Transfer incomplete. Report actually transferred amount of data // back to the serial driver. int chars_left; HAL_READ_UINT32(dma_base+CYGARC_REG_DMATCR, chars_left); CYG_ASSERT(chars_left > 0, "DMA incomplete, but no data left"); CYG_ASSERT(chars_left <= sh_chan->dma_xmt_len, "More data remaining than was attempted transferred"); (chan->callbacks->data_xmt_done)(chan, sh_chan->dma_xmt_len - chars_left); }#ifdef CYGDBG_USE_ASSERTS { cyg_uint32 dmaor; HAL_READ_UINT32(CYGARC_REG_DMAOR, dmaor); CYG_ASSERT(0== (dmaor & (CYGARC_REG_DMAOR_AE | CYGARC_REG_DMAOR_NMIF)), "DMA error"); }#endif // The DMA engine is free again. sh_chan->dma_xmt_running = false;}// Serial xmt DMA completion interrupt handler (ISR)static cyg_uint32 sh_dma_xmt_ISR(cyg_vector_t vector, cyg_addrword_t data){ serial_channel *chan = (serial_channel *)data; sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv; cyg_uint32 _cr; // mask serial interrupt HAL_READ_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, _cr); _cr &= ~CYGARC_REG_SCIF_SCSCR_TIE; // Disable xmit interrupt HAL_WRITE_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, _cr); // mask DMA interrupt and disable engine HAL_READ_UINT32(sh_chan->dma_xmt_base+CYGARC_REG_CHCR, _cr); _cr &= ~(CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE); HAL_WRITE_UINT32(sh_chan->dma_xmt_base+CYGARC_REG_CHCR, _cr); return CYG_ISR_CALL_DSR; // Cause DSR to be run}// Serial xmt DMA completion interrupt handler (DSR)static void sh_dma_xmt_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data){ serial_channel *chan = (serial_channel *)data; sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv; (chan->callbacks->data_xmt_done)(chan, sh_chan->dma_xmt_len); // Try to load the engine again. sh_chan->dma_xmt_running = (CYG_XMT_OK == sh_scif_start_dma_xmt(chan)) ? true : false;}#endif // CYGINT_IO_SERIAL_SH_SCIF_DMA
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?