diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c index 83a039bcf0..914054eb35 100644 --- a/components/drivers/serial/serial.c +++ b/components/drivers/serial/serial.c @@ -1,364 +1,379 @@ -/* - * File : serial.c - * This file is part of RT-Thread RTOS - * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team - * - * The license and distribution terms for this file may be - * found in the file LICENSE in this distribution or at - * http://www.rt-thread.org/license/LICENSE - * - * Change Logs: - * Date Author Notes - * 2006-03-13 bernard first version - * 2012-05-15 lgnq modified according bernard's implementation. - * 2012-05-28 bernard code cleanup - */ - -#include -#include -#include - -rt_inline void serial_ringbuffer_init(struct serial_ringbuffer *rbuffer) -{ - rt_memset(rbuffer->buffer, 0, sizeof(rbuffer->buffer)); - rbuffer->put_index = 0; - rbuffer->get_index = 0; -} - -rt_inline void serial_ringbuffer_putc(struct serial_ringbuffer *rbuffer, char ch) -{ - rt_base_t level; - - /* disable interrupt */ - level = rt_hw_interrupt_disable(); - - rbuffer->buffer[rbuffer->put_index] = ch; - rbuffer->put_index = (rbuffer->put_index + 1) & (SERIAL_RBUFFER_SIZE - 1); - - /* if the next position is read index, discard this 'read char' */ - if (rbuffer->put_index == rbuffer->get_index) - { - rbuffer->get_index = (rbuffer->get_index + 1) & (SERIAL_RBUFFER_SIZE - 1); - } - - /* enable interrupt */ - rt_hw_interrupt_enable(level); -} - -rt_inline int serial_ringbuffer_putchar(struct serial_ringbuffer *rbuffer, char ch) -{ - rt_base_t level; - rt_uint16_t next_index; - - /* disable interrupt */ - level = rt_hw_interrupt_disable(); - - next_index = (rbuffer->put_index + 1) & (SERIAL_RBUFFER_SIZE - 1); - if (next_index != rbuffer->get_index) - { - rbuffer->buffer[rbuffer->put_index] = ch; - rbuffer->put_index = next_index; - } - else - { - /* enable interrupt */ - rt_hw_interrupt_enable(level); - return -1; - } - - /* enable interrupt */ - rt_hw_interrupt_enable(level); - return 1; -} - -rt_inline int serial_ringbuffer_getc(struct serial_ringbuffer *rbuffer) -{ - int ch; - rt_base_t level; - - ch = -1; - /* disable interrupt */ - level = rt_hw_interrupt_disable(); - if (rbuffer->get_index != rbuffer->put_index) - { - ch = rbuffer->buffer[rbuffer->get_index]; - rbuffer->get_index = (rbuffer->get_index + 1) & (SERIAL_RBUFFER_SIZE - 1); - } - /* enable interrupt */ - rt_hw_interrupt_enable(level); - return ch; -} - -rt_inline rt_uint32_t serial_ringbuffer_size(struct serial_ringbuffer *rbuffer) -{ - rt_uint32_t size; - rt_base_t level; - - level = rt_hw_interrupt_disable(); - size = (rbuffer->put_index - rbuffer->get_index) & (SERIAL_RBUFFER_SIZE - 1); - rt_hw_interrupt_enable(level); - - return size; -} - -/* RT-Thread Device Interface */ - -/* - * This function initializes serial - */ -static rt_err_t rt_serial_init(struct rt_device *dev) -{ - rt_err_t result = RT_EOK; - struct rt_serial_device *serial; - - RT_ASSERT(dev != RT_NULL); - serial = (struct rt_serial_device *)dev; - - if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED)) - { - /* apply configuration */ - if (serial->ops->configure) - result = serial->ops->configure(serial, &serial->config); - - if (result != RT_EOK) - return result; - - if (dev->flag & RT_DEVICE_FLAG_INT_RX) - serial_ringbuffer_init(serial->int_rx); - - if (dev->flag & RT_DEVICE_FLAG_INT_TX) - serial_ringbuffer_init(serial->int_tx); - - /* set activated */ - dev->flag |= RT_DEVICE_FLAG_ACTIVATED; - } - - return result; -} - -static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) -{ - struct rt_serial_device *serial; - rt_uint32_t int_flags = 0; - - RT_ASSERT(dev != RT_NULL); - serial = (struct rt_serial_device *)dev; - - if (dev->flag & RT_DEVICE_FLAG_INT_RX) - int_flags = RT_SERIAL_RX_INT; - if (dev->flag & RT_DEVICE_FLAG_INT_TX) - int_flags |= RT_SERIAL_TX_INT; - - if (int_flags) - { - serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)int_flags); - } - - return RT_EOK; -} - -static rt_err_t rt_serial_close(struct rt_device *dev) -{ - struct rt_serial_device *serial; - rt_uint32_t int_flags = 0; - - RT_ASSERT(dev != RT_NULL); - serial = (struct rt_serial_device *)dev; - - if (dev->flag & RT_DEVICE_FLAG_INT_RX) - int_flags = RT_SERIAL_RX_INT; - if (dev->flag & RT_DEVICE_FLAG_INT_TX) - int_flags |= RT_SERIAL_TX_INT; - - if (int_flags) - { - serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)int_flags); - } - - return RT_EOK; -} - -static rt_size_t rt_serial_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size) -{ - rt_uint8_t *ptr; - rt_uint32_t read_nbytes; - struct rt_serial_device *serial; - - RT_ASSERT(dev != RT_NULL); - serial = (struct rt_serial_device *)dev; - - ptr = (rt_uint8_t *)buffer; - - if (dev->flag & RT_DEVICE_FLAG_INT_RX) - { - /* interrupt mode Rx */ - while (size) - { - int ch; - - ch = serial_ringbuffer_getc(serial->int_rx); - if (ch == -1) - break; - - *ptr = ch & 0xff; - ptr ++; - size --; - } - } - else - { - /* polling mode */ - while ((rt_uint32_t)ptr - (rt_uint32_t)buffer < size) - { - *ptr = serial->ops->getc(serial); - ptr ++; - } - } - - read_nbytes = (rt_uint32_t)ptr - (rt_uint32_t)buffer; - /* set error code */ - if (read_nbytes == 0) - { - rt_set_errno(-RT_EEMPTY); - } - - return read_nbytes; -} - -static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, - const void *buffer, rt_size_t size) -{ - rt_uint8_t *ptr; - rt_size_t write_nbytes = 0; - struct rt_serial_device *serial; - - RT_ASSERT(dev != RT_NULL); - serial = (struct rt_serial_device *)dev; - - ptr = (rt_uint8_t*)buffer; - - if (dev->flag & RT_DEVICE_FLAG_INT_TX) - { - /* warning: data will be discarded if buffer is full */ - while (size) - { - if (serial_ringbuffer_putchar(serial->int_tx, *ptr) != -1) - { - ptr ++; - size --; - } - else - break; - } - } - else - { - /* polling mode */ - while (size) - { - /* - * to be polite with serial console add a line feed - * to the carriage return character - */ - if (*ptr == '\n' && (dev->flag & RT_DEVICE_FLAG_STREAM)) - { - serial->ops->putc(serial, '\r'); - } - - serial->ops->putc(serial, *ptr); - - ++ptr; - --size; - } - } - - write_nbytes = (rt_uint32_t)ptr - (rt_uint32_t)buffer; - if (write_nbytes == 0) - { - rt_set_errno(-RT_EFULL); - } - - return write_nbytes; -} - -static rt_err_t rt_serial_control(struct rt_device *dev, rt_uint8_t cmd, void *args) -{ - struct rt_serial_device *serial; - - RT_ASSERT(dev != RT_NULL); - serial = (struct rt_serial_device *)dev; - - switch (cmd) - { - case RT_DEVICE_CTRL_SUSPEND: - /* suspend device */ - dev->flag |= RT_DEVICE_FLAG_SUSPENDED; - break; - - case RT_DEVICE_CTRL_RESUME: - /* resume device */ - dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED; - break; - - case RT_DEVICE_CTRL_CONFIG: - /* configure device */ - serial->ops->configure(serial, (struct serial_configure *)args); - break; - } - - return RT_EOK; -} - -/* - * serial register - */ -rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, const char *name, rt_uint32_t flag, void *data) -{ - struct rt_device *device; - RT_ASSERT(serial != RT_NULL); - - device = &(serial->parent); - - device->type = RT_Device_Class_Char; - device->rx_indicate = RT_NULL; - device->tx_complete = RT_NULL; - - device->init = rt_serial_init; - device->open = rt_serial_open; - device->close = rt_serial_close; - device->read = rt_serial_read; - device->write = rt_serial_write; - device->control = rt_serial_control; - device->user_data = data; - - /* register a character device */ - return rt_device_register(device, name, flag); -} - -/* ISR for serial interrupt */ -void rt_hw_serial_isr(struct rt_serial_device *serial) -{ - int ch = -1; - - /* interrupt mode receive */ - RT_ASSERT(serial->parent.flag & RT_DEVICE_FLAG_INT_RX); - - while (1) - { - ch = serial->ops->getc(serial); - if (ch == -1) - break; - - serial_ringbuffer_putc(serial->int_rx, ch); - } - - /* invoke callback */ - if (serial->parent.rx_indicate != RT_NULL) - { - rt_size_t rx_length; - - /* get rx length */ - rx_length = serial_ringbuffer_size(serial->int_rx); - serial->parent.rx_indicate(&serial->parent, rx_length); - } -} +/* + * File : serial.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-13 bernard first version + * 2012-05-15 lgnq modified according bernard's implementation. + * 2012-05-28 bernard code cleanup + */ + +#include +#include +#include + +rt_inline void serial_ringbuffer_init(struct serial_ringbuffer *rbuffer) +{ + rt_memset(rbuffer->buffer, 0, sizeof(rbuffer->buffer)); + rbuffer->put_index = 0; + rbuffer->get_index = 0; +} + +rt_inline void serial_ringbuffer_putc(struct serial_ringbuffer *rbuffer, + char ch) +{ + rt_base_t level; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + rbuffer->buffer[rbuffer->put_index] = ch; + rbuffer->put_index = (rbuffer->put_index + 1) & (SERIAL_RBUFFER_SIZE - 1); + + /* if the next position is read index, discard this 'read char' */ + if (rbuffer->put_index == rbuffer->get_index) + { + rbuffer->get_index = (rbuffer->get_index + 1) & (SERIAL_RBUFFER_SIZE - 1); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} + +rt_inline int serial_ringbuffer_putchar(struct serial_ringbuffer *rbuffer, + char ch) +{ + rt_base_t level; + rt_uint16_t next_index; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + next_index = (rbuffer->put_index + 1) & (SERIAL_RBUFFER_SIZE - 1); + if (next_index != rbuffer->get_index) + { + rbuffer->buffer[rbuffer->put_index] = ch; + rbuffer->put_index = next_index; + } + else + { + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return -1; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return 1; +} + +rt_inline int serial_ringbuffer_getc(struct serial_ringbuffer *rbuffer) +{ + int ch; + rt_base_t level; + + ch = -1; + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + if (rbuffer->get_index != rbuffer->put_index) + { + ch = rbuffer->buffer[rbuffer->get_index]; + rbuffer->get_index = (rbuffer->get_index + 1) & (SERIAL_RBUFFER_SIZE - 1); + } + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return ch; +} + +rt_inline rt_uint32_t serial_ringbuffer_size(struct serial_ringbuffer *rbuffer) +{ + rt_uint32_t size; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + size = (rbuffer->put_index - rbuffer->get_index) & (SERIAL_RBUFFER_SIZE - 1); + rt_hw_interrupt_enable(level); + + return size; +} + +/* RT-Thread Device Interface */ + +/* + * This function initializes serial + */ +static rt_err_t rt_serial_init(struct rt_device *dev) +{ + rt_err_t result = RT_EOK; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED)) + { + /* apply configuration */ + if (serial->ops->configure) + result = serial->ops->configure(serial, &serial->config); + + if (result != RT_EOK) + return result; + + if (dev->flag & RT_DEVICE_FLAG_INT_RX) + serial_ringbuffer_init(serial->int_rx); + + if (dev->flag & RT_DEVICE_FLAG_INT_TX) + serial_ringbuffer_init(serial->int_tx); + + /* set activated */ + dev->flag |= RT_DEVICE_FLAG_ACTIVATED; + } + + return result; +} + +static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) +{ + struct rt_serial_device *serial; + rt_uint32_t int_flags = 0; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + if (dev->flag & RT_DEVICE_FLAG_INT_RX) + int_flags = RT_SERIAL_RX_INT; + if (dev->flag & RT_DEVICE_FLAG_INT_TX) + int_flags |= RT_SERIAL_TX_INT; + + if (int_flags) + { + serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)int_flags); + } + + return RT_EOK; +} + +static rt_err_t rt_serial_close(struct rt_device *dev) +{ + struct rt_serial_device *serial; + rt_uint32_t int_flags = 0; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + if (dev->flag & RT_DEVICE_FLAG_INT_RX) + int_flags = RT_SERIAL_RX_INT; + if (dev->flag & RT_DEVICE_FLAG_INT_TX) + int_flags |= RT_SERIAL_TX_INT; + + if (int_flags) + { + serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)int_flags); + } + + return RT_EOK; +} + +static rt_size_t rt_serial_read(struct rt_device *dev, + rt_off_t pos, + void *buffer, + rt_size_t size) +{ + rt_uint8_t *ptr; + rt_uint32_t read_nbytes; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + ptr = (rt_uint8_t *)buffer; + + if (dev->flag & RT_DEVICE_FLAG_INT_RX) + { + /* interrupt mode Rx */ + while (size) + { + int ch; + + ch = serial_ringbuffer_getc(serial->int_rx); + if (ch == -1) + break; + + *ptr = ch & 0xff; + ptr ++; + size --; + } + } + else + { + /* polling mode */ + while ((rt_uint32_t)ptr - (rt_uint32_t)buffer < size) + { + *ptr = serial->ops->getc(serial); + ptr ++; + } + } + + read_nbytes = (rt_uint32_t)ptr - (rt_uint32_t)buffer; + /* set error code */ + if (read_nbytes == 0) + { + rt_set_errno(-RT_EEMPTY); + } + + return read_nbytes; +} + +static rt_size_t rt_serial_write(struct rt_device *dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) +{ + rt_uint8_t *ptr; + rt_size_t write_nbytes = 0; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + ptr = (rt_uint8_t*)buffer; + + if (dev->flag & RT_DEVICE_FLAG_INT_TX) + { + /* warning: data will be discarded if buffer is full */ + while (size) + { + if (serial_ringbuffer_putchar(serial->int_tx, *ptr) != -1) + { + ptr ++; + size --; + } + else + break; + } + } + else + { + /* polling mode */ + while (size) + { + /* + * to be polite with serial console add a line feed + * to the carriage return character + */ + if (*ptr == '\n' && (dev->flag & RT_DEVICE_FLAG_STREAM)) + { + serial->ops->putc(serial, '\r'); + } + + serial->ops->putc(serial, *ptr); + + ++ ptr; + -- size; + } + } + + write_nbytes = (rt_uint32_t)ptr - (rt_uint32_t)buffer; + if (write_nbytes == 0) + { + rt_set_errno(-RT_EFULL); + } + + return write_nbytes; +} + +static rt_err_t rt_serial_control(struct rt_device *dev, + rt_uint8_t cmd, + void *args) +{ + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + switch (cmd) + { + case RT_DEVICE_CTRL_SUSPEND: + /* suspend device */ + dev->flag |= RT_DEVICE_FLAG_SUSPENDED; + break; + + case RT_DEVICE_CTRL_RESUME: + /* resume device */ + dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED; + break; + + case RT_DEVICE_CTRL_CONFIG: + /* configure device */ + serial->ops->configure(serial, (struct serial_configure *)args); + break; + } + + return RT_EOK; +} + +/* + * serial register + */ +rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, + const char *name, + rt_uint32_t flag, + void *data) +{ + struct rt_device *device; + RT_ASSERT(serial != RT_NULL); + + device = &(serial->parent); + + device->type = RT_Device_Class_Char; + device->rx_indicate = RT_NULL; + device->tx_complete = RT_NULL; + + device->init = rt_serial_init; + device->open = rt_serial_open; + device->close = rt_serial_close; + device->read = rt_serial_read; + device->write = rt_serial_write; + device->control = rt_serial_control; + device->user_data = data; + + /* register a character device */ + return rt_device_register(device, name, flag); +} + +/* ISR for serial interrupt */ +void rt_hw_serial_isr(struct rt_serial_device *serial) +{ + int ch = -1; + + /* interrupt mode receive */ + RT_ASSERT(serial->parent.flag & RT_DEVICE_FLAG_INT_RX); + + while (1) + { + ch = serial->ops->getc(serial); + if (ch == -1) + break; + + serial_ringbuffer_putc(serial->int_rx, ch); + } + + /* invoke callback */ + if (serial->parent.rx_indicate != RT_NULL) + { + rt_size_t rx_length; + + /* get rx length */ + rx_length = serial_ringbuffer_size(serial->int_rx); + serial->parent.rx_indicate(&serial->parent, rx_length); + } +}