Browse Source

添加 audio设备驱动

yygg 8 years ago
parent
commit
c046b78f07

+ 9 - 0
components/drivers/audio/SConscript

@@ -0,0 +1,9 @@
+from building import *
+
+cwd     = GetCurrentDir()
+src     = Glob('*.c')
+CPPPATH = [cwd]
+
+group = DefineGroup('cJSON', src, depend = ['RT_USING_AUDIO'], CPPPATH = CPPPATH)
+
+Return('group')

+ 843 - 0
components/drivers/audio/audio.c

@@ -0,0 +1,843 @@
+/*
+ * audio.c
+ *
+ *  Created on: 2016Äê10ÔÂ19ÈÕ
+ *      Author: Urey
+ */
+
+#include <stdio.h>
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <drivers/audio.h>
+
+#define AUDIO_DEBUG   0
+#if AUDIO_DEBUG
+#define AUDIO_DBG(...)     printf("[AUDIO]:"),printf(__VA_ARGS__)
+#else
+#define AUDIO_DBG(...)
+#endif
+
+
+rt_err_t _audio_queue_init(struct rt_audio_queue *queue, rt_uint16_t size, rt_uint16_t lwm)
+{
+    RT_ASSERT(queue != RT_NULL);
+
+    queue->count 	= 0;
+    queue->size  	= size;
+    queue->lwm 		= lwm;
+    queue->waiting_lwm 	= RT_FALSE;
+
+    queue->get_index 	= 0;
+    queue->put_index 	= 0;
+
+    rt_list_init(&(queue->suspended_push_list));
+    rt_list_init(&(queue->suspended_pop_list));
+
+    queue->queue = (struct rt_audio_frame *)rt_malloc(sizeof(struct rt_audio_frame) * size);
+    if (queue->queue == RT_NULL)
+    {
+        return -RT_ENOMEM;
+    }
+
+    return RT_EOK;
+}
+
+rt_err_t _audio_queue_push(struct rt_audio_queue *queue, struct rt_audio_frame *frame, rt_int32_t timeout)
+{
+    rt_ubase_t  level;
+    rt_thread_t thread;
+    rt_err_t    result;
+
+    RT_ASSERT(queue != RT_NULL);
+
+    result = RT_EOK;
+    thread = rt_thread_self();
+
+    AUDIO_DBG("%s count = %d\n",__func__,queue->count);
+
+    level = rt_hw_interrupt_disable();
+    while(queue->count == queue->size)
+    {// audio queue is full
+        queue->waiting_lwm = RT_TRUE;
+
+        /* queue is full */
+        if (timeout == 0)
+        {
+            result = -RT_ETIMEOUT;
+            goto __exit;
+        }
+
+        /* current context checking */
+        RT_DEBUG_NOT_IN_INTERRUPT;
+
+        /* reset thread error number */
+        thread->error = RT_EOK;
+
+        /* suspend thread on the push list */
+        rt_thread_suspend(thread);
+        rt_list_insert_before(&(queue->suspended_push_list), &(thread->tlist));
+        /* start timer */
+        if (timeout > 0)
+        {
+			/* reset the timeout of thread timer and start it */
+			rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout);
+			rt_timer_start(&(thread->thread_timer));
+        }
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(level);
+
+        /* do schedule */
+        rt_schedule();
+
+        /* thread is waked up */
+        result = thread->error;
+        level = rt_hw_interrupt_disable();
+        if (result != RT_EOK) goto __exit;
+    }
+
+    queue->queue[queue->put_index].data_ptr  = frame->data_ptr;
+    queue->queue[queue->put_index].data_size = frame->data_size;
+    queue->queue[queue->put_index].data_ofs	 = frame->data_ofs;
+    queue->put_index = (queue->put_index + 1) % queue->size;
+    queue->count ++;
+
+    if (!rt_list_isempty(&(queue->suspended_pop_list)))
+    {
+        /* there is at least one thread in suspended list */
+
+        /* get thread entry */
+        thread = rt_list_entry(queue->suspended_pop_list.next,
+                               struct rt_thread,
+                               tlist);
+
+        /* resume it */
+        rt_thread_resume(thread);
+        rt_hw_interrupt_enable(level);
+
+        /* perform a schedule */
+        rt_schedule();
+
+        return result;
+    }
+__exit:
+	rt_hw_interrupt_enable(level);
+
+	return result;
+}
+
+rt_err_t _audio_queue_pop(struct rt_audio_queue *queue, struct rt_audio_frame *frame, rt_int32_t timeout)
+{
+    rt_ubase_t  level;
+    rt_thread_t thread;
+    rt_err_t    result;
+
+    RT_ASSERT(queue != RT_NULL);
+    RT_ASSERT(frame != RT_NULL);
+
+    result = RT_EOK;
+    thread = rt_thread_self();
+
+    AUDIO_DBG("%s count = %d\n",__func__,queue->count);
+
+    level = rt_hw_interrupt_disable();
+    while (queue->count == 0)
+    {
+        /* queue is empty */
+        if (timeout == 0)
+        {
+            result = -RT_ETIMEOUT;
+            goto __exit;
+        }
+
+        /* current context checking */
+        RT_DEBUG_NOT_IN_INTERRUPT;
+
+        /* reset thread error number */
+        thread->error = RT_EOK;
+
+        /* suspend thread on the pop list */
+        rt_thread_suspend(thread);
+        rt_list_insert_before(&(queue->suspended_pop_list), &(thread->tlist));
+        /* start timer */
+        if (timeout > 0)
+        {
+            /* reset the timeout of thread timer and start it */
+			rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout);
+            rt_timer_start(&(thread->thread_timer));
+        }
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(level);
+
+        /* do schedule */
+        rt_schedule();
+
+        /* thread is waked up */
+        result = thread->error;
+        level  = rt_hw_interrupt_disable();
+        if (result != RT_EOK)
+            goto __exit;
+    }
+
+    frame->data_ptr  = queue->queue[queue->get_index].data_ptr;
+    frame->data_size = queue->queue[queue->get_index].data_size;
+    frame->data_ofs  = queue->queue[queue->get_index].data_ofs;
+
+    queue->get_index = (queue->get_index + 1) % queue->size;
+    queue->count --;
+
+    if ((queue->waiting_lwm == RT_TRUE) &&
+        (queue->put_index - queue->get_index) <= queue->lwm)
+    {
+        queue->waiting_lwm = RT_FALSE;
+
+        /*
+         * there is at least one thread in suspended list
+         * and less than low water mark
+         */
+        if (!rt_list_isempty(&(queue->suspended_push_list)))
+        {
+            /* get thread entry */
+            thread = rt_list_entry(queue->suspended_push_list.next,
+                                   struct rt_thread,
+                                   tlist);
+
+            /* resume it */
+            rt_thread_resume(thread);
+            rt_hw_interrupt_enable(level);
+
+            /* perform a schedule */
+            rt_schedule();
+        }
+
+        return result;
+    }
+__exit:
+	rt_hw_interrupt_enable(level);
+	return result;
+}
+
+rt_err_t _audio_queue_peak(struct rt_audio_queue *queue, struct rt_audio_frame *frame)
+{
+    rt_ubase_t  level;
+
+    RT_ASSERT(queue != RT_NULL);
+
+    AUDIO_DBG("%s count = %d\n",__func__,queue->count);
+
+    level = rt_hw_interrupt_disable();
+    if (queue->count == 0)
+    {
+        rt_hw_interrupt_enable(level);
+
+        return -RT_EEMPTY;
+    }
+
+    frame->data_ptr  = queue->queue[queue->get_index].data_ptr;
+    frame->data_size = queue->queue[queue->get_index].data_size;
+    frame->data_ofs  = queue->queue[queue->get_index].data_ofs;
+
+    rt_hw_interrupt_enable(level);
+
+    return RT_EOK;
+}
+
+rt_err_t _audio_queue_unpeak(struct rt_audio_queue *queue, struct rt_audio_frame *frame)
+{
+    rt_ubase_t  level;
+
+    RT_ASSERT(queue != RT_NULL);
+
+    level = rt_hw_interrupt_disable();
+
+    if (queue->count == 0)
+    {
+        rt_hw_interrupt_enable(level);
+
+        return -RT_EEMPTY;
+    }
+
+	queue->queue[queue->get_index].data_ptr  = frame->data_ptr;
+	queue->queue[queue->get_index].data_size = frame->data_size;
+	queue->queue[queue->get_index].data_ofs  = frame->data_ofs;
+
+    rt_hw_interrupt_enable(level);
+
+    return RT_EOK;
+}
+
+rt_err_t _audio_queue_reset(struct rt_audio_queue *queue)
+{
+    struct rt_thread *thread;
+    register rt_ubase_t temp;
+
+    rt_enter_critical();
+
+    /* wakeup all suspend threads */
+    /* resume on pop list */
+     while (!rt_list_isempty(&(queue->suspended_pop_list)))
+     {
+         /* disable interrupt */
+         temp = rt_hw_interrupt_disable();
+
+         /* get next suspend thread */
+         thread = rt_list_entry(queue->suspended_pop_list.next,
+                                struct rt_thread,
+                                tlist);
+         /* set error code to RT_ERROR */
+         thread->error = -RT_ERROR;
+
+         /*
+          * resume thread
+          * In rt_thread_resume function, it will remove current thread from
+          * suspend list
+          */
+         rt_thread_resume(thread);
+
+         /* enable interrupt */
+         rt_hw_interrupt_enable(temp);
+     }
+
+     /* resume on push list */
+     while (!rt_list_isempty(&(queue->suspended_push_list)))
+     {
+         /* disable interrupt */
+         temp = rt_hw_interrupt_disable();
+
+         /* get next suspend thread */
+         thread = rt_list_entry(queue->suspended_push_list.next,
+                                struct rt_thread,
+                                tlist);
+         /* set error code to RT_ERROR */
+         thread->error = -RT_ERROR;
+
+         /*
+          * resume thread
+          * In rt_thread_resume function, it will remove current thread from
+          * suspend list
+          */
+         rt_thread_resume(thread);
+
+         /* enable interrupt */
+         rt_hw_interrupt_enable(temp);
+     }
+     rt_exit_critical();
+
+     rt_schedule();
+}
+
+
+static rt_err_t _audio_send_replay_frame(struct rt_audio_device *audio)
+{
+    rt_err_t    result = RT_EOK;
+    rt_base_t	level;
+    struct rt_audio_frame	 frame;
+
+    RT_ASSERT(audio != RT_NULL);
+
+	//check repaly queue is empty
+	if(_audio_queue_peak(&audio->replay->queue,&frame) != RT_EOK)
+	{
+		AUDIO_DBG("TX queue is empty\n");
+		result = -RT_EEMPTY;
+
+		level = rt_hw_interrupt_disable();
+		audio->replay->activated = RT_FALSE;
+		rt_hw_interrupt_enable(level);
+
+		goto _exit;
+	}
+
+	if(audio->ops->transmit != RT_NULL)
+	{
+		AUDIO_DBG("audio transmit...\n");
+		if(audio->ops->transmit(audio,frame.data_ptr,RT_NULL, frame.data_size) != frame.data_size)
+		{
+			result = -RT_EBUSY;
+
+			goto _exit;
+		}
+	}
+
+	//pop the head frame...
+	_audio_queue_pop(&audio->replay->queue,&frame,RT_WAITING_NO);
+
+_exit:
+	return result;
+}
+
+static rt_err_t _audio_flush_replay_frame(struct rt_audio_device *audio)
+{
+    struct rt_audio_frame	 frame;
+
+	if(audio->replay == RT_NULL)
+		return -RT_EIO;
+
+	while(_audio_queue_peak(&audio->replay->queue,&frame) == RT_EOK)
+	{
+		//pop the head frame...
+		_audio_queue_pop(&audio->replay->queue,&frame,RT_WAITING_NO);
+
+		/* notify transmitted complete. */
+		if(audio->parent.tx_complete != RT_NULL)
+			audio->parent.tx_complete(&audio->parent,(void *)frame.data_ptr);
+	}
+
+	return RT_EOK;
+}
+
+static rt_err_t _audio_dev_init(struct rt_device *dev)
+{
+    rt_err_t result = RT_EOK;
+    struct rt_audio_device *audio;
+
+    RT_ASSERT(dev != RT_NULL);
+    audio = (struct rt_audio_device *)dev;
+
+    /* initialize replay & record */
+    audio->replay = RT_NULL;
+    audio->record = RT_NULL;
+
+    /* apply configuration */
+    if (audio->ops->init)
+        result = audio->ops->init(audio);
+
+    return result;
+}
+
+static rt_err_t _audio_dev_open(struct rt_device *dev, rt_uint16_t oflag)
+{
+    rt_err_t    result = RT_EOK;
+    rt_base_t	level;
+    struct rt_audio_device     *audio;
+
+    RT_ASSERT(dev != RT_NULL);
+    audio = (struct rt_audio_device *)dev;
+
+    /* check device flag with the open flag */
+    if ((oflag & RT_DEVICE_OFLAG_RDONLY) && !(dev->flag & RT_DEVICE_FLAG_RDONLY))
+        return -RT_EIO;
+    if ((oflag & RT_DEVICE_OFLAG_WRONLY) && !(dev->flag & RT_DEVICE_FLAG_WRONLY))
+        return -RT_EIO;
+
+    /* get open flags */
+    dev->open_flag = oflag & 0xff;
+
+    /* initialize the Rx/Tx structure according to open flag */
+    if (oflag & RT_DEVICE_OFLAG_WRONLY)
+    {
+    	AUDIO_DBG("open audio device ,oflag = %x\n",oflag);
+    	if(audio->replay == RT_NULL)
+    	{
+    		struct rt_audio_replay	*replay = (struct rt_audio_replay	*)rt_malloc(sizeof(struct rt_audio_replay));
+
+    		if(replay == RT_NULL)
+    		{
+    			AUDIO_DBG("request memory for replay error\n");
+    			return -RT_ENOMEM;
+    		}
+
+    		//init queue for audio replay
+    		_audio_queue_init(&replay->queue,CFG_AUDIO_REPLAY_QUEUE_COUNT,CFG_AUDIO_REPLAY_QUEUE_COUNT / 2);
+
+    		replay->activated = RT_FALSE;
+    		audio->replay = replay;
+    	}
+
+        dev->open_flag |= RT_DEVICE_OFLAG_WRONLY;
+    }
+
+    if(oflag & RT_DEVICE_OFLAG_RDONLY)
+    {
+    	if(audio->record == RT_NULL)
+    	{
+    		struct rt_audio_record	*record = (struct rt_audio_record *)rt_malloc(sizeof(struct rt_audio_record));
+
+    		if(record == RT_NULL)
+    		{
+    			AUDIO_DBG("request memory for record error\n");
+    			return -RT_ENOMEM;
+    		}
+
+    		//init pipe for record
+    		{
+    			rt_size_t size  = CFG_AUDIO_RECORD_PIPE_SIZE;
+    			rt_uint8_t *buf = rt_malloc(CFG_AUDIO_RECORD_PIPE_SIZE);
+
+    			if(buf == RT_NULL)
+    			{
+    				rt_free(record);
+        			AUDIO_DBG("request pipe memory error\n");
+
+    				return -RT_ENOMEM;
+    			}
+
+    			rt_pipe_init(&record->pipe,"recpipe",RT_PIPE_FLAG_FORCE_WR | RT_PIPE_FLAG_BLOCK_RD,buf,CFG_AUDIO_RECORD_PIPE_SIZE);
+    		}
+
+    		record->activated   = RT_FALSE;
+    		audio->record       = record;
+    	}
+
+    	//open record pipe
+    	if(audio->record != RT_NULL)
+    	{
+    		rt_device_open(RT_DEVICE(&audio->record->pipe),RT_DEVICE_OFLAG_RDONLY);
+    	}
+
+        dev->open_flag |= RT_DEVICE_OFLAG_RDONLY;
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t _audio_dev_close(struct rt_device *dev)
+{
+    struct rt_audio_device     *audio;
+    RT_ASSERT(dev != RT_NULL);
+    audio = (struct rt_audio_device *)dev;
+
+	//shutdown the lower device
+    if(audio->ops->shutdown != RT_NULL)
+    	audio->ops->shutdown(audio);
+
+    if(dev->open_flag & RT_DEVICE_OFLAG_WRONLY)
+    {
+    	struct rt_audio_frame frame;
+    	//stop replay stream
+    	audio->ops->stop(audio,AUDIO_STREAM_REPLAY);
+
+    	//flush all frame
+    	while(_audio_queue_peak(&audio->replay->queue,&frame) == RT_EOK)
+    	{
+    		_audio_queue_pop(&audio->replay->queue,&frame,RT_WAITING_NO);
+
+    		//indicate this frame complete(maybe upper device need free data)
+    		if(dev->tx_complete != RT_NULL)
+    			dev->tx_complete(dev,(void *)frame.data_ptr);
+    	}
+
+    	dev->open_flag &= ~RT_DEVICE_OFLAG_WRONLY;
+    }
+
+    if(dev->open_flag & RT_DEVICE_OFLAG_RDONLY)
+    {
+    	//stop record stream
+    	audio->ops->stop(audio,AUDIO_STREAM_RECORD);
+
+    	//close record pipe
+    	if(audio->record != RT_NULL)
+    		rt_device_close(RT_DEVICE(&audio->record->pipe));
+
+    	dev->open_flag &= ~RT_DEVICE_OFLAG_RDONLY;
+    }
+
+	return RT_EOK;
+}
+
+static rt_size_t _audio_dev_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+    struct rt_audio_device     *audio;
+    RT_ASSERT(dev != RT_NULL);
+    audio = (struct rt_audio_device *)dev;
+    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);
+}
+
+static rt_size_t _audio_dev_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+    rt_err_t    result = RT_EOK;
+    rt_base_t	level;
+    struct rt_audio_device     *audio;
+
+    RT_ASSERT(dev != RT_NULL);
+    audio = (struct rt_audio_device *)dev;
+
+    if(!(dev->open_flag & RT_DEVICE_OFLAG_WRONLY) || (audio->replay == RT_NULL))
+    	return 0;
+
+    AUDIO_DBG("audio write : pos = %d,buffer = %x,size = %d\n",pos,(rt_uint32_t)buffer,size);
+    //push a new frame to tx queue
+    {
+        struct rt_audio_frame		frame;
+        frame.data_ptr  = buffer;
+        frame.data_size = size;
+        frame.data_ofs  = 0;
+
+        result = _audio_queue_push(&audio->replay->queue,&frame,RT_WAITING_FOREVER);
+        if(result != RT_EOK)
+        {
+    		AUDIO_DBG("TX frame queue push error\n");
+    		rt_set_errno(-RT_EFULL);
+            return 0;
+        }
+    }
+
+    //check tx state...
+    level = rt_hw_interrupt_disable();
+    if(audio->replay->activated != RT_TRUE)
+    {
+    	audio->replay->activated = RT_TRUE;
+    	rt_hw_interrupt_enable(level);
+
+    	_audio_send_replay_frame(audio);
+    }
+
+    return size;
+}
+
+static rt_err_t _audio_dev_control(struct rt_device *dev, rt_uint8_t cmd, void *args)
+{
+    rt_err_t    result = RT_EOK;
+    struct rt_audio_device    *audio;
+    RT_ASSERT(dev != RT_NULL);
+    audio = (struct rt_audio_device *)dev;
+
+    //dev stat...
+	switch (cmd)
+	{
+	case AUDIO_CTL_GETCAPS:
+	{
+		struct rt_audio_caps *caps = (struct rt_audio_caps *) args;
+
+		AUDIO_DBG("AUDIO_CTL_GETCAPS: main_type = %d,sub_type = %d\n",caps->main_type,caps->sub_type);
+		if (audio->ops->getcaps != RT_NULL)
+		{
+			result = audio->ops->getcaps(audio, caps);
+		}
+	}
+		break;
+	case AUDIO_CTL_CONFIGURE:
+	{
+		struct rt_audio_caps *caps = (struct rt_audio_caps *) args;
+
+		AUDIO_DBG("AUDIO_CTL_CONFIGURE: main_type = %d,sub_type = %d\n",caps->main_type,caps->sub_type);
+		if (audio->ops->configure != RT_NULL)
+		{
+			result = audio->ops->configure(audio, caps);
+		}
+	}
+
+		break;
+	case AUDIO_CTL_SHUTDOWN:
+	{
+		AUDIO_DBG("AUDIO_CTL_SHUTDOWN\n");
+
+		if (audio->ops->shutdown != RT_NULL)
+			result = audio->ops->shutdown(audio);
+
+		//flush replay frame...
+		_audio_flush_replay_frame(audio);
+	}
+		break;
+
+	case AUDIO_CTL_START:
+	{
+		int stream = *(int *) args;
+
+		AUDIO_DBG("AUDIO_CTL_START: stream = %d\n",stream);
+		if (audio->ops->start != RT_NULL)
+			result = audio->ops->start(audio, stream);
+	}
+		break;
+	case AUDIO_CTL_STOP:
+	{
+		int stream = *(int *) args;
+
+		AUDIO_DBG("AUDIO_CTL_STOP: stream = %d\n",stream);
+		if (audio->ops->start != RT_NULL)
+			result = audio->ops->stop(audio, stream);
+
+		if(stream == AUDIO_STREAM_REPLAY)
+		{
+			_audio_flush_replay_frame(audio);
+		}
+	}
+		break;
+	case AUDIO_CTL_PAUSE:
+	{
+		int stream = *(int *) args;
+
+		AUDIO_DBG("AUDIO_CTL_PAUSE: stream = %d\n",stream);
+		if (audio->ops->start != RT_NULL)
+			result = audio->ops->suspend(audio, stream);
+	}
+		break;
+	case AUDIO_CTL_RESUME:
+	{
+		int stream = *(int *) args;
+
+		AUDIO_DBG("AUDIO_CTL_RESUME: stream = %d\n",stream);
+		if (audio->ops->start != RT_NULL)
+			result = audio->ops->resume(audio, stream);
+
+		//resume tx frame...
+		if(stream == AUDIO_STREAM_REPLAY)
+			_audio_send_replay_frame(audio);
+	}
+		break;
+#ifdef AUDIO_DEVICE_USE_PRIVATE_BUFFER
+	case AUDIO_CTL_ALLOCBUFFER:
+	{
+		struct rt_audio_buf_desc *desc = (struct rt_audio_buf_desc *)args;
+
+		if((audio->ops->buffer_alloc != RT_NULL) && (desc != RT_NULL))
+		{
+			result = audio->ops->buffer_alloc(audio,&desc->data_ptr,&desc->data_size);
+			break;
+		}
+
+		result = -RT_EIO;
+	}
+		break;
+	case AUDIO_CTL_FREEBUFFER:
+	{
+		rt_uint8_t *data_ptr = (rt_uint8_t *)args;
+		if((audio->ops->buffer_free != RT_NULL) && (data_ptr != RT_NULL))
+		{
+			audio->ops->buffer_free(audio,data_ptr);
+			break;
+		}
+	}
+		break;
+#endif
+	default:
+		result = audio->ops->control(audio, cmd, args);
+		break;
+	}
+
+    return result;
+}
+
+
+rt_err_t rt_audio_register(struct rt_audio_device *audio, const char *name, rt_uint32_t flag, void *data)
+{
+    struct rt_device *device;
+    RT_ASSERT(audio != RT_NULL);
+    device = &(audio->parent);
+
+    device->type        = RT_Device_Class_Sound;
+    device->rx_indicate = RT_NULL;
+    device->tx_complete = RT_NULL;
+
+    device->init        = _audio_dev_init;
+    device->open        = _audio_dev_open;
+    device->close       = _audio_dev_close;
+    device->read        = _audio_dev_read;
+    device->write       = _audio_dev_write;
+    device->control     = _audio_dev_control;
+    device->user_data   = data;
+
+    /* register a character device */
+    return rt_device_register(device, name, flag | RT_DEVICE_FLAG_REMOVABLE);
+}
+
+
+
+rt_size_t rt_audio_get_buffer_size(struct rt_audio_device *audio)
+{
+//	return (audio->config.period_count * audio->config.period_size);
+	return 0;
+}
+
+int rt_audio_samplerate_to_speed(rt_uint32_t bitValue)
+{
+	int speed = 0;
+	switch (bitValue)
+	{
+	case AUDIO_SAMP_RATE_8K:
+		speed = 8000;
+		break;
+	case AUDIO_SAMP_RATE_11K:
+		speed = 11052;
+		break;
+	case AUDIO_SAMP_RATE_16K:
+		speed = 16000;
+		break;
+	case AUDIO_SAMP_RATE_22K:
+		speed = 22050;
+		break;
+	case AUDIO_SAMP_RATE_32K:
+		speed = 32000;
+		break;
+	case AUDIO_SAMP_RATE_44K:
+		speed = 44100;
+		break;
+	case AUDIO_SAMP_RATE_48K:
+		speed = 48000;
+		break;
+	case AUDIO_SAMP_RATE_96K:
+		speed = 96000;
+		break;
+	case AUDIO_SAMP_RATE_128K:
+		speed = 128000;
+		break;
+	case AUDIO_SAMP_RATE_160K:
+		speed = 160000;
+		break;
+	case AUDIO_SAMP_RATE_172K:
+		speed = 176400;
+		break;
+	case AUDIO_SAMP_RATE_192K:
+		speed = 192000;
+		break;
+	default:
+
+		break;
+	}
+
+	return speed;
+}
+
+
+
+rt_uint32_t rt_audio_format_to_bits(rt_uint32_t format)
+{
+    switch (format)
+    {
+    case AUDIO_FMT_PCM_U8:
+    case AUDIO_FMT_PCM_S8:
+        return 8;
+    case AUDIO_FMT_PCM_S16_LE:
+    case AUDIO_FMT_PCM_S16_BE:
+    case AUDIO_FMT_PCM_U16_LE:
+    case AUDIO_FMT_PCM_U16_BE:
+        return 16;
+    default:
+        return 32;
+    };
+}
+
+void rt_audio_tx_complete(struct rt_audio_device *audio,rt_uint8_t *pbuf)
+{
+	rt_err_t result;
+	AUDIO_DBG("audio tx complete ptr=%x...\n",(rt_uint32_t)pbuf);
+
+	//try to send all frame
+	do
+	{
+		result = _audio_send_replay_frame(audio);
+	}while(result == RT_EOK);
+
+	/* notify transmitted complete. */
+	if(audio->parent.tx_complete != RT_NULL)
+		audio->parent.tx_complete(&audio->parent,(void *)pbuf);
+}
+
+void rt_audio_rx_done(struct rt_audio_device *audio,rt_uint8_t *pbuf,rt_size_t len)
+{
+    rt_err_t    result = RT_EOK;
+
+    //save data to record pipe
+    rt_device_write(RT_DEVICE(RT_DEVICE(&audio->record->pipe)),0,pbuf,len);
+
+    /* invoke callback */
+    if(audio->parent.rx_indicate != RT_NULL)
+    	audio->parent.rx_indicate(&audio->parent,len);
+}
+

+ 214 - 3
components/drivers/include/drivers/audio.h

@@ -1,5 +1,216 @@
-#ifndef AUDIO_H__
-#define AUDIO_H__
+#ifndef __AUDIO_H__
+#define __AUDIO_H__
+
+//#define AUDIO_DEVICE_USE_PRIVATE_BUFFER
+
+
+/* AUDIO command */
+#define _AUDIO_CTL(a)	(0x10 + a)
+
+#define AUDIO_CTL_GETCAPS            _AUDIO_CTL(1)
+#define AUDIO_CTL_CONFIGURE          _AUDIO_CTL(2)
+#define AUDIO_CTL_SHUTDOWN           _AUDIO_CTL(3)
+#define AUDIO_CTL_START              _AUDIO_CTL(4)
+#define AUDIO_CTL_STOP               _AUDIO_CTL(5)
+#define AUDIO_CTL_PAUSE              _AUDIO_CTL(6)
+#define AUDIO_CTL_RESUME             _AUDIO_CTL(7)
+#define AUDIO_CTL_GETBUFFERINFO      _AUDIO_CTL(8)
+#define AUDIO_CTL_ALLOCBUFFER        _AUDIO_CTL(9)
+#define AUDIO_CTL_FREEBUFFER         _AUDIO_CTL(10)
+#define AUDIO_CTL_HWRESET            _AUDIO_CTL(11)
+
+
+/* Audio Device Types */
+#define AUDIO_TYPE_QUERY            0x00
+#define AUDIO_TYPE_INPUT            0x01
+#define AUDIO_TYPE_OUTPUT           0x02
+#define AUDIO_TYPE_MIXER            0x04
+#define AUDIO_TYPE_SELECTOR         0x08
+#define AUDIO_TYPE_EFFECT           0x10
+
+/* Audio Format Types */
+#define AUDIO_FMT_PCM_U8         	0x0001
+#define AUDIO_FMT_PCM_S8         	0x0002
+
+#define AUDIO_FMT_PCM_U16_LE     	0x0010
+#define AUDIO_FMT_PCM_S16_BE     	0x0020
+#define AUDIO_FMT_PCM_S16_LE     	0x0040
+#define AUDIO_FMT_PCM_U16_BE     	0x0080
+#define AUDIO_FMT_PCM_U24_LE     	0x0100
+#define AUDIO_FMT_PCM_S24_BE     	0x0200
+#define AUDIO_FMT_PCM_S24_LE     	0x0400
+#define AUDIO_FMT_PCM_U24_BE     	0x0800
+#define AUDIO_FMT_PCM_U32_LE     	0x1000
+#define AUDIO_FMT_PCM_S32_BE     	0x2000
+#define AUDIO_FMT_PCM_S32_LE     	0x4000
+#define AUDIO_FMT_PCM_U32_BE     	0x8000
+
+/* Supported Sampling Rates */
+#define AUDIO_SAMP_RATE_8K          0x0001
+#define AUDIO_SAMP_RATE_11K         0x0002
+#define AUDIO_SAMP_RATE_16K         0x0004
+#define AUDIO_SAMP_RATE_22K         0x0008
+#define AUDIO_SAMP_RATE_32K         0x0010
+#define AUDIO_SAMP_RATE_44K         0x0020
+#define AUDIO_SAMP_RATE_48K         0x0040
+#define AUDIO_SAMP_RATE_96K         0x0080
+#define AUDIO_SAMP_RATE_128K        0x0100
+#define AUDIO_SAMP_RATE_160K        0x0200
+#define AUDIO_SAMP_RATE_172K        0x0400
+#define AUDIO_SAMP_RATE_192K        0x0800
+
+/* Supported Bit Rates */
+#define AUDIO_BIT_RATE_22K          0x01
+#define AUDIO_BIT_RATE_44K          0x02
+#define AUDIO_BIT_RATE_48K          0x04
+#define AUDIO_BIT_RATE_96K          0x08
+#define AUDIO_BIT_RATE_128K         0x10
+#define AUDIO_BIT_RATE_160K         0x20
+#define AUDIO_BIT_RATE_172K         0x40
+#define AUDIO_BIT_RATE_192K         0x80
+
+
+
+/* Support Dsp(input/output) Units controls */
+#define AUDIO_DSP_PARAM				0						/* get/set all params */
+#define AUDIO_DSP_SAMPLERATE           1                       /*  ²ÉÑùƵÂÊ                    */
+#define AUDIO_DSP_FMT        			2
+#define AUDIO_DSP_CHANNELS          	3
+
+/* Supported Mixer Units controls */
+#define AUDIO_MIXER_QUERY              0x0000
+#define AUDIO_MIXER_MUTE               0x0001
+#define AUDIO_MIXER_VOLUME             0x0002
+#define AUDIO_MIXER_BASS               0x0004
+#define AUDIO_MIXER_MID                0x0008
+#define AUDIO_MIXER_TREBLE             0x0010
+#define AUDIO_MIXER_EQUALIZER          0x0020
+#define AUDIO_MIXER_LINE           	0x0040
+#define AUDIO_MIXER_DIGITAL			0x0080
+#define AUDIO_MIXER_MIC				0x0100
+
+#define AUDIO_MIXER_EXTEND				0x8000		//extend mixer command
+
+#define CFG_AUDIO_REPLAY_QUEUE_COUNT		4
+#define CFG_AUDIO_RECORD_PIPE_SIZE			(8 * 1024)
+
+enum
+{
+    AUDIO_STREAM_REPLAY = 0,
+    AUDIO_STREAM_RECORD,
+    AUDIO_STREAM_LAST = AUDIO_STREAM_RECORD,
+};
+
+/* the preferred number and size of audio pipeline buffer for the audio device */
+struct rt_audio_buf_info
+{
+	rt_uint32_t buffer_size; 					/* Preferred qty of buffers */
+	rt_uint32_t buffer_count; 					/* Preferred size of the buffers */
+};
+struct rt_audio_buf_desc
+{
+	rt_uint8_t *data_ptr;
+	rt_size_t	data_size;
+};
+
+struct rt_audio_frame
+{
+    const void *data_ptr;
+    rt_size_t 	data_size;
+    rt_size_t	data_ofs;
+};
+
+struct rt_audio_queue
+{
+	rt_uint16_t count;
+    rt_uint16_t size;
+    rt_uint16_t lwm;
+    rt_bool_t   waiting_lwm;
+
+    rt_uint16_t get_index;
+    rt_uint16_t put_index;
+
+    struct rt_audio_frame *queue;
+
+    rt_list_t suspended_push_list;
+    rt_list_t suspended_pop_list;
+};
+
+struct rt_audio_device;
+struct rt_audio_caps;
+struct rt_audio_configure;
+struct rt_audio_ops
+{
+	rt_err_t	(*getcaps)			(struct rt_audio_device *audio,struct rt_audio_caps *caps);
+	rt_err_t 	(*configure)		(struct rt_audio_device *audio,struct rt_audio_caps *caps);
+
+    rt_err_t    (*init)         	(struct rt_audio_device *audio);
+    rt_err_t    (*shutdown)    		(struct rt_audio_device *audio);
+    rt_err_t    (*start)    		(struct rt_audio_device *audio,int stream);
+    rt_err_t    (*stop)    			(struct rt_audio_device *audio,int stream);
+    rt_err_t    (*suspend)  		(struct rt_audio_device *audio,int stream);
+    rt_err_t    (*resume)       	(struct rt_audio_device *audio,int stream);
+
+    rt_err_t    (*control)      	(struct rt_audio_device *audio, rt_uint8_t cmd, void *arg);
+    rt_size_t   (*transmit)     	(struct rt_audio_device *audio, const void *writeBuf,void *readBuf, rt_size_t size);
+
+    //get page size of codec or private buffer's info
+    void		(*buffer_info)		(struct rt_audio_device *audio,struct rt_audio_buf_info *info );
+#ifdef AUDIO_DEVICE_USE_PRIVATE_BUFFER
+    rt_err_t	(*buffer_alloc)		(struct rt_audio_device *audio,rt_uint8_t **data_ptr,rt_size_t *size);
+    void		(*buffer_free)		(struct rt_audio_device *audio,rt_uint8_t *data_ptr);
+#endif
+};
+
+
+struct rt_audio_configure
+{
+    rt_uint32_t channels;
+
+    rt_uint32_t samplefmt;
+    rt_uint32_t samplerate;
+    rt_uint32_t samplefmts;
+};
+
+struct rt_audio_caps
+{
+	int main_type;
+	int sub_type;
+
+	union
+	{
+		rt_uint32_t mask;
+		int 		value;
+		struct rt_audio_configure config;
+	}udata;
+};
+
+struct rt_audio_replay
+{
+    rt_bool_t activated;
+	struct rt_audio_queue 		queue;
+};
+
+struct rt_audio_record
+{
+    rt_bool_t activated;
+    struct rt_pipe_device		pipe;
+};
+
+struct rt_audio_device
+{
+    struct rt_device        	parent;
+    struct rt_audio_ops  		*ops;
+
+    struct rt_audio_replay	*replay;
+    struct rt_audio_record *record;
+};
+
+rt_err_t rt_audio_register(struct rt_audio_device *audio, const char *name, rt_uint32_t flag, void *data);
+void rt_audio_tx_complete(struct rt_audio_device *audio,rt_uint8_t *pbuf);
+void rt_audio_rx_done(struct rt_audio_device *audio,rt_uint8_t *pbuf,rt_size_t len);
+rt_uint32_t rt_audio_format_to_bits(rt_uint32_t format);
+
 
 /* Device Control Commands */
 #define CODEC_CMD_RESET             0
@@ -11,4 +222,4 @@
 
 #define CODEC_VOLUME_MAX            (63)
 
-#endif
+#endif /* __AUDIO_H__ */