diff --git a/components/drivers/audio/audio.c b/components/drivers/audio/audio.c index dbd661217ad1054ad261c2217bd15e463cc4376c..66d9557d4645371858249acd22efd1d5addafc95 100644 --- a/components/drivers/audio/audio.c +++ b/components/drivers/audio/audio.c @@ -29,6 +29,9 @@ #include +#include "audio_pipe.h" + + #define AUDIO_DEBUG 0 #if AUDIO_DEBUG #define AUDIO_DBG(...) printf("[AUDIO]:"),printf(__VA_ARGS__) @@ -36,6 +39,8 @@ #define AUDIO_DBG(...) #endif +static struct rt_audio_pipe audio_pipe; + static rt_err_t _audio_send_replay_frame(struct rt_audio_device *audio) { rt_err_t result = RT_EOK; @@ -178,8 +183,8 @@ static rt_err_t _audio_dev_open(struct rt_device *dev, rt_uint16_t oflag) return -RT_ENOMEM; } - - rt_pipe_init(&record->pipe, "recpipe", RT_PIPE_FLAG_FORCE_WR | RT_PIPE_FLAG_BLOCK_RD, buf, + + rt_audio_pipe_init(&audio_pipe, "recpipe", RT_PIPE_FLAG_FORCE_WR | RT_PIPE_FLAG_BLOCK_RD, buf, CFG_AUDIO_RECORD_PIPE_SIZE); } @@ -190,7 +195,7 @@ static rt_err_t _audio_dev_open(struct rt_device *dev, rt_uint16_t oflag) //open record pipe if (audio->record != RT_NULL) { - rt_device_open(RT_DEVICE(&audio->record->pipe), RT_DEVICE_OFLAG_RDONLY); + rt_device_open(RT_DEVICE(&audio_pipe), RT_DEVICE_OFLAG_RDONLY); } dev->open_flag |= RT_DEVICE_OFLAG_RDONLY; @@ -236,7 +241,7 @@ static rt_err_t _audio_dev_close(struct rt_device *dev) //close record pipe if (audio->record != RT_NULL) - rt_device_close(RT_DEVICE(&audio->record->pipe)); + rt_device_close(RT_DEVICE(&audio_pipe)); dev->open_flag &= ~RT_DEVICE_OFLAG_RDONLY; } @@ -252,7 +257,7 @@ static rt_size_t _audio_dev_read(struct rt_device *dev, rt_off_t pos, void *buff if (!(dev->open_flag & RT_DEVICE_OFLAG_RDONLY) || (audio->record == RT_NULL)) return 0; - return rt_device_read(RT_DEVICE(&audio->record->pipe), pos, buffer, size); + return rt_device_read(RT_DEVICE(&audio_pipe), pos, buffer, size); } static rt_size_t _audio_dev_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) @@ -528,7 +533,7 @@ void rt_audio_rx_done(struct rt_audio_device *audio, rt_uint8_t *pbuf, rt_size_t rt_err_t result = RT_EOK; //save data to record pipe - rt_device_write(RT_DEVICE(RT_DEVICE(&audio->record->pipe)), 0, pbuf, len); + rt_device_write(RT_DEVICE(RT_DEVICE(&audio_pipe)), 0, pbuf, len); /* invoke callback */ if (audio->parent.rx_indicate != RT_NULL) diff --git a/components/drivers/audio/audio_pipe.c b/components/drivers/audio/audio_pipe.c new file mode 100644 index 0000000000000000000000000000000000000000..abef1557a4754c7dd26430cdd93cb695238033fe --- /dev/null +++ b/components/drivers/audio/audio_pipe.c @@ -0,0 +1,296 @@ +/* + * File : pipe.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2012, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2012-09-30 Bernard first version. + */ + +#include +#include +#include + +#include "audio_pipe.h" + +static void _rt_pipe_resume_writer(struct rt_audio_pipe *pipe) +{ + if (!rt_list_isempty(&pipe->suspended_write_list)) + { + rt_thread_t thread; + + RT_ASSERT(pipe->flag & RT_PIPE_FLAG_BLOCK_WR); + + /* get suspended thread */ + thread = rt_list_entry(pipe->suspended_write_list.next, + struct rt_thread, + tlist); + + /* resume the write thread */ + rt_thread_resume(thread); + + rt_schedule(); + } +} + +static rt_size_t rt_pipe_read(rt_device_t dev, + rt_off_t pos, + void *buffer, + rt_size_t size) +{ + rt_uint32_t level; + rt_thread_t thread; + struct rt_audio_pipe *pipe; + rt_size_t read_nbytes; + + pipe = (struct rt_audio_pipe *)dev; + RT_ASSERT(pipe != RT_NULL); + + if (!(pipe->flag & RT_PIPE_FLAG_BLOCK_RD)) + { + level = rt_hw_interrupt_disable(); + read_nbytes = rt_ringbuffer_get(&(pipe->ringbuffer), buffer, size); + + /* if the ringbuffer is empty, there won't be any writer waiting */ + if (read_nbytes) + _rt_pipe_resume_writer(pipe); + + rt_hw_interrupt_enable(level); + + return read_nbytes; + } + + thread = rt_thread_self(); + + /* current context checking */ + RT_DEBUG_NOT_IN_INTERRUPT; + + do { + level = rt_hw_interrupt_disable(); + read_nbytes = rt_ringbuffer_get(&(pipe->ringbuffer), buffer, size); + if (read_nbytes == 0) + { + rt_thread_suspend(thread); + /* waiting on suspended read list */ + rt_list_insert_before(&(pipe->suspended_read_list), + &(thread->tlist)); + rt_hw_interrupt_enable(level); + + rt_schedule(); + } + else + { + _rt_pipe_resume_writer(pipe); + rt_hw_interrupt_enable(level); + break; + } + } while (read_nbytes == 0); + + return read_nbytes; +} + +static void _rt_pipe_resume_reader(struct rt_audio_pipe *pipe) +{ + if (pipe->parent.rx_indicate) + pipe->parent.rx_indicate(&pipe->parent, + rt_ringbuffer_data_len(&pipe->ringbuffer)); + + if (!rt_list_isempty(&pipe->suspended_read_list)) + { + rt_thread_t thread; + + RT_ASSERT(pipe->flag & RT_PIPE_FLAG_BLOCK_RD); + + /* get suspended thread */ + thread = rt_list_entry(pipe->suspended_read_list.next, + struct rt_thread, + tlist); + + /* resume the read thread */ + rt_thread_resume(thread); + + rt_schedule(); + } +} + +static rt_size_t rt_pipe_write(rt_device_t dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) +{ + rt_uint32_t level; + rt_thread_t thread; + struct rt_audio_pipe *pipe; + rt_size_t write_nbytes; + + pipe = (struct rt_audio_pipe *)dev; + RT_ASSERT(pipe != RT_NULL); + + if ((pipe->flag & RT_PIPE_FLAG_FORCE_WR) || + !(pipe->flag & RT_PIPE_FLAG_BLOCK_WR)) + { + level = rt_hw_interrupt_disable(); + + if (pipe->flag & RT_PIPE_FLAG_FORCE_WR) + write_nbytes = rt_ringbuffer_put_force(&(pipe->ringbuffer), + buffer, size); + else + write_nbytes = rt_ringbuffer_put(&(pipe->ringbuffer), + buffer, size); + + _rt_pipe_resume_reader(pipe); + + rt_hw_interrupt_enable(level); + + return write_nbytes; + } + + thread = rt_thread_self(); + + /* current context checking */ + RT_DEBUG_NOT_IN_INTERRUPT; + + do { + level = rt_hw_interrupt_disable(); + write_nbytes = rt_ringbuffer_put(&(pipe->ringbuffer), buffer, size); + if (write_nbytes == 0) + { + /* pipe full, waiting on suspended write list */ + rt_thread_suspend(thread); + /* waiting on suspended read list */ + rt_list_insert_before(&(pipe->suspended_write_list), + &(thread->tlist)); + rt_hw_interrupt_enable(level); + + rt_schedule(); + } + else + { + _rt_pipe_resume_reader(pipe); + rt_hw_interrupt_enable(level); + break; + } + } while (write_nbytes == 0); + + return write_nbytes; +} + +static rt_err_t rt_pipe_control(rt_device_t dev, int cmd, void *args) +{ + struct rt_audio_pipe *pipe; + + pipe = (struct rt_audio_pipe *)dev; + + if (cmd == PIPE_CTRL_GET_SPACE && args) + *(rt_size_t*)args = rt_ringbuffer_space_len(&pipe->ringbuffer); + return RT_EOK; +} + +/** + * This function will initialize a pipe device and put it under control of + * resource management. + * + * @param pipe the pipe device + * @param name the name of pipe device + * @param flag the attribute of the pipe device + * @param buf the buffer of pipe device + * @param size the size of pipe device buffer + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_audio_pipe_init(struct rt_audio_pipe *pipe, + const char *name, + enum rt_audio_pipe_flag flag, + rt_uint8_t *buf, + rt_size_t size) +{ + RT_ASSERT(pipe); + RT_ASSERT(buf); + + /* initialize suspended list */ + rt_list_init(&pipe->suspended_read_list); + rt_list_init(&pipe->suspended_write_list); + + /* initialize ring buffer */ + rt_ringbuffer_init(&pipe->ringbuffer, buf, size); + + pipe->flag = flag; + + /* create pipe */ + pipe->parent.type = RT_Device_Class_Pipe; + pipe->parent.init = RT_NULL; + pipe->parent.open = RT_NULL; + pipe->parent.close = RT_NULL; + pipe->parent.read = rt_pipe_read; + pipe->parent.write = rt_pipe_write; + pipe->parent.control = rt_pipe_control; + + return rt_device_register(&(pipe->parent), name, RT_DEVICE_FLAG_RDWR); +} + +/** + * This function will detach a pipe device from resource management + * + * @param pipe the pipe device + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_audio_pipe_detach(struct rt_audio_pipe *pipe) +{ + return rt_device_unregister(&pipe->parent); +} + +#ifdef RT_USING_HEAP +rt_err_t rt_audio_pipe_create(const char *name, enum rt_audio_pipe_flag flag, rt_size_t size) +{ + rt_uint8_t *rb_memptr = RT_NULL; + struct rt_audio_pipe *pipe = RT_NULL; + + /* get aligned size */ + size = RT_ALIGN(size, RT_ALIGN_SIZE); + pipe = (struct rt_audio_pipe *)rt_calloc(1, sizeof(struct rt_audio_pipe)); + if (pipe == RT_NULL) + return -RT_ENOMEM; + + /* create ring buffer of pipe */ + rb_memptr = rt_malloc(size); + if (rb_memptr == RT_NULL) + { + rt_free(pipe); + return -RT_ENOMEM; + } + + return rt_audio_pipe_init(pipe, name, flag, rb_memptr, size); +} + +void rt_audio_pipe_destroy(struct rt_audio_pipe *pipe) +{ + if (pipe == RT_NULL) + return; + + /* un-register pipe device */ + rt_audio_pipe_detach(pipe); + + /* release memory */ + rt_free(pipe->ringbuffer.buffer_ptr); + rt_free(pipe); + + return; +} + +#endif /* RT_USING_HEAP */ diff --git a/components/drivers/audio/audio_pipe.h b/components/drivers/audio/audio_pipe.h new file mode 100644 index 0000000000000000000000000000000000000000..2c9d5d3e152f0798b9db7d94317090f5462acb94 --- /dev/null +++ b/components/drivers/audio/audio_pipe.h @@ -0,0 +1,65 @@ +#ifndef __AUDIO_PIPE_H__ +#define __AUDIO_PIPE_H__ + +/** + * Pipe Device + */ +#include +#include + +#ifndef RT_PIPE_BUFSZ +#define PIPE_BUFSZ 512 +#else +#define PIPE_BUFSZ RT_PIPE_BUFSZ +#endif + +/* portal device */ +struct rt_audio_portal_device +{ + struct rt_device parent; + struct rt_device *write_dev; + struct rt_device *read_dev; +}; + +enum rt_audio_pipe_flag +{ + /* both read and write won't block */ + RT_PIPE_FLAG_NONBLOCK_RDWR = 0x00, + /* read would block */ + RT_PIPE_FLAG_BLOCK_RD = 0x01, + /* write would block */ + RT_PIPE_FLAG_BLOCK_WR = 0x02, + /* write to this pipe will discard some data when the pipe is full. + * When this flag is set, RT_PIPE_FLAG_BLOCK_WR will be ignored since write + * operation will always be success. */ + RT_PIPE_FLAG_FORCE_WR = 0x04, +}; + +struct rt_audio_pipe +{ + struct rt_device parent; + + /* ring buffer in pipe device */ + struct rt_ringbuffer ringbuffer; + + enum rt_audio_pipe_flag flag; + + /* suspended list */ + rt_list_t suspended_read_list; + rt_list_t suspended_write_list; + + struct rt_audio_portal_device *write_portal; + struct rt_audio_portal_device *read_portal; +}; + +#define PIPE_CTRL_GET_SPACE 0x14 /**< get the remaining size of a pipe device */ + +rt_err_t rt_audio_pipe_init(struct rt_audio_pipe *pipe, + const char *name, + enum rt_audio_pipe_flag flag, + rt_uint8_t *buf, + rt_size_t size); + + +#endif + diff --git a/components/drivers/include/drivers/audio.h b/components/drivers/include/drivers/audio.h index 5f974994e78d83aedaf44108b0c5092eed16b57c..04821c2bc828a5263937b94e0cc38360b8aab008 100644 --- a/components/drivers/include/drivers/audio.h +++ b/components/drivers/include/drivers/audio.h @@ -198,7 +198,6 @@ struct rt_audio_replay struct rt_audio_record { rt_bool_t activated; - struct rt_pipe_device pipe; }; struct rt_audio_device