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 + -
显示快捷键?