linux 命令行 音频分析,linux音频驱动分析

linux音频驱动分析

creator   sz111@126.com

int __init utu2440_uda1341_init(void)

{

int             ret = 0;

//printk("ghcstop.........probe\n");

//首先是对L3总线的一些控制操作。

ret = l3_attach_client(&uda1341, "l3-bit-24x0-gpio", "uda1341");

if (ret)

{

printk("l3_attach_client() failed.\n");

return ret;

}

l3_open(&uda1341);

start_uda1341();

//定义输出和输入stream和对应的DMA

output_stream.dma_ch = S3C2410_DMA_CH2;

output_stream.dmaclient.name = "audio_out";

//申请输出DMA

if (audio_init_dma(&output_stream, "UDA1341 out"))

{

audio_clear_dma(&output_stream);

printk(KERN_WARNING AUDIO_NAME_VERBOSE ": unable to get DMA channels\n");

return -EBUSY;

}

input_stream.dma_ch = S3C2410_DMA_CH1;

input_stream.dmaclient.name = "audio_in";

if (audio_init_dma(&input_stream, "UDA1341 in"))

{

audio_clear_dma(&input_stream);

printk(KERN_WARNING AUDIO_NAME_VERBOSE ": unable to get DMA channels\n");

return -EBUSY;

}

audio_dev_dsp = register_sound_dsp(&utu2440_audio_fops, -1);

audio_dev_mixer = register_sound_mixer(&utu2440_mixer_fops, -1);

printk(AUDIO_NAME_VERBOSE " initialized\n");

return 0;

}

上述中audio_stream的数据结构如下:

typedef struct

{

int             size;       /* buffer size */

char           *start;      /* point to actual buffer */

dma_addr_t      dma_addr;   /* physical buffer address */

struct semaphore sem;       /* down before touching the buffer */

int             master;     /* owner for buffer allocation, contain size when true */

} audio_buf_t;

typedef struct

{

audio_buf_t    *buffers;    /* pointer to audio buffer structures */

audio_buf_t    *buf;        /* current buffer used by read/write */

u_int           buf_idx;    /* index for the pointer above */

u_int           fragsize;   /* fragment i.e. buffer size */

u_int           nbfrags;    /* nbr of fragments */

int             bytecount;  /* nbr of processed bytes */

int             fragcount;  /* nbr of fragment transitions */

u_int           channels;   /* audio channels 1:mono, 2:stereo */

u_int           rate;       /* audio rate */

dmach_t         dma_ch;     /* DMA channel (channel2 for audio) */

int             active:1;   /* actually in progress */

int             stopped:1;  /* might be active but stopped */

wait_queue_head_t frag_wq;  /* for poll(), etc. */

s3c2410_dma_client_t dmaclient; /* kernel 2.6 dma client */

} audio_stream_t;

void __exit utu2440_uda1341_exit(void)

{

unregister_sound_dsp(audio_dev_dsp);

unregister_sound_mixer(audio_dev_mixer);

audio_clear_dma(&output_stream);

audio_clear_dma(&input_stream);

l3_close(&uda1341);

l3_detach_client(&uda1341);

printk(AUDIO_NAME_VERBOSE " unloaded\n");

//return 0;

}

/* s3c2410_request_dma

*

* get control of an dma channel

*/

//这个函数的任务就是申请DMA通道和DMA中断。

int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client,

void *dev)

{

s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];

unsigned long flags;

int err;

pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",

channel, client->name, dev);

check_channel(channel);

local_irq_save(flags);

dbg_showchan(chan);

if (chan->in_use) {

if (client != chan->client) {

printk(KERN_ERR "dma%d: already in use\n", channel);

local_irq_restore(flags);

return -EBUSY;

} else {

printk(KERN_ERR "dma%d: client already has channel\n", channel);

}

}

chan->client = client;

chan->in_use = 1;

if (!chan->irq_claimed) {

pr_debug("dma%d: %s : requesting irq %d\n",

channel, __FUNCTION__, chan->irq);

//申请DMA中断,每个通道有一个他对应的中断,但是所有的通道的中断

//都是采用s3c2410_dma_irq函数进行处理的,所以要在s3c2410_dma_irq

//函数中对通道进行判断,所以在request_irq中最后一个数据是channel的

//指针。虽然这个申请的中断并不是共享中断。

err = request_irq(chan->irq, s3c2410_dma_irq, SA_INTERRUPT,

client->name, (void *)chan);

if (err) {

chan->in_use = 0;

local_irq_restore(flags);

printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n",

client->name, chan->irq, chan->number);

return err;

}

chan->irq_claimed = 1;

chan->irq_enabled = 1;

}

local_irq_restore(flags);

/* need to setup */

pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan);

return 0;

}

//dma中断的处理程序。

static irqreturn_t

s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs)

{

s3c2410_dma_chan_t *chan = (s3c2410_dma_chan_t *)devpw;

s3c2410_dma_buf_t  *buf;

buf = chan->curr;

dbg_showchan(chan);

/* modify the channel state */

switch (chan->load_state) {

case S3C2410_DMALOAD_1RUNNING:

/* TODO - if we are running only one buffer, we probably

* want to reload here, and then worry about the buffer

* callback */

chan->load_state = S3C2410_DMALOAD_NONE;

break;

case S3C2410_DMALOAD_1LOADED:

/* iirc, we should go back to NONE loaded here, we

* had a buffer, and it was never verified as being

* loaded.

*/

chan->load_state = S3C2410_DMALOAD_NONE;

break;

case S3C2410_DMALOAD_1LOADED_1RUNNING:

/* we'll worry about checking to see if another buffer is

* ready after we've called back the owner. This should

* ensure we do not wait around too long for the DMA

* engine to start the next transfer

*/

chan->load_state = S3C2410_DMALOAD_1LOADED;

break;

case S3C2410_DMALOAD_NONE:

printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n",

chan->number);

break;

default:

printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n",

chan->number, chan->load_state);

break;

}

if (buf != NULL) {

/* update the chain to make sure that if we load any more

* buffers when we call the callback function, things should

* work properly */

chan->curr = buf->next;

buf->next  = NULL;

if (buf->magic != BUF_MAGIC) {

printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n",

chan->number, __FUNCTION__, buf);

return IRQ_HANDLED;

}

s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK);

/* free resouces */

s3c2410_dma_freebuf(buf);

} else {

}

if (chan->next != NULL) {

unsigned long flags;

switch (chan->load_state) {

case S3C2410_DMALOAD_1RUNNING:

/* don't need to do anything for this state */

break;

case S3C2410_DMALOAD_NONE:

/* can load buffer immediately */

break;

case S3C2410_DMALOAD_1LOADED:

if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {

/* flag error? */

printk(KERN_ERR "dma%d: timeout waiting for load\n",

chan->number);

return IRQ_HANDLED;

}

break;

case S3C2410_DMALOAD_1LOADED_1RUNNING:

goto no_load;

default:

printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n",

chan->number, chan->load_state);

return IRQ_HANDLED;

}

local_irq_save(flags);

s3c2410_dma_loadbuffer(chan, chan->next);

local_irq_restore(flags);

} else {

s3c2410_dma_lastxfer(chan);

/* see if we can stop this channel.. */

if (chan->load_state == S3C2410_DMALOAD_NONE) {

pr_debug("dma%d: end of transfer, stopping channel (%ld)\n",

chan->number, jiffies);

s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP);

}

}

no_load:

return IRQ_HANDLED;

}

Atomic transfer:指的是DMA的单次原子操作,它可以是Unit模式(传输1个data size),也可以是burst模式(传输4个data size),具体对应DCON[28]。

Data Size:指的是单次原子操作的数据位宽,8、16、32,具体对应DCON[21:20]。

Request

Source:DMA请求的来源有两种,软件&硬件模块,由DCON[23]控制;当为前者时,由软件对DMASKTRIG寄存器的位0置位触发一次

DMA 操作。当为后者时,具体来源由DCON[26:24]控制,不同硬件模块的某时间触发一次DMA操作,具体要见不同的硬件模块。

DMA service

mode:DMA的工作模式有两种,单一服务模式&整体服务模式。前一模式下,一次DMA请求完成一项原子操作,并且transfer

count的值减1。后一模式下,一次DMA请求完成一批原子操作,直到transfer

count等于0表示完成一次整体服务。具体对应DCON[27]。

RELOAD:在reload模式下,当transfer count的值变为零时,将自动加src、dst、TC的值加载到CURR_DST、

CURR_SRC、CURR_TC,并开始一次新的DMA传输。该模式一般和整体服务模式一起使用,也就是说当一次整体服务开始后,src、dst、TC

的值都已经被加载,因此可以更改为下一次

服务的地址,2410说明文档中建议加入以下语句来判断当前的服务开始,src、dst、TC的值可以被更改了:while((rDSTATn & 0xfffff) == 0) ;

Req&Ack:DMA请求和应答的协议有两种,Demard mode 和 Handshake mode。两者对Request和Ack的时序定义有所不同:在Demard模式下,如果

DMA完成一次请求如果Request仍然有效,那么DMA就认为这是下一次DMA请求;在Handshake模式下,DMA完成一次请求后等待Request信号无效,然后把ACK也置无效,再等待下一次Request。这个设计外部DMA请求时可能要用到。

传输总长度:DMA一次整体服务传输的总长度为:

Data Size × Atomic transfer size × TC(字节)。

static int __init audio_init_dma(audio_stream_t * s, char *desc)

{

int ret;

if (s->dma_ch == S3C2410_DMA_CH2) // 输出DMA

{

//申请DMA

ret = s3c2410_dma_request(s->dma_ch, &(s->dmaclient), NULL);

if( ret )

{

dprintk("%s: dma request err\n", __FUNCTION__ );

return ret;

}

ao_dcon =

S3C2410_DCON_HANDSHAKE|S3C2410_DCON_SYNC_PCLK|S3C2410_DCON_TSZUNIT|S3C2410_DCON_SSERVE|S3C2410_DCON_CH2_I2SSDO|S3C2410_DCON_NORELOAD|

s3c2410_dma_config(s->dma_ch, 2, ao_dcon); // a out, halfword

/* flags */

#define S3C2410_DMAF_SLOW         (1dma_ch, S3C2410_DMAF_AUTOSTART); // a out

//在这里定义buffdone callback,他会在dma中断里面调用。

s3c2410_dma_set_buffdone_fn(s->dma_ch, audio_dmaout_done_callback);

//设定sourc为mem,

#define BUF_ON_MEM        (ON_AHB | ADDR_INC)

#define BUF_ON_APB        (ON_APB    | ADDR_FIX)

s3c2410_dma_devconfig(s->dma_ch, S3C2410_DMASRC_MEM, BUF_ON_APB, 0x55000010);

dprintk("%s: dma request done audio out channel\n", __FUNCTION__ );

return 0;

}

else if (s->dma_ch == S3C2410_DMA_CH1)

{

ret = s3c2410_dma_request(s->dma_ch, &(s->dmaclient), NULL);

if( ret )

{

dprintk("%s: dma request err\n", __FUNCTION__ );

return ret;

}

ai_dcon =

S3C2410_DCON_HANDSHAKE|S3C2410_DCON_SYNC_PCLK|S3C2410_DCON_TSZUNIT|S3C2410_DCON_SSERVE|S3C2410_DCON_CH1_I2SSDI|S3C2410_DCON_NORELOAD|

s3c2410_dma_config(s->dma_ch, 2, ai_dcon); // a in, halfword

s3c2410_dma_setflags(s->dma_ch, S3C2410_DMAF_AUTOSTART); // a in

s3c2410_dma_set_buffdone_fn(s->dma_ch, audio_dmain_done_callback);

s3c2410_dma_devconfig(s->dma_ch, S3C2410_DMASRC_HW, BUF_ON_APB, 0x55000010);

dprintk("%s: dma request done audio in channel\n", __FUNCTION__ );

return 0;

}

else

return 1;

}

然后就是对open的介绍了。

static int

utu2440_audio_open(struct inode *inode, struct file *file)

{

int             cold = !audio_active;

dprintk("audio_open\n");

if ((file->f_flags & O_ACCMODE) == O_RDONLY)

{

if (audio_rd_refcount || audio_wr_refcount)

return -EBUSY;

audio_rd_refcount++;

}

else if ((file->f_flags & O_ACCMODE) == O_WRONLY)

{

if (audio_wr_refcount)

return -EBUSY;

audio_wr_refcount++;

}

else if ((file->f_flags & O_ACCMODE) == O_RDWR)

{

if (audio_rd_refcount || audio_wr_refcount)

return -EBUSY;

audio_rd_refcount++;

audio_wr_refcount++;

}

else

return -EINVAL;

if (cold)

{

audio_rate = AUDIO_RATE_DEFAULT;

audio_channels = AUDIO_CHANNELS_DEFAULT;

/*

* the UDA1341 is stereo only ==> 2 channels

*/

if ((file->f_mode & FMODE_WRITE))

{

output_stream.fragsize = AUDIO_FRAGSIZE_DEFAULT; //每个缓冲区的大小

output_stream.nbfrags = AUDIO_NBFRAGS_DEFAULT; //环形缓冲区的数量

output_stream.channels = audio_channels;

start_utu2440_iis_bus_tx();

audio_clear_buf(&output_stream);

init_waitqueue_head(&output_stream.frag_wq);

}

if ((file->f_mode & FMODE_READ))

{

input_stream.fragsize = AUDIO_FRAGSIZE_DEFAULT;

input_stream.nbfrags = AUDIO_NBFRAGS_DEFAULT;

input_stream.channels = audio_channels;

start_utu2440_iis_bus_rx();

audio_clear_buf(&input_stream);

init_waitqueue_head(&input_stream.frag_wq);

}

}

return 0;

}

关键部分的函数。

static          ssize_t

utu2440_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)

{

const char     *buffer0 = buffer;

audio_stream_t *s = &output_stream;

int             chunksize,

ret = 0;

dprintk("audio_write : start count=%d\n", count);

switch (file->f_flags & O_ACCMODE)

{

case O_WRONLY:

case O_RDWR:

break;

default:

return -EPERM;

}

if (!s->buffers && audio_setup_buf(s))

return -ENOMEM;

count &= ~0x03;

while (count > 0)

{

audio_buf_t    *b = s->buf;

if (file->f_flags & O_NONBLOCK)

{

ret = -EAGAIN;

if (down_trylock(&b->sem))

break;

}

else

{

ret = -ERESTARTSYS;

if (down_interruptible(&b->sem))

break;

}

if (s->channels == 2)

{   //每次传输的是一个缓冲区的大小,b->size代表目前传输了多少。

chunksize = s->fragsize - b->size;

if (chunksize > count)

chunksize = count;

dprintk("write %d to %d\n", chunksize, s->buf_idx);

if (copy_from_user(b->start + b->size, buffer, chunksize))

{

up(&b->sem);

return -EFAULT;

}

b->size += chunksize;

}

else

{

chunksize = (s->fragsize - b->size) >> 1;

if (chunksize > count)

chunksize = count;

dprintk("write %d to %d\n", chunksize * 2, s->buf_idx);

if (copy_from_user_mono_stereo(b->start + b->size, buffer, chunksize))

{

up(&b->sem);

return -EFAULT;

}

b->size += chunksize * 2;

}

buffer += chunksize;

count -= chunksize;

if (b->size fragsize)

{

up(&b->sem);

break;

}

s->active = 1;          // ghcstop add

//每次从b->dma_addr开始传输 b->size个数据,一直传输到所有的缓冲区满,

//等待dma传输中断,释放sem,可以有空闲的缓冲区。

s3c2410_dma_enqueue(s->dma_ch, (void *) b, b->dma_addr, b->size);

b->size = 0;

NEXT_BUF(s, buf);

}

if ((buffer - buffer0))

ret = buffer - buffer0;

dprintk("audio_write : end count=%d\n\n", ret);

return ret;

}

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/49088/showart_499493.html

阅读(1203) | 评论(0) | 转发(0) |

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值