From c3da9dda7bebabc547a30e8bba86fdca3e5f0546 Mon Sep 17 00:00:00 2001 From: armink Date: Sat, 14 May 2016 11:43:06 +0800 Subject: [PATCH] [DeviceDrivers] Add fifo mode to serial DMA rx when serial->config.bufsz != 0. --- components/drivers/serial/serial.c | 257 ++++++++++++++++++++++------- 1 file changed, 201 insertions(+), 56 deletions(-) diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c index dbdfc5e578..7cb1c6b339 100644 --- a/components/drivers/serial/serial.c +++ b/components/drivers/serial/serial.c @@ -27,9 +27,10 @@ * the size of ring buffer. * 2014-07-10 bernard rewrite serial framework * 2014-12-31 bernard use open_flag for poll_tx stream mode. - * 2015-05-19 Quintin fix DMA tx mod tx_dma->activated flag !=RT_FALSE BUG + * 2015-05-19 Quintin fix DMA tx mod tx_dma->activated flag !=RT_FALSE BUG * in open function. * 2015-11-10 bernard fix the poll rx issue when there is no data. + * 2016-05-10 armink add fifo mode to DMA rx when serial->config.bufsz != 0. */ #include @@ -37,13 +38,13 @@ #include /* - * Serial poll routines + * Serial poll routines */ rt_inline int _serial_poll_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length) { int ch; int size; - + RT_ASSERT(serial != RT_NULL); size = length; @@ -52,7 +53,7 @@ rt_inline int _serial_poll_rx(struct rt_serial_device *serial, rt_uint8_t *data, ch = serial->ops->getc(serial); if (ch == -1) break; - *data = ch; + *data = ch; data ++; length --; if (ch == '\n') break; @@ -77,9 +78,9 @@ rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t { serial->ops->putc(serial, '\r'); } - + serial->ops->putc(serial, *data); - + ++ data; -- length; } @@ -96,8 +97,8 @@ rt_inline int _serial_int_rx(struct rt_serial_device *serial, rt_uint8_t *data, struct rt_serial_rx_fifo* rx_fifo; RT_ASSERT(serial != RT_NULL); - size = length; - + size = length; + rx_fifo = (struct rt_serial_rx_fifo*) serial->serial_rx; RT_ASSERT(rx_fifo != RT_NULL); @@ -136,7 +137,7 @@ rt_inline int _serial_int_tx(struct rt_serial_device *serial, const rt_uint8_t * { int size; struct rt_serial_tx_fifo *tx; - + RT_ASSERT(serial != RT_NULL); size = length; @@ -157,32 +158,136 @@ rt_inline int _serial_int_tx(struct rt_serial_device *serial, const rt_uint8_t * return size - length; } +/** + * Calculate DMA received data length. + * + * @param serial serial device + * + * @return length + */ +static rt_size_t rt_dma_calc_recved_len(struct rt_serial_device *serial) { + static rt_size_t rx_length; + struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + + RT_ASSERT(rx_fifo != RT_NULL); + + rx_length = (rx_fifo->put_index >= rx_fifo->get_index)? (rx_fifo->put_index - rx_fifo->get_index): + (serial->config.bufsz - (rx_fifo->get_index - rx_fifo->put_index)); + return rx_length; +} + +/** + * Read data finish by DMA mode then update the gut index for receive fifo. + * + * @param serial serial device + * @param len get data length for this operate + */ +static void rt_dma_recv_update_get_index(struct rt_serial_device *serial, rt_size_t len) { + struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + + RT_ASSERT(rx_fifo != RT_NULL); + RT_ASSERT(len <= rt_dma_calc_recved_len(serial)); + + rx_fifo->get_index += len; + if (rx_fifo->get_index > serial->config.bufsz ) { + rx_fifo->get_index -= serial->config.bufsz; + } +} + +/** + * DMA received finish then update put index for receive fifo. + * + * @param serial serial device + * @param len received length for this transmit + */ +static void rt_dma_recv_update_put_index(struct rt_serial_device *serial, rt_size_t len) { + struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + rt_size_t i; + + RT_ASSERT(rx_fifo != RT_NULL); + + if (rx_fifo->get_index <= rx_fifo->put_index) { + rx_fifo->put_index += len; + /* beyond the fifo end */ + if (rx_fifo->put_index >= serial->config.bufsz) { + for (i = 0; i <= len / serial->config.bufsz; i++) { + rx_fifo->put_index -= serial->config.bufsz; + } + /* force overwrite get index */ + if (rx_fifo->put_index >= rx_fifo->get_index) { + rx_fifo->get_index = rx_fifo->put_index + 1; + } + } + } else { + rx_fifo->put_index += len; + if(rx_fifo->put_index >= rx_fifo->get_index) { + /* beyond the fifo end */ + if(rx_fifo->put_index >= serial->config.bufsz) { + for (i = 0; i <= len / serial->config.bufsz; i++) { + rx_fifo->put_index -= serial->config.bufsz; + } + } + /* force overwrite get index */ + rx_fifo->get_index = rx_fifo->put_index + 1; + } + } +} + /* * Serial DMA routines */ rt_inline int _serial_dma_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length) { rt_base_t level; - int result = RT_EOK; - struct rt_serial_rx_dma *rx_dma; RT_ASSERT((serial != RT_NULL) && (data != RT_NULL)); - rx_dma = (struct rt_serial_rx_dma*)serial->serial_rx; - RT_ASSERT(rx_dma != RT_NULL); level = rt_hw_interrupt_disable(); - if (rx_dma->activated != RT_TRUE) - { - rx_dma->activated = RT_TRUE; - serial->ops->dma_transmit(serial, data, length, RT_SERIAL_DMA_RX); - } - else result = -RT_EBUSY; - rt_hw_interrupt_enable(level); - if (result == RT_EOK) return length; + if (serial->config.bufsz == 0) { + int result = RT_EOK; + struct rt_serial_rx_dma *rx_dma; + + rx_dma = (struct rt_serial_rx_dma*)serial->serial_rx; + RT_ASSERT(rx_dma != RT_NULL); + + if (rx_dma->activated != RT_TRUE) + { + rx_dma->activated = RT_TRUE; + RT_ASSERT(serial->ops->dma_transmit != RT_NULL); + serial->ops->dma_transmit(serial, data, length, RT_SERIAL_DMA_RX); + } + else result = -RT_EBUSY; + rt_hw_interrupt_enable(level); + + if (result == RT_EOK) return length; + + rt_set_errno(result); + return 0; + } else { + struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + rt_size_t recv_len = 0, fifo_recved_len = rt_dma_calc_recved_len(serial); + + RT_ASSERT(rx_fifo != RT_NULL); - rt_set_errno(result); - return 0; + if (length < fifo_recved_len) { + recv_len = length; + } else { + recv_len = fifo_recved_len; + } + + if (rx_fifo->get_index + recv_len < serial->config.bufsz) { + rt_memcpy(data, rx_fifo->buffer + rx_fifo->get_index, recv_len); + } else { + rt_memcpy(data, rx_fifo->buffer + rx_fifo->get_index, + serial->config.bufsz - rx_fifo->get_index); + rt_memcpy(data + serial->config.bufsz - rx_fifo->get_index, rx_fifo->buffer, + recv_len + rx_fifo->get_index - serial->config.bufsz); + } + rt_dma_recv_update_get_index(serial, recv_len); + rt_hw_interrupt_enable(level); + return recv_len; + } } rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) @@ -192,8 +297,8 @@ rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t * struct rt_serial_tx_dma *tx_dma; tx_dma = (struct rt_serial_tx_dma*)(serial->serial_tx); - - result = rt_data_queue_push(&(tx_dma->data_queue), data, length, RT_WAITING_FOREVER); + + result = rt_data_queue_push(&(tx_dma->data_queue), data, length, RT_WAITING_FOREVER); if (result == RT_EOK) { level = rt_hw_interrupt_disable(); @@ -203,7 +308,7 @@ rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t * rt_hw_interrupt_enable(level); /* make a DMA transfer */ - serial->ops->dma_transmit(serial, data, length, RT_SERIAL_DMA_TX); + serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX); } else { @@ -250,7 +355,7 @@ static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) serial = (struct rt_serial_device *)dev; /* check device flag with the open flag */ - if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX)) + if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX)) return -RT_EIO; if ((oflag & RT_DEVICE_FLAG_DMA_TX) && !(dev->flag & RT_DEVICE_FLAG_DMA_TX)) return -RT_EIO; @@ -261,26 +366,41 @@ static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) /* get open flags */ dev->open_flag = oflag & 0xff; - + /* initialize the Rx/Tx structure according to open flag */ if (serial->serial_rx == RT_NULL) { if (oflag & RT_DEVICE_FLAG_DMA_RX) { - struct rt_serial_rx_dma* rx_dma; - - rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma)); - RT_ASSERT(rx_dma != RT_NULL); - rx_dma->activated = RT_FALSE; - - serial->serial_rx = rx_dma; + if (serial->config.bufsz == 0) { + struct rt_serial_rx_dma* rx_dma; + + rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma)); + RT_ASSERT(rx_dma != RT_NULL); + rx_dma->activated = RT_FALSE; + + serial->serial_rx = rx_dma; + } else { + struct rt_serial_rx_fifo* rx_fifo; + + rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) + + serial->config.bufsz); + RT_ASSERT(rx_fifo != RT_NULL); + rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1); + rt_memset(rx_fifo->buffer, 0, serial->config.bufsz); + rx_fifo->put_index = 0; + rx_fifo->get_index = 0; + serial->serial_rx = rx_fifo; + /* configure fifo address and length to low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX); + } dev->open_flag |= RT_DEVICE_FLAG_DMA_RX; } else if (oflag & RT_DEVICE_FLAG_INT_RX) { struct rt_serial_rx_fifo* rx_fifo; - rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) + + rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) + serial->config.bufsz); RT_ASSERT(rx_fifo != RT_NULL); rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1); @@ -308,7 +428,7 @@ static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma)); RT_ASSERT(tx_dma != RT_NULL); tx_dma->activated = RT_FALSE; - + rt_data_queue_init(&(tx_dma->data_queue), 8, 4, RT_NULL); serial->serial_tx = tx_dma; @@ -346,7 +466,7 @@ static rt_err_t rt_serial_close(struct rt_device *dev) /* this device has more reference count */ if (dev->ref_count > 1) return RT_EOK; - + if (dev->open_flag & RT_DEVICE_FLAG_INT_RX) { struct rt_serial_rx_fifo* rx_fifo; @@ -362,12 +482,23 @@ static rt_err_t rt_serial_close(struct rt_device *dev) } else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX) { - struct rt_serial_rx_dma* rx_dma; + if (serial->config.bufsz == 0) { + struct rt_serial_rx_dma* rx_dma; - rx_dma = (struct rt_serial_rx_dma*)serial->serial_tx; - RT_ASSERT(rx_dma != RT_NULL); + rx_dma = (struct rt_serial_rx_dma*)serial->serial_rx; + RT_ASSERT(rx_dma != RT_NULL); + + rt_free(rx_dma); + } else { + struct rt_serial_rx_fifo* rx_fifo; - rt_free(rx_dma); + rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_free(rx_fifo); + } + /* configure low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_RX); serial->serial_rx = RT_NULL; dev->open_flag &= ~RT_DEVICE_FLAG_DMA_RX; } @@ -376,7 +507,7 @@ static rt_err_t rt_serial_close(struct rt_device *dev) { struct rt_serial_tx_fifo* tx_fifo; - tx_fifo = (struct rt_serial_tx_fifo*)serial->serial_rx; + tx_fifo = (struct rt_serial_tx_fifo*)serial->serial_tx; RT_ASSERT(tx_fifo != RT_NULL); rt_free(tx_fifo); @@ -534,30 +665,30 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) ch = serial->ops->getc(serial); if (ch == -1) break; - + /* disable interrupt */ level = rt_hw_interrupt_disable(); - + rx_fifo->buffer[rx_fifo->put_index] = ch; rx_fifo->put_index += 1; if (rx_fifo->put_index >= serial->config.bufsz) rx_fifo->put_index = 0; - + /* if the next position is read index, discard this 'read char' */ if (rx_fifo->put_index == rx_fifo->get_index) { rx_fifo->get_index += 1; if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0; } - + /* enable interrupt */ rt_hw_interrupt_enable(level); } - + /* invoke callback */ if (serial->parent.rx_indicate != RT_NULL) { rt_size_t rx_length; - + /* get rx length */ level = rt_hw_interrupt_disable(); rx_length = (rx_fifo->put_index >= rx_fifo->get_index)? (rx_fifo->put_index - rx_fifo->get_index): @@ -584,19 +715,19 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) struct rt_serial_tx_dma* tx_dma; tx_dma = (struct rt_serial_tx_dma*) serial->serial_tx; - + rt_data_queue_pop(&(tx_dma->data_queue), &last_data_ptr, &data_size, 0); if (rt_data_queue_peak(&(tx_dma->data_queue), &data_ptr, &data_size) == RT_EOK) { /* transmit next data node */ tx_dma->activated = RT_TRUE; - serial->ops->dma_transmit(serial, data_ptr, data_size, RT_SERIAL_DMA_TX); + serial->ops->dma_transmit(serial, (rt_uint8_t *)data_ptr, data_size, RT_SERIAL_DMA_TX); } else { tx_dma->activated = RT_FALSE; } - + /* invoke callback */ if (serial->parent.tx_complete != RT_NULL) { @@ -607,13 +738,27 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) case RT_SERIAL_EVENT_RX_DMADONE: { int length; - struct rt_serial_rx_dma* rx_dma; - rx_dma = (struct rt_serial_rx_dma*)serial->serial_rx; /* get DMA rx length */ length = (event & (~0xff)) >> 8; - serial->parent.rx_indicate(&(serial->parent), length); - rx_dma->activated = RT_FALSE; + + if (serial->config.bufsz == 0) { + struct rt_serial_rx_dma* rx_dma; + + rx_dma = (struct rt_serial_rx_dma*)serial->serial_rx; + RT_ASSERT(rx_dma != RT_NULL); + + RT_ASSERT(serial->parent.rx_indicate != RT_NULL); + serial->parent.rx_indicate(&(serial->parent), length); + rx_dma->activated = RT_FALSE; + } else { + /* update fifo put index */ + rt_dma_recv_update_put_index(serial, length); + /* invoke callback */ + if (serial->parent.rx_indicate != RT_NULL) { + serial->parent.rx_indicate(&(serial->parent), rt_dma_calc_recved_len(serial)); + } + } break; } } -- GitLab