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

来源:互联网 发布:软件大全网站 编辑:程序博客网 时间:2024/05/10 15:11


该版本的声卡驱动有缺陷,放音是不正常的,会断断续续,所以还需要修改。主要就是对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 0
原创粉丝点击