基于S3C2440的Linux-3.6.6移植——声卡移植



该版本的声卡驱动有缺陷,放音是不正常的,会断断续续,所以还需要修改。主要就是对sound/soc/samsung目录下dma.c文件进行修改。基于网上的资料,我整理如下:

 

首先把第63行的下列语句注释掉,因为要重新写该函数:

static void audio_buffdone(void *data);

 

然后把dma_enqueue函数和audio_buffdone替换为下面的内容:

static void dma_enqueue(structsnd_pcm_substream *substream)

{

       structruntime_data *prtd = substream->runtime->private_data;

       dma_addr_tpos = prtd->dma_pos;

       unsignedint limit;

       intret;

 

       pr_debug("Entered%s\n", __func__);

 

       limit= (prtd->dma_end - prtd->dma_start) / prtd->dma_period;

 

       pr_debug("%s:loaded %d, limit %d\n",

                            __func__,prtd->dma_loaded, limit);

 

       while(prtd->dma_loaded < limit) {

              unsignedlong len = prtd->dma_period;

              pr_debug("dma_loaded:%d\n", prtd->dma_loaded);

 

              ret= s3c2410_dma_enqueue(prtd->params->channel,

                     substream,pos,len);

 

              if(ret== 0){

                     prtd->dma_loaded++;

                     pos+= prtd->dma_period;

                     if(pos>=prtd->dma_end)

                            pos= prtd->dma_start;

              }else

                     break;

       }

 

       prtd->dma_pos= pos;

}

 

static void audio_buffdone(structs3c2410_dma_chan *channel,

                                   void*dev_id, int size,

                                   enums3c2410_dma_buffresult result)

{

       structsnd_pcm_substream *substream = dev_id;

       structruntime_data *prtd;

 

       pr_debug("Entered%s\n", __func__);

 

       if(result== S3C2410_RES_ABORT || result == S3C2410_RES_ERR)

              return;

 

       prtd= substream->runtime->private_data;

 

       if(substream)

              snd_pcm_period_elapsed(substream);

 

       spin_lock(&prtd->lock);

       if(!samsung_dma_has_circular()){

              prtd->dma_loaded--;

              dma_enqueue(substream);

       }

 

       spin_unlock(&prtd->lock);

}

 

最后在dma_hw_params函数中的snd_pcm_set_runtime_buffer(substream,&substream->dma_buffer);语句之前添加下列语句:

s3c2410_dma_set_buffdone_fn(prtd->params->channel,audio_buffdone);

 

在编译系统的时候,我们还需要配置menuconfig

Device Drivers --->

       <*>Soundcard support --->

              <*>AdvancedLinux Sound Architecture --->

                     <*>ALSAfor SoC audio support --->

                            <*>SynopsysI2S Device Driver

                            <*>ASoCsupport for Samsung

                            <*>SoCI2S Audio support UDA134X wired to a S3C24XX

                            <*>Buildall ASoC CODEC drivers

                            <*>ASoCSimple sound card support

 

这样声卡驱动的移植就完成了。在启动系统的时候,系统会自动打印出下列信息:

S3C24XX_UDA134X SoC Audio driver

soc-audio soc-audio: ASoC machineS3C24XX_UDA134X should use snd_soc_register_card()

UDA134X SoC Audio Codec

soc-audio soc-audio:  uda134x-hifi <-> s3c24xx-iis mapping ok

 

在系统启动以后,我们也可以查看到声卡设备:

[root@zhaocj /]#cat /proc/asound/devices

  2:[ 0- 0]: digital audio playback

  3:[ 0- 0]: digital audio capture

  4:[ 0]   : control

 33:       : timer

[root@zhaocj /dev]#ls /dev/pcm*

pcmC0D0c pcmC0D0p

 

Linux下的声卡驱动是基于ALSA的,ALSA提供了标准的库和工具可以用于测试Linux下的声卡。下面我们就来移植它们。

 

http://www.alsa-project.org/网站内下载这两个文件:alsa-lib-1.0.25.tar.bz2alsa-utils-1.0.25.tar.bz2。分别解压它们:

tar jxvf alsa-lib-1.0.25.tar.bz2

tar jxvf alsa-utils-1.0.25.tar.bz2

进入alsa-lib-1.0.25目录,执行下列命令:

./configure  --host=arm-linux  --prefix=/usr/share/arm-alsa  --disable-python  --with-alsa-devdir=/dev

make

make install

进入alsa-util-1.0.25目录,执行下列命令:

./configure --host=arm-linux--prefix=/usr/share/arm-alsa --with-alsa-inc-prefix=/usr/share/arm-alsa/include--with-alsa-prefix=/usr/share/arm-alsa/lib --disable-alsamixer --disable-xmlto

make

make install

然后把编译好的一些文件复制到根文件的相应目录下:

cp  -r /usr/share/arm-alsa/share/*  /home/zhaocj/root/rootfs/usr/share/arm-alsa/share/

cp  /usr/share/arm-alsa/bin/*  /home/zhaocj/root/rootfs/bin/

cp  -d /usr/share/arm-alsa/lib/liba*  /home/zhaocj/root/rootfs/lib

我的根文件系统在/home/zhaocj/root/rootfs目录下,另外在复制之前,还需要手动创建usr/share/arm-alsa/share/目录。运行ALSA需要一起库文件,如果根文件系统没有相关的库文件,还需要复制所需的库文件,具体内容请看我以前的文章——动态库的根文件系统的制作

 

一切准备就绪,我们来测试一下声卡。启动开发板,上传一个wav音频文件到temp目录下,然后执行下列命令:

[root@zhaocj /temp]#aplay music.wav

我们会看到系统在放音的同时,会在命令行下显示该wav文件的相关信息。

 

我们写一段应用程序来对声卡放音进行测试,该文件名为myplay.c

#include <alsa/asoundlib.h>

 

struct WAV_HEADER

{

       char rld[4];                              //"RIFF"标志

       intrLen;                                     //文件长度

       charwld[4];                               //"WAVE"标志

       charfld[4];                                //"fmt"标志

       intfLen;

       shortwFormatTag;                      //编码格式

       shortwChannels;                        //声道数

       intnSamplesPersec ;                   //采样频率

       intnAvgBitsPerSample;               //每秒的字节数 = 采样频率×每采样数据的字节数

       shortwBlockAlign;                     //每采样数据的字节数 = 采样位数 × 声道数 / 8

       shortwBitsPerSample;               //每个采样数据的位数(8位、16位等)

       chardld[4];                                //"data"标记符

       intwSampleLength;                    //音频数据的大小

} wav_header;

 

int main(int argc,char *argv[])

{

       intfd, size, err;

       snd_pcm_t*playback_handle;

       snd_pcm_hw_params_t*hw_params;

       snd_pcm_uframes_tframes;

       char*buffer;

 

       if(argc!=2)

       {

              printf("使用规则:myplay + WAV文件名\n");

              return-1;

       }

             

       fd= open(argv[1],O_RDONLY);

       if(fp< 0)

       {

              perror("openfile failed.\n");

              return-1;

       }

             

       if(read(fd,&wav_header, sizeof(wav_header)) < 0 )

       {

              printf("readwave file header error!");

              return-1;

       }

             

       printf("%s文件文件大小:%d\n",argv[1], wav_header.rLen);

       printf("声道数:%d\n",wav_header.wChannels);

       printf("采样频率:%d\n",wav_header.nSamplesPersec);

       printf("采样的位数:%d\n",wav_header.wBitsPerSample);

       printf("音频数据大小:%d\n",wav_header.wSampleLength);

             

       //打开PCM设备

       if(snd_pcm_open(&playback_handle,"default",SND_PCM_STREAM_PLAYBACK,0)< 0)

       {

              printf("cantopen audio device %s \n",argv[1]);

              return-1;

       }

   

       //分配snd_pcm_hw_params_t结构体

       if(snd_pcm_hw_params_malloc(&hw_params)< 0)

       {

              printf("cantopen allocate parameter structure. \n");

              return-1;

       }

   

       //初始化snd_pcm_hw_params_t结构体

       if(snd_pcm_hw_params_any(playback_handle,hw_params) < 0)

       {

              printf("snd_pcm_hw_params_any.\n");

              return-1;

       }

   

       //初始化访问权限

       if(snd_pcm_hw_params_set_access(playback_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED) < 0)

       {

              printf("cantset access type.  \n");

              return-1;

       }

   

       //采样位数

       switch(wav_header.wBitsPerSample/ 8)

       {

              case1:

                     snd_pcm_hw_params_set_format(playback_handle,hw_params,SND_PCM_FORMAT_U8);

                     break;

              case2:

                     snd_pcm_hw_params_set_format(playback_handle,hw_params,SND_PCM_FORMAT_S16_LE);

                     break;

              case3:

                     snd_pcm_hw_params_set_format(playback_handle,hw_params,SND_PCM_FORMAT_S24_LE);

                     break;

              default:

                     return-1;

       }

   

       //设置声道,1表示单声道,2表示立体声

       if(snd_pcm_hw_params_set_channels(playback_handle,hw_params, wav_header.wChannels) < 0)

       {

              printf("snd_pcm_hw_params_set_channels.\n");

              return-1;

       }

   

       //设置采样频率

       if(snd_pcm_hw_params_set_rate_near(playback_handle,hw_params, &wav_header.nSamplesPersec, 0) < 0)

       {

              printf("snd_pcm_hw_params_set_rate_near.\n");

              return-1;

       }

             

       //设置参数

       if(snd_pcm_hw_params(playback_handle,hw_params) < 0)

       {

              printf("snd_pcm_hw_params.\n");

              return-1;

       }

      

       //获得周期大小;

       if(snd_pcm_hw_params_get_period_size(hw_params,&frames, 0) < 0)

       {

              printf("snd_pcm_hw_params_get_period_size.\n");

              return-1;

       }

             

       size= frames * wav_header.wBlockAlign;

       buffer=(char*)malloc(size);

       lseek(fd,58,SEEK_SET);//定位歌曲到数据区,,44

      

       while(1)

       {

              memset(buffer,0,sizeof(buffer));

                    

              if(read(fd,buffer, size) == 0)

              {

                     printf("音频播放完毕\n");

                     break;

              }

                                                       

              //写音频数据到PCM设备

              if(err=snd_pcm_writei(playback_handle,buffer, frames) == -EPIPE)

              {

                     printf("XRUN.\n");

                     snd_pcm_prepare(playback_handle);

              }

              elseif(err < 0)

              {

                     printf("ERROR.cannotwrite to PCM device.%s\n",snd_strerror(err));

              }

       }

             

       snd_pcm_drain(playback_handle);

       snd_pcm_close(playback_handle);

       free(buffer);         

      

       return0;

}

 

需要说明的是,通过snd_pcm_hw_params_get_period_size函数可以得到周期,即一帧数据的采样个数frames,也就是PCM设备在一次中断所能处理的采样数。而PCM设备一次中断所能处理的字节数量为一帧的采样个数×每个采样的字节数,即size= frames * wav_header.wBlockAlign。每次我们都从WAV文件中读取size个字节数的数据,然后交给PCM设备去处理,直到所有数据从WAV文件中读取完为止。向PCM设备写数据(snd_pcm_writei)可以实现放音,该函数的第二个参数就是从WAV文件所读取的数据,第三个参数就是周期大小。

 

使用下列命令编译该文件:

arm-linux-gcc  -lasound -L/usr/share/arm-alsa/lib -I/usr/share/arm-alsa/include  -lm  -ldl -lpthread  -o  myplay myplay.c

 

最后需要强调一点的是,本次声卡驱动的移植,仅能够保障放音功能的基本正常。对我的开发板来说,录音功能还实现不了。我认为硬件应该是没有问题的,那一定是驱动的问题。但很遗憾,本人能力有限,还无法找到问题所在。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值