Chinaunix首页 | 论坛 | 博客
  • 博客访问: 353398
  • 博文数量: 49
  • 博客积分: 3229
  • 博客等级: 中校
  • 技术积分: 616
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-26 21:46
文章分类

全部博文(49)

文章存档

2011年(8)

2010年(2)

2009年(3)

2008年(36)

我的朋友

分类:

2008-07-30 15:43:21

搞定了linux-2.6.20下的。一开始,从网上找了驱动移植过 去,编译通过,但在用qtopia中的播放器播放音视频的时候,每次都会卡住。在smdk2410_audio_write函数中加上各种调试信息,发 现:每次都是调用8次smdk2410_audio_write后,程序就卡住了。进一步分析发现,原来在执行中的
if (down_interruptible(&b->sem))这一句的时候,卡在了这个down操作处。

先来看看smdk2410_audio_write这个函数吧:
staticssize_t smdk2410_audio_write(structfile*file,constchar*buffer,
        size_tcount,loff_t*ppos)
{   
   constchar*buffer0=buffer;
    audio_stream_t*s=&output_stream;
   intchunksize,ret=0;
   switch(file->f_flags&O_ACCMODE){
        caseO_WRONLY:
        caseO_RDWR:
            break;
        default:
            return-EPERM;
   }
   //如果s->buffers=NUll,则初始化s,也即初始化output_stream,由此可知,output_stream只在第一次调用smdk2410_audio_write的时候被初始化。

   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)){//对b->sem执行down操作,以互斥。

        //        return ret;

               break;
            }
        }
        if(audio_channels==2){
            chunksize=s->fragsize-b->size;//确定每次传输的字节数。b->size=0

            if(chunksize>count)
                chunksize=count;
            if(copy_from_user(b->start+b->size,buffer,chunksize)){
                up(&b->sem);//出错后则up信号量,并返回

               return-EFAULT;
            }
            b->size+=chunksize;
        }else{//对于本驱动而言,audio_channels被成了2,所以这下面的是不会被执行的

            chunksize=(s->fragsize-b->size)>>1;
            if(chunksize>count)
                chunksize=count;
            if(copy_from_user_mono_stereo(b->start+b->size,
                buffer,chunksize)){
                    up(&b->sem);
                    return-EFAULT;
               }
                b->size+=chunksize*2;
        }
        buffer+=chunksize;//更新buffer位置

        count-=chunksize;// 更新还需传输字节数

        if(b->size<s->fragsize){//不知道这里的意思是什么

            up(&b->sem);
            break;
        }
        ret=s3c2410_dma_enqueue(s->dmach,(void*)b,b->dma_addr,b->size);//提交b到dma对列中

        if(ret){
            printk("dma enqueue failed.\n");
            returnret;
        }
        b->size=0;
        NEXT_BUF(s,buf);//让s->buf指向s->buffers中的下一个位置

   }
   if((buffer-buffer0))
        ret=buffer-buffer0;
    printk(KERN_WARNING"ret is %d\n",ret);
   returnret;
}


这时,我有了一个疑问,在本write操作中,我们只看到了down信号量,以及在出错的时候up信号量,正确传输之后怎么没有up信号量呢。查了一点资料,知道了当dma传输完成之后,会调用回调用函数, 顺藤摸瓜,找到相应的回调函数
statcvoidaudio_dmaout_done_callback(structs3c2410_dma_chan*ch,void*buf,intsize,enums3c2410_dma_buffresult result)
{
    audio_buf_t*b=(audio_buf_t*)buf;
    up(&b->sem);
    wake_up(&b->sem.wait);
}

audio_dmaout_done_callback函数中的确会up信号量。但根据驱动打印出的信息,说明没有调用回调函数,也就是说没有完成dma传输。那问题是不是出在s3c2410_dma_enqueue函数中呢,查看其源码,
ints3c2410_dma_enqueue(unsignedintchannel,void*id,
            dma_addr_t data,intsize)
{
   ..................
        if(chan->flags&S3C2410_DMAF_AUTOSTART){
            s3c2410_dma_ctrl(chan->number,S3C2410_DMAOP_START);
        }
   ...................
    local_irq_restore(flags);
   return0;
}


天 啊,s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START);第一个参数居然为chan->number。2.6.20的内核中,DMA通道号是经过虚实映射的。 dma相关的函数都是接受虚拟的dma通道号,然后再通过lookup_dma_channel函数将虚拟通道号转换成相应的实际通道号,这里的参数 chan->number很明显是个实通道号呀(虚拟通道号的变量名一般为channel)。帮修改 s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START);为s3c2410_dma_ctrl(
chan->number|DMACH_LOW_LEVEL, S3C2410_DMAOP_START);重新编译,下载内核后,用qtopia中的播放器播放音视频均正常。
阅读(2594) | 评论(0) | 转发(0) |
0

上一篇:关于2.6.20中的dma的

下一篇:一道C的面试题

给主人留下些什么吧!~~