36、ALSA声卡驱动和应用

(注意;内核上电的时候会把一些没运行的控制器模块的时钟都关掉,所有在写驱动的时候需要在使用的使用使用clk_get和clk_enable使能时钟)

(说明:与ALSA声卡对应的是OSS架构,第二期视频中的声卡驱动就是指的OSS架构驱动,ALSA可以模拟OSS)

(amixer controls执行后返回的可设置属性里面input mux表示录音的时候的通道,在mini2440和tq2440上两者不同,需要修改)

1.裸板
WAV文件格式:http://blog.chinaunix.net/uid-21977330-id-3976817.html

涉及的操作:A、SOC(2440)部分的IIS操作,DMA操作(一般就是设置IIS管脚和寄存器,通过DMA把数据拷贝到IIS的FIFO寄存器)

      B、Codec(编解码芯片)部分的设置(初始化、格式、音量)

           C、Machine(单板)部分的控制引脚不同,使用控制引脚来初始化Codec

裸版程序也是分为这三个部分来操作

2.驱动

sound.c是顶层驱动框架,里面会注册个file_operations,其只有一个open函数,在open函数中已次设备号在snd_minors数组中找到一项,取出真正的file_operation结构体,入口函数会创建一个字符设备register_chrdev

说明:

snd_card_create(init.c)

  snd_ctl_create(同一个声卡有多个逻辑设备,ctl应该是控制类的逻辑设备)

    .dev_register = snd_ctl_dev_register

    snd_device_new(创建声卡的ctl逻辑设备,通过参数SNDRV_DEV_CONTROL指定,其会导致dev_register 被调用,也就是snd_ctl_dev_register被调用)    

      snd_ctl_dev_register(control.c中)

        snd_register_device(这个函数有个参数snd_ctl_f_ops,其是真正的file_operation结构体)

          snd_register_device_for_dev(用数据填充preg)

            snd_minors[minor]=preg;

            device_create(在class下创建设备)

snd_pcm_new(某个声卡驱动里面调用snd_pcm_new)

  _snd_pcm_new

    snd_pcm_new_stream(pcm,.....PLAYBACK,playback_count);//创建播放 

    snd_pcm_new_stream(pcm,.....CAPTURE,capture_count);//创建录音

    .dev_register = snd_pcm_dev_register

       snd_device_new(创建声卡的pcm逻辑设备,通过参数SNDRV_DEV_PCM指定,其会导致dev_register 被调用,也就是snd_pcm_dev_register被调用)   

      snd_pcm_dev_register()

        for(执行两次,声卡具有录音和播放功能)

          snd_register_device_for_dev(pcm.c中)(有个snd_pcm_f_ops[]数组,是真正的file_operation结构体)

            snd_minors[minor]=preg;

            device_create(在class下创建设备,class是在sound_core.c中的init_soundcore中被调用)

怎么写ALSA声卡驱动?(参考:Linux ALSA声卡驱动.pdf)

1、调用snd_card_create接口创建snd_card的一个实例

2、初始化,调用snd_pcm_new和snd_ctl_create(snd_ctl_create在snd_card_create中已被默认调用)等接口创建声卡的功能部件(逻辑设备)

3、snd_card_register

linux在ALSA架构的基础上又封装了一个ASOC(alsa system on chip),使用ASOC的代码不用安装ALSA的架构写驱动,ASOC把驱动分为三部分:

1、machine:单板相关,表面platform是哪个,cpu DAI是哪个(cpu与codes相连的接口是哪个),DMA是哪个(负责数据传输)

            表面codec是哪个,codec DAI是哪个(codec与cpu相连的接口是哪个)

machine这块的代码对应linux内核中s3c24xx_uda134x.c,通过注册一个platform_driver,当通过总线发现有同名时,调用probe(A)函数,在内核中搜索注册的名字,发现在mach-mini2440.c中有个platform_device;

同时,在probe(A)函数中会通过platform_device_alloc("soc-audio")来创建一个device,因此肯定会有一个同名的driver,在内核中搜索,发现在soc-core.c中有个同名的driver,查看此结构中probe(B),其会调用snd_soc_register_card(card),这个card就是在probe(A)中通过platform_set_drvdata设置的核心结构体(struct snd_soc_card) snd_soc_s3c24XX_uda134x,这个核心结构体有一个dai_link的子结构体,里面有很多名字,比如:

codec_name = "uda134x-codec"//指定了用哪个codec

codec_dai_name = "uda134x-hifi"//指定了用codec芯片里的哪一个DAI(比如哪个IIS接口)

cpu_dai_name = "s3c24xx-iis"//指定了用CPU(比如2440)的那个DAI(比如哪个IIS接口)

platform_name = "samsung_audio"//指定了CPU端的DMA

根据这里设置的名字来找到对应的platform和codec驱动程序

2、platform:DAI  设置接口用来初始化codec及数据传输(对外),需要构造struct snd_soc_dai_driver s3c24xx_i2s_dai结构体(里面有接口支持的最小和最大通道数(左右声道))

      DMA 数据传输(内部),需要构造struct snd_soc_platform_driver samsung_asoc_platform结构体

根据 "s3c24xx-iis"名字在内核中搜索,找到s3c24xx-i2s.c文件中的platform_driver,devs.c文件中的platrom_device,这两者是一对,platform_driver中的probe函数调用snd_soc_register_dai(注册了一个dai),有个核心的结构体s3c24xx_i2s_dai,该结构体里面的函数用于操作cpu-DAI(比如提供设置2440端IIS控制器的函数等)

根据 "samsung_audio"名字在内核中搜索,找到sound/soc/samsung/dma.c文件中的platform_driver,sound/arm/plat-samsung/devs.c文件中的platrom_device,这两者是一对,platform_driver中的probe函数调用snd_soc_register_platform,其也有个核心结构体samsung_asoc_platform,该结构体里面的函数用于操作cpu的DMA

3、codec:DAI 要实现uda134x_dai

     控制接口 要实现soc_codec_dev_uda134x

根据 "uda134x-codec"名字在内核中搜索,找到sound/soc/codecs/uda134x.c文件中的platform_driver,arch/arm/mach-s3c24xx/mach-mini2440.c文件中的platrom_device,这两者是一对,platform_driver中的probe函数调用snd_soc_register_codec,关键结构体有struct snd_soc_codec_driver  soc_codec_dev_uda134x和struct snd_soc_dai_driver uda134x_dai,其中soc_codec_dev_uda134x就是提供了函数来完成控制接口初始化的工作,uda134x_dai提供DAI接口相关操作和参数

ASOC和ALSA这么关联起来:

1. platform:
1.1 s3c24xx-i2s.c : 把s3c24xx_i2s_dai放入链表dai_list, .name = "s3c24xx-iis",
s3c24xx_iis_dev_probe
  snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);
    list_add(&dai->list, &dai_list);

1.2 sound/soc/samsung/dma.c : 把samsung_asoc_platform放入了链表platform_list, .name = "samsung-audio",
samsung_asoc_platform_probe
  snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
    list_add(&platform->list, &platform_list);

2. codec: uda134x.c
uda134x_codec_probe
  snd_soc_register_codec(&pdev->dev,&soc_codec_dev_uda134x, &uda134x_dai, 1);
    struct snd_soc_codec *codec;
    codec->driver = codec_drv; (codec_drv= &soc_codec_dev_uda134x)

    snd_soc_register_dais(dev, dai_drv, num_dai); // dai_drv就是uda134x_dai
      list_add(&dai->list, &dai_list); : 把uda134x_dai放入了链表dai_list,dai_list里面既有uda134x_dai,也有s3c24xx_i2s_dai
    list_add(&codec->list, &codec_list);

3. machine:
s3c24xx_uda134x_probe
  s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
  platform_set_drvdata(s3c24xx_uda134x_snd_device, &snd_soc_s3c24xx_uda134x);
  platform_device_add(s3c24xx_uda134x_snd_device);//device添加的时候会导致soc-core.c里面的同名(名字是在上面设置的"soc-audio")driver的probe(soc_probe)函数被调用

    .....
    soc_probe
      snd_soc_register_card(card); // card = &snd_soc_s3c24xx_uda134x

        card->rtd = devm_kzalloc(card->dev,...
        card->rtd[i].dai_link = &card->dai_link[i]; // &s3c24xx_uda134x_dai_link

        list_add(&card->list, &card_list);

        snd_soc_instantiate_cards(); // 实例化声卡

          //对card_list里面的每一个card调用snd_soc_instantiate_card
          snd_soc_instantiate_card(card);
            3.1 /* bind DAIs */
            for (i = 0; i < card->num_links; i++)//soc-core.c是公共函数,这里的num_links是指的源码里面的各个平台的数目,比如samsung、mips、atmel,对各个平台的card绑定machine、platform、codec
              soc_bind_dai_link(card, i);
                3.1.1 /*根据名字在dai_list 中find CPU DAI */
                rtd->cpu_dai = cpu_dai; = //&s3c24xx_i2s_dai
                3.1.2 /*根据名字在codec_list 中 find_codec */
                rtd->codec = codec; = // codec, codec->driver=&soc_codec_dev_uda134x
                3.1.3 /*根据名字在dai_list 中 find CODEC DAI */
                rtd->codec_dai = codec_dai; // = &uda134x_dai
                3.1.4 /*根据名字在platform_list 中 find_platform(DMA) */
                rtd->platform = platform; // = &samsung_asoc_platform
            3.2 /* initialize the register cache for each available codec */
            ret = snd_soc_init_codec_cache(codec, compress_type);//初始化codec的寄存器

            3.3 snd_card_create(这里开始是ALSA驱动框架了,可以看前面的怎么写ALSA驱动)

            3.4 /* early DAI link probe */
            soc_probe_dai_link
              /* probe the cpu_dai *///调用cpu_dai的probe函数来做一些初始化,下面的一样
              /* probe the CODEC */
              /* probe the platform */ 
              /* probe the CODEC DAI */
              /* create the pcm */
              ret = soc_new_pcm(rtd, num);
                struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
                soc_pcm_ops->open = soc_pcm_open;
                soc_pcm_ops->close = soc_pcm_close;
                soc_pcm_ops->hw_params = soc_pcm_hw_params;
                soc_pcm_ops->hw_free = soc_pcm_hw_free;
                soc_pcm_ops->prepare = soc_pcm_prepare;
                soc_pcm_ops->trigger = soc_pcm_trigger;
                soc_pcm_ops->pointer = soc_pcm_pointer;
            
                snd_pcm_new (这里开始是ALSA驱动框架了,可以看前面的怎么写ALSA驱动)


            3.5 snd_card_register(这里开始是ALSA驱动框架了,可以看前面的怎么写ALSA驱动)


2.1 配置内核支持UDA1341:
CONFIG_SND_S3C24XX_I2S // s3c24xx-i2s.c
CONFIG_SND_SOC_SAMSUNG // dma.c

CONFIG_SND_SOC_UDA134X // uda134x.c

CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X // s3c24xx_uda134x.c

CONFIG_S3C24XX_DMA

-> Device Drivers
  -> Sound card support
    -> Advanced Linux Sound Architecture
      -> ALSA for SoC audio support
       <*> ASoC support for Samsung // CONFIG_SND_SOC_SAMSUNG
       <*> SoC I2S Audio support UDA134X wired to a S3C24XX // CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X // s3c24xx_uda134x.c

-> System Type
  [*] S3C2410 DMA support

2.2 修改代码

(根据上面的分析platform_device都是在mach_mini2440.c中被注册(定义有的在dev.c中),我们jz2440需要照样修改,如下)
a. 修改mach-smdk2440.c 添加"s3c24xx_uda134x"平台设备(machine)
b. 修改mach-smdk2440.c 添加"s3c24xx-iis"平台设备(platform IIS)
c. 修改mach-smdk2440.c 添加"samsung-audio"平台设备(platform DMA)
d. 修改mach-smdk2440.c 添加"uda134x-codec"平台设备

2.3 修改bug: soundsocsamsungdma.c
pos += prtd->dma_period;
改为
pos += prtd->dma_period*limit;


3. 编译alsa-lib, alsa-util以使用声卡:
3.1 alsa-lib(是库,应用程序使用库可以简化对声卡的操作)  :
sudo mv /usr /usr_bak
export PATH=/usr_bak/local/sbin:/usr_bak/local/bin:/usr_bak/sbin:/usr_bak/bin:/sbin:/bin:/usr_bak/games:/usr_bak/local/arm/4.3.2/bin
./configure --host=arm-linux(alsa-lib在编译的时候如果指定了prefix=A,即安装目录A,则必须把生成的库放到开发板上同名目录A,如果不指定则默认的是/usr,而PC机上usr目录下有很多问题,这样安装会破坏PC机usr目录,所有有上面的mv操作,先保存usr)
make
sudo mkdir /usr
sudo chown book:book /usr
make install
sudo cp -rf /usr /work/projects/alsa/
sudo rm -rf /usr
sudo mv /usr_bak /usr
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.3.2/bin

把头文件和库复制进交叉工具链里
cd /work/projects/alsa/usr/include
sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include

cd /work/projects/alsa/usr/lib
sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib

把库复制到根文件系统的lib目录下
cd /work/projects/alsa
sudo -rfd usr /work/nfs_root/fs_mini_mdev_new

3.2 alsa-util(是应用程序,通过alse-lib来访问声卡)
3.2.1 先编译依赖:ncurses-5.9.tar.gz
./configure --host=arm-linux --prefix=$PWD/tmp --with-shared(./configure --help会看到--with-shared表示会生成共享lib)
make && make install(会出错,不用理会,在tmp目录下已经生成lib了)

把头文件和库复制进交叉工具链里
cd /work/projects/alsa/ncurses-5.9/tmp/include/ncurses(必须还把ncurses目录拷贝到include中去,因为有些头文件回去ncurses目录下去找)
sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
cd /work/projects/alsa/ncurses-5.9/tmp/include/
sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include

cd /work/projects/alsa/ncurses-5.9/tmp/lib
sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib

把库复制到根文件系统的lib目录下
cd /work/projects/alsa/ncurses-5.9/tmp/lib
sudo cp *so* -rfd /work/nfs_root/fs_mini_mdev_new/lib


3.2.2 编译alsa-util:
./configure --host=arm-linux --prefix=$PWD/tmp --with-curses=ncurses --disable-xmlto --disable-nls
make
sudo make install 

cd ./tmp

sudo cp * -rfd /work/nfs_root/fs_mini_mdev_new/usr

3.2.3 测试(alsa-util生成的应用程序是访问/dev/snd下的声卡设备节点,所有需要生成链接文件)
mkdir /dev/snd
cd /dev/snd/
ln -s /dev/controlC0
ln -s /dev/pcmC0D0p
ln -s /dev/pcmC0D0c

播放:
aplay Windows.wav
调音量:
amixer controls

(执行上面的指令后会获得可以设置声卡的哪些属性,有个numid,再通过numid来设置具体的值)
amixer cget numid=1
amixer cset numid=1 30


4. 编译新的strace工具以跟踪声卡调用过程:(旧的工具识别不了alsa-util的ioctl)

见第二期视频blog,编译完成后把生成的bin目录下的工具全部拷贝到网络文件系统的bin目录下

执行strace -o aplay.log aplay Windows.wax  生成的log见后面或者“asoc分析.txt”

strace分析: aplay Windows.wav
1. /dev/snd/controlC0 对应的file_operations是snd_ctl_f_ops
open : snd_ctl_open
SNDRV_CTL_IOCTL_PVERSION : snd_ctl_ioctl -> put_user(SNDRV_CTL_VERSION, ip)
SNDRV_CTL_IOCTL_CARD_INFO : snd_ctl_ioctl -> snd_ctl_card_info(card, ctl, cmd, argp);
                        copy_to_user

SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE : snd_ctl_ioctl -> snd_pcm_control_ioctl -> get_user后control->prefer_pcm_subdevice = val;
(snd_ctl_ioctl 没有SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE 命令,其是在snd_control_ioctls这个东西中取出snd_pcm_control_ioctl结构体,snd_pcm_control_ioctl在alsa_pcm_init函数中通过snd_ctl_register_ioctl函数注册到 snd_control_ioctls中
close
上述三个ioctl不涉及硬件操作

2. /dev/snd/pcmC0D0p 对应的file_operations是snd_pcm_f_ops[0]
open : snd_pcm_playback_open
  snd_pcm_open
    snd_pcm_open_file
      struct snd_pcm_substream *substream;(这个结构体很有用)
      snd_pcm_open_substream(会对*substream;赋值)
        err = snd_pcm_hw_constraints_init(substream);
            snd_mask_any
            snd_interval_any
            ......
        err = substream->ops->open(substream) // substream->ops : snd_pcm_ops结构体(就是在soc_new_pcm里面设置的soc_pcm_ops)
                    soc_pcm_open
                      依次调用cpu_dai, dma, codec_dai, machine的open或startup函数
uda134x_startup 里:snd_pcm_hw_constraint_minmax(SNDRV_PCM_HW_PARAM_RATE),snd_pcm_hw_constraint_minmax(SNDRV_PCM_HW_PARAM_SAMPLE_BITS)
dma_open里: snd_pcm_hw_constraint_integer,snd_soc_set_runtime_hwparams
runtime->hw.info = hw->info; = SNDRV_PCM_INFO_INTERLEAVED |
    SNDRV_PCM_INFO_BLOCK_TRANSFER |
    SNDRV_PCM_INFO_MMAP |
    SNDRV_PCM_INFO_MMAP_VALID |
    SNDRV_PCM_INFO_PAUSE |
    SNDRV_PCM_INFO_RESUME,
        snd_pcm_hw_constraints_complete
      pcm_file->substream = substream;
      file->private_data = pcm_file;

注意:substream->ops = soc_new_pcm函数里的soc_pcm_ops

以下的ioctl入口都是:snd_pcm_playback_ioctl
SNDRV_PCM_IOCTL_INFO : snd_pcm_info_user(substream, arg);
                substream->ops->ioctl(substream,
                  snd_pcm_lib_ioctl

SNDRV_PCM_IOCTL_PVERSION : put_user(SNDRV_PCM_VERSION, (int __user *)arg)
SNDRV_PCM_IOCTL_TTSTAMP : snd_pcm_tstamp(substream, arg);

SNDRV_PCM_IOCTL_SYNC_PTR : snd_pcm_sync_ptr(substream, arg); 先不管

SNDRV_PCM_IOCTL_HW_REFINE(规范硬件参数) .... : snd_pcm_hw_refine_user(substream, arg);
                            memdup_user(获取用户空间的参数)
                            snd_pcm_hw_refine(substream, params); (把参数根据硬件修改下)先不管
                            copy_to_user(把修改的参数传回去)
SNDRV_PCM_IOCTL_HW_PARAMS : snd_pcm_hw_params_user(substream, arg);
(设置硬件参数)             memdup_user
                    snd_pcm_hw_params
                      substream->ops->hw_params(substream, params);(substream->ops就是在 soc_new_pcm函数里设置的soc_pcm_ops

                         soc_pcm_hw_params
                            依次调用dai_link(machine),codec_dai,cpu_dai,platform(dma)的hw_params函数
SNDRV_PCM_IOCTL_SYNC_PTR (同步指针)
SNDRV_PCM_IOCTL_SW_PARAMS : snd_pcm_sw_params_user(substream, arg);
(软件参数设置)             snd_pcm_sw_params 不涉及硬件操作

SNDRV_PCM_IOCTL_SYNC_PTR
SNDRV_PCM_IOCTL_PREPARE : snd_pcm_prepare(substream, file);
                  snd_power_wait // 电源管理相关,先不管
                  .... 调用到platform里的prepare

SNDRV_PCM_IOCTL_SYNC_PTR
SNDRV_PCM_IOCTL_SW_PARAMS

循环:
SNDRV_PCM_IOCTL_WRITEI_FRAMES : copy_from_user(从用户空间拿到音频相关数据)
                   snd_pcm_lib_write
                    snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_write_transfer)
                      snd_pcm_lib_write_transfer
                        copy_from_user
                        snd_pcm_start(substream); // 启动传输,依次调用dai_link(machine),codec_dai,cpu_dai,platform(dma)的trigger函数


SNDRV_PCM_IOCTL_SYNC_PTR

SNDRV_PCM_IOCTL_DRAIN
SNDRV_PCM_IOCTL_DROP
SNDRV_PCM_IOCTL_HW_FREE
close

strace分析: amixer cset numid=1 30 (设置音量)
/dev/snd/controlC0
open
SNDRV_CTL_IOCTL_CARD_INFO
SNDRV_CTL_IOCTL_PVERSION
SNDRV_CTL_IOCTL_ELEM_INFO
SNDRV_CTL_IOCTL_ELEM_READ
SNDRV_CTL_IOCTL_ELEM_WRITE : snd_ctl_elem_write_user
                  snd_ctl_elem_write
                    // 找到一个snd_kcontrol
                    kctl = snd_ctl_find_id(card, &control->id);
                    // 调用它的put
                    result = kctl->put(kctl, control);

(在内核源码uda134X.c中定义两个snd_kcontrol_new结构体,这个结构体提供了声卡芯片的属性参数信息及操作函数,然后用snd_soc_add_codec_controls函数根据这个结构体来构造snd_kcontrol)


附:
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
.name = "S3C24XX_UDA134X",
.owner = THIS_MODULE,
.dai_link = &s3c24xx_uda134x_dai_link,
.num_links = 1,
};

static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
.name = "UDA134X",
.stream_name = "UDA134X",
.codec_name = "uda134x-codec",
.codec_dai_name = "uda134x-hifi",
.cpu_dai_name = "s3c24xx-iis",
.ops = &s3c24xx_uda134x_ops,
.platform_name = "samsung-audio",
};

5. 从零写ALSA声卡驱动
5.1 写框架

见代码
5.2 实现参数设置
open: soc_pcm_open 依次调用cpu_dai, dma, codec_dai, machine的open或startup函数

只在dma的open函数里添加参数相关的代码

SNDRV_PCM_IOCTL_HW_PARAMS: soc_pcm_hw_params 依次调用machine,codec_dai,cpu_dai,platform(dma)的hw_params函数来设置参数

在uda1341.c, s3c2440-iis.c里实现hw_params函数

(s3c2440-dma.c 主要涉及数据传输(dma寄存器的设置),在下一节实现hw_params函数)

(uda1341的寄存器值支持写,不支持读,所有说如果不保存设置寄存器的时候写入的值时,根本不知道寄存器里面的值,所以需要在写入前先保存)
5.3 实现数据传输

5.4 调试

a. 修改语法错误 11th_myalsa
b. 配置内核去掉原来的声卡驱动
-> Device Drivers
-> Sound card support
-> Advanced Linux Sound Architecture
-> ALSA for SoC audio support

c. 使用新内核启动

d. 安装新驱动

insmod alsa/driver/myalsa/platform/s3c2440_iis.ko
insmod alsa/driver/myalsa/platform/s3c2440_dma.ko
insmod alsa/driver/myalsa/codec/uda1341.ko
insmod alsa/driver/myalsa/machine/s3c2440_uda1341.ko
mkdir /dev/snd
cd /dev/snd/
ln -s /dev/controlC0
ln -s /dev/pcmC0D0p
ln -s /dev/pcmC0D0c
cd /


e. aplay来测试

insmod ker_rw.ko
regeditor r32 0x4B000080 9
regeditor r32 0x55000000 5

5.5 添加音量控制

5.6 从零写WM8976声卡驱动程序
insmod alsa/driver/myalsa/platform/s3c2440_iis.ko
insmod alsa/driver/myalsa/platform/s3c2440_dma.ko
insmod alsa/driver/myalsa/codec/wm8976.ko
insmod alsa/driver/myalsa/machine/s3c2440_uda1341.ko
mkdir /dev/snd
cd /dev/snd/
ln -s /dev/controlC0
ln -s /dev/pcmC0D0p
ln -s /dev/pcmC0D0c
cd /

aplay Groove_Coverage-she.wav &

amixer controls
amixer cget numid=1
amixer cset numid=1 30

5.7 移植WM8976声卡驱动程序(源码中01th_wm8976都是在自己写的驱动中测试)
http://www.wolfsonmicro.com/products/audio-hubs-%28codecs%29/stereo-low-power-codecs/wm8976/
http://opensource.wolfsonmicro.com/content/linux-drivers-wolfson-devices
https://gitorious.org/slimlogic/linux-omap/source/36d409f2c6306cb407f5d862afe987ba245e455a:sound/soc/codecs/wm8976.c

insmod alsa/driver/myalsa/platform/s3c2440_iis.ko
insmod alsa/driver/myalsa/platform/s3c2440_dma.ko
insmod alsa/driver/myalsa/codec/wm8976.ko
insmod alsa/driver/myalsa/machine/s3c2440_uda1341.ko
mkdir /dev/snd
cd /dev/snd/
ln -s /dev/controlC0
ln -s /dev/pcmC0D0p
ln -s /dev/pcmC0D0c
cd /

aplay Groove_Coverage-she.wav &

amixer cset numid=45 30


把wm8976放入内核:
a. 把wm8976.c, wm8976.h放入soundsoccodecs目录
b. 修改soundsoccodecs的Makefile, Kconfig
c. 把soundsocsamsungs3c24xx_uda134x.c复制为s3c2440_wm8976.c
修改codec, codec_dai的name
d. 修改soundsocsamsung的Makefile, Kconfig

mkdir /dev/snd
cd /dev/snd/
ln -s /dev/controlC0
ln -s /dev/pcmC0D0p
ln -s /dev/pcmC0D0c
cd /

aplay Groove_Coverage-she.wav &
amixer cset numid=45 30


5.8 修改内核声卡BUG

1、sound/soc/samsung/dma.c中

(dma传输完成后调用audio_buffdone处理dma中已取数据的period,period是用链表串起来,之前的代码在audio_buffdone和dma_enqueue中都执行了移动当前period指针的操作,多做了一次,所有需要先删除audio_buffdone中的移动操作,把执行的从链表中删除period的操作提前,之后移动当前指针,并将已使用的period加入链表最后面,最后数据都处理好后才是snd_pcm_period_elapsed更新参数到内核)(其实这里的链表应该说成队列跟合适)

static void audio_buffdone(void *data)
{
struct snd_pcm_substream *substream = data;
struct runtime_data *prtd = substream->runtime->private_data;

pr_debug("Entered %s ", __func__);

if (prtd->state & ST_RUNNING) {
prtd->dma_pos += prtd->dma_period;//去掉
if (prtd->dma_pos >= prtd->dma_end);//去掉
prtd->dma_pos = prtd->dma_start;;//去掉

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);
}
}

static void dma_enqueue(struct snd_pcm_substream *substream)
{

  dma_info.len = prtd->dma_period*limit;改为dma_info.len = prtd->dma_period

}


5.9 编写简单的声卡应用程序
a. ALSA声卡使用体验:使用arecord录音,使用aplay播放
准备:
cd linux-3.4.2
patch -p1 < ../linux-3.4.2_alsa_wm8976_uda1341_jz2440_mini2440_tq2440.patch
cp config_wm8976_jz2440 .config 或 cp config_uda1341_tq2440_mini2440 .config
make uImage


jz2440:
i. 声音差
arecord test.wav(录音指令)
aplay test.wav

ii. 声音好
arecord -f cd test.wav
aplay test.wav

mini2440:
把MIC2通道打开(uda1341有两个录音接口,根据看电路图看麦克风链接到那个接口来设置)
amixer cset numid=11 2

i. 声音差
arecord test.wav

(录音的时候wm8976.c中的struct snd_soc_dai_driver wm8976_dai变量的capture对应的channels_min和max都是1(变量录音只能单声道),而s3c24xx-i2s.c的struct snd_soc_dai_driver s3c24xx_i2s_dai变量的capture对应的channels_min和max都是2(表示录音只能双声道),把wm8976.c中的channels_max改为2就可以了,表示两者都支持)

aplay test.wav

ii. 声音好
arecord -f cd test.wav(修改采样率和数据位数可以提高声音质量)
aplay test.wav

tq2440:
把MIC1通道打开
amixer cset numid=11 1

i. 声音差
arecord test.wav
aplay test.wav

ii. 声音好
arecord -f cd test.wav
aplay test.wav


b. 编写一个应用程序:一边录音一边播放(可以仿照alsa_util的源码,两者都在源码的aplay.c中)
A Tutorial on Using the ALSA Audio API:
alsa-lib使用方法
open_the_device();
set_the_parameters_of_the_device();
while (!done) {
/* one or both of these */
receive_audio_data_from_the_device();
deliver_audio_data_to_the_device();
}
close the device

 应用程序依赖alse-lib库,可以去查看我们之前编译的alse-lib,查看find -name "*so*",发现基本都是libasound.so,所有我们在应用程序中执行arm-linux-gcc的时候加上-lasound就可以


Alsa中PCM参数设置
http://blog.chinaunix.net/uid-10995602-id-2918643.html

mkdir /dev/snd
cd /dev/snd/
ln -s /dev/controlC0
ln -s /dev/pcmC0D0p
ln -s /dev/pcmC0D0c

修改: archarmmach-s3c24xxmach-smdk2440.c

CONFIG_SND_SOC_SAMSUNG
CONFIG_SND_S3C24XX_I2S

CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X

CONFIG_SND_SOC_UDA134X
CONFIG_S3C24XX_DMA


tar xjf alsa-lib-1.0.27.2.tar.bz2
./configure --host=arm-linux
make


ncurse:
./configure --host=arm-linux --prefix=$PWD/tmp --with-curses=ncurses
make
make install
cd tmp/lib
sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
cd tmp/include/ncurses
sudo cp * -rfd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include


tar xjf alsa-utils-1.0.27.2
./configure --host=arm-linux --prefix=$PWD/tmp --with-curses=ncurses --disable-xmlto --disable-nls
aplay /Windows.wav
ALSA lib conf.c:3707:(snd_config_update_r) Cannot access file /work/projects/alsa/alsa-lib-1.0.27.2/tmp/share/alsa/alsa.conf

./configure --host=arm-linux --with-curses=ncurses --disable-xmlto --disable-nls


make

sudo make install

configure: error: required curses helper header not found

要先编译安装ncurses-5.9
./configure --host=arm-linux --prefix=$PWD/tmp --with-shared


/bin/bash: xmlto: command not found
http://blog.csdn.net/lamdoc/article/details/12563061
sudo apt-get install xmlto // 安装失败, 配置时加上 --disable-xmlto

sudo apt-get install gettext

3.应用

soundcoresound.c
register_chrdev(major, "alsa", &snd_fops)


snd_open
struct snd_minor *mptr = NULL;
mptr = snd_minors[minor];
file->f_op = fops_get(mptr->f_ops);
err = file->f_op->open(inode, file);


用strace跟踪:
open("/dev/snd/controlC0", O_RDWR|0x80000 /* O_??? */) = 3
ioctl(3, USBDEVFS_CONTROL, 0xbe96a5bc) = 0 SNDRV_CTL_IOCTL_PVERSION
ioctl(3, 0x40045532, 0xbe96a5a8) = 0 #define _IOW('U',0x32,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE
close(3) = 0

SNDRV_CTL_IOCTL_CARD_INFO
SNDRV_CTL_IOCTL_PVERSION
SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE

alsa_pcm_init
snd_ctl_register_ioctl(snd_pcm_control_ioctl);
_snd_ctl_register_ioctl(fcn, &snd_control_ioctls);

snd_ctl_ioctl 用到 snd_control_ioctls

static const struct file_operations snd_ctl_f_ops =
.unlocked_ioctl = snd_ctl_ioctl,
}

snd_card_create
snd_ctl_create
snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops) , ops里有 snd_ctl_dev_register > snd_register_device (snd_ctl_f_ops)

snd_card_register
snd_device_register_all
dev->ops->dev_register
snd_ctl_dev_register
snd_register_device


open("/dev/snd/pcmC0D0p", O_RDWR|O_NONBLOCK|0x80000) = 4

soc_probe
snd_soc_register_card
snd_soc_instantiate_cards
snd_soc_instantiate_card
soc_probe_dai_link
soc_new_pcm

soc_pcm_ops->open = soc_pcm_open;
soc_pcm_ops->close = soc_pcm_close;
soc_pcm_ops->hw_params = soc_pcm_hw_params;
soc_pcm_ops->hw_free = soc_pcm_hw_free;
soc_pcm_ops->prepare = soc_pcm_prepare;
soc_pcm_ops->trigger = soc_pcm_trigger;
soc_pcm_ops->pointer = soc_pcm_pointer;

snd_pcm_new
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};

snd_device_new

if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);

if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);

snd_pcm_dev_register
err = snd_register_device_for_dev(devtype, pcm->card,
pcm->device,
&snd_pcm_f_ops[cidx],
pcm, str, dev);

pcm设备的 file_operations 来自 snd_pcm_f_ops

#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT 8
#define _IOC_SIZESHIFT 16
#define _IOC_DIRSHIFT 30


open("/dev/snd/pcmC0D0p", O_RDWR|O_NONBLOCK|0x80000) = 4

SNDRV_PCM_IOCTL_INFO : snd_pcm_info_user
SNDRV_PCM_IOCTL_PVERSION : put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
SNDRV_PCM_IOCTL_TTSTAMP : snd_pcm_tstamp(substream, arg), 传入的值是SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY或SNDRV_PCM_TSTAMP_TYPE_MONOTONIC
SNDRV_PCM_IOCTL_SYNC_PTR : snd_pcm_sync_ptr(substream, arg)
SNDRV_PCM_IOCTL_HW_REFINE : snd_pcm_hw_refine_user(substream, arg) > snd_pcm_hw_refine >
SNDRV_PCM_IOCTL_SYNC_PTR :
SNDRV_PCM_IOCTL_SW_PARAMS : snd_pcm_sw_params_user(substream, arg) > snd_pcm_sw_params >
SNDRV_PCM_IOCTL_SYNC_PTR
SNDRV_PCM_IOCTL_PREPARE : snd_pcm_prepare(substream, file) >
SNDRV_PCM_IOCTL_SYNC_PTR
SNDRV_PCM_IOCTL_SW_PARAMS
SNDRV_PCM_IOCTL_WRITEI_FRAMES : snd_pcm_lib_write >
SNDRV_PCM_IOCTL_SYNC_PTR
SNDRV_PCM_IOCTL_DRAIN : snd_pcm_drain
SNDRV_PCM_IOCTL_DROP : snd_pcm_drop(substream)
SNDRV_PCM_IOCTL_HW_FREE : snd_pcm_hw_free(substream)

fcntl64(4, F_SETFD, FD_CLOEXEC) = 0
ioctl(4, AGPIOC_ACQUIRE or APM_IOC_STANDBY, 0xbe9a749c) = 0 SNDRV_PCM_IOCTL_INFO
fcntl64(4, F_GETFL) = 0x80802 (flags O_RDWR|O_NONBLOCK|0x80000)
ioctl(4, AGPIOC_INFO, 0xbe9a75c0) = 0 SNDRV_PCM_IOCTL_PVERSION
ioctl(4, AGPIOC_SETUP, 0xbe9a7490) = 0 SNDRV_PCM_IOCTL_TTSTAMP
ioctl(4, 0xc0844123, 0xbe9a73b8) = 0 SNDRV_PCM_IOCTL_SYNC_PTR
fcntl64(4, F_GETFL) = 0x80802 (flags O_RDWR|O_NONBLOCK|0x80000)
fcntl64(4, F_SETFL, O_RDWR|0x80000 /* O_??? */) = 0
ioctl(4, AGPIOC_ACQUIRE or APM_IOC_STANDBY, 0xbe9a7be8) = 0 SNDRV_PCM_IOCTL_INFO
ioctl(4, 0xc25c4110, 0xbe9a7520) = 0 SNDRV_PCM_IOCTL_HW_REFINE
ioctl(4, 0xc25c4110, 0xbe9a7180) = 0 SNDRV_PCM_IOCTL_HW_REFINE
ioctl(4, 0xc25c4110, 0xbe9a7180) = 0
ioctl(4, 0xc25c4110, 0xbe9a7520) = 0
ioctl(4, 0xc25c4110, 0xbe9a7180) = 0
ioctl(4, 0xc25c4110, 0xbe9a7180) = 0
ioctl(4, 0xc25c4110, 0xbe9a7520) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a6ee0) = 0
ioctl(4, 0xc25c4110, 0xbe9a7280) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a69c8) = 0
ioctl(4, 0xc25c4110, 0xbe9a69c8) = 0
ioctl(4, 0xc25c4110, 0xbe9a69c8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a69c8) = 0
ioctl(4, 0xc25c4110, 0xbe9a69c8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6d68) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a6fd8) = 0
ioctl(4, 0xc25c4110, 0xbe9a7470) = 0
ioctl(4, 0xc25c4110, 0xbe9a7470) = 0
ioctl(4, 0xc25c4110, 0xbe9a7470) = 0
ioctl(4, 0xc25c4110, 0xbe9a7470) = 0
ioctl(4, 0xc25c4110, 0xbe9a7418) = 0
ioctl(4, 0xc25c4110, 0xbe9a7418) = 0
ioctl(4, 0xc25c4110, 0xbe9a7418) = 0
ioctl(4, 0xc25c4110, 0xbe9a7418) = 0
ioctl(4, 0xc25c4110, 0xbe9a7418) = 0
ioctl(4, 0xc25c4110, 0xbe9a7490) = 0
ioctl(4, 0xc25c4110, 0xbe9a7490) = 0
ioctl(4, 0xc25c4110, 0xbe9a7490) = 0
ioctl(4, 0xc25c4110, 0xbe9a7860) = 0
ioctl(4, 0xc25c4111, 0xbe9a7860) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0 SNDRV_PCM_IOCTL_SYNC_PTR
ioctl(4, 0xc0684113, 0xbe9a7404) = 0 SNDRV_PCM_IOCTL_SW_PARAMS
ioctl(4, 0xc0844123, 0x2e868) = 0 SNDRV_PCM_IOCTL_SYNC_PTR
ioctl(4, 0x4140, 0xc54f8) = 0 SNDRV_PCM_IOCTL_PREPARE
ioctl(4, 0xc0844123, 0x2e868) = 0 SNDRV_PCM_IOCTL_SYNC_PTR
ioctl(4, 0xc0684113, 0xbe9a77f0) = 0 SNDRV_PCM_IOCTL_SW_PARAMS
read(3, "^36337753743773375377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0 SNDRV_PCM_IOCTL_WRITEI_FRAMES
ioctl(4, 0xc0844123, 0x2e868) = 0 SNDRV_PCM_IOCTL_SYNC_PTR
read(3, "2223,2071%1$377261133037424615373377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, ",5246374(52743753174365376304N00133231"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "Z 2146345 3536265 26736 ,7+ 217310354"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "262 20 s Jf370 ;vL 25 F1035010&72367"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "30217 22203v305205731216211217f1376313"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "Z 4172fh16356 x 36610Efl6314 .334010224"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "^30310371273 3611245 )4uv\630f2637"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "36236533127_36727327g36737326323366.25302"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "30525N 20222%fS2022316q16C20Ov35520/7342"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "&3)53745y2?10363377w10303374X10276371273"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "l37423373g37016371345363(367375356.365{351"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "O4G6R6-076 2227371v23410221 K 363 ]v"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "237320d327*33231733332534234434122735221351"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "214337*342i343260346276346B352n350 355j353"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "3403263543569324b35724324305356270321e355"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "35423351M21M3320 234523522677224372m "..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "3776`362257322436735361374;3733112365"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "Q20342323037t537,]6311/U040136012410m1w"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "353371f370c373252366336374[364316373317361"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "305363n365i371216365*1e367% 1037235017 376"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "C372003536336|32*34:25213032517626235 "..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "266163052177f:2Av3111Vv37724v1775207"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "C330m330331262377376335224376234345324374"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "34433327.374 2721737025424636726121207"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "2141725137332627B375>3532737620237311262"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\f357374213 2443752546I376x5224376*5204"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "2343T363S2177366+253371/37653732033748373"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "26762333772126>133352171 5172%5k4274"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "37310222351`7 35027455347O040346%3213345"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "241373336372317373e372r374225371"375231370"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, " 367301370]366m370315365W370204365243370236"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "e3227376f326037624533243763453353376 4347"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "11037630237722376o377+3763770376205376"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "334372317376305372>3772713722503773013726"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "2253770423437727425437734232513772353220"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "316v345364377366326377373255377371"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "30713501336124713461g13451!13331321"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "267377341270377G1301377p13203772161344377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "351377 2D33e+203009234C262"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "240R377232L377232O377237R377244Y377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "257377)276377243173777344377377377367"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "21037717215377 2273772225037725267"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "16x6xy373377v365377r361377r"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "162f5 7 f1621162420"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "356377 3633772336537736370377$373377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "337377337377334377341377332377344377330377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, ""353377 351377373523773535037731"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "370377370377370377377377371377376377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, " 171 ff"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "71610165177 6f6 5"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0 SNDRV_PCM_IOCTL_WRITEI_FRAMES
ioctl(4, 0xc0844123, 0x2e868) = 0 SNDRV_PCM_IOCTL_SYNC_PTR
read(3, ""..., 6808) = 6808
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0 SNDRV_PCM_IOCTL_WRITEI_FRAMES
ioctl(4, 0xc0844123, 0x2e868) = 0 SNDRV_PCM_IOCTL_SYNC_PTR
fcntl64(4, F_GETFL) = 0x80002 (flags O_RDWR|0x80000)
fcntl64(4, F_SETFL, O_RDWR|0x80000 /* O_??? */) = 0
ioctl(4, 0x4144, 0xc50a4) = 0 SNDRV_PCM_IOCTL_DRAIN
fcntl64(4, F_GETFL) = 0x80002 (flags O_RDWR|0x80000)
fcntl64(4, F_SETFL, O_RDWR|0x80000 /* O_??? */) = 0
close(3) = 0
ioctl(4, 0x4143, 0xc51ac) = 0 SNDRV_PCM_IOCTL_DROP
ioctl(4, 0x4112, 0) = 0 SNDRV_PCM_IOCTL_HW_FREE
close(4) = 0

insmod regmap-i2c.ko
insmod regmap-spi.ko
insmod snd-soc-core.ko

echo "8 4 1 7" > /proc/sys/kernel/printk
insmod alsa/driver/platform/s3c2440-i2s.ko
insmod alsa/driver/platform/s3c2440-dma.ko
insmod alsa/driver/codec/uda1341.ko
insmod alsa/driver/machine/s3c2440_sound.ko
mkdir /dev/snd
cd /dev/snd/
ln -s /dev/controlC0
ln -s /dev/pcmC0D0p
ln -s /dev/pcmC0D0c
cd /
echo "8 4 1 7" > /proc/sys/kernel/printk

regeditor r32 0x55000000 5; regeditor r32 0x4B000080 9

修改BUG:soundsocsamsungdma.c

static void dma_enqueue(struct snd_pcm_substream *substream)

//pos += prtd->dma_period;
pos += prtd->dma_period*limit;

http://wenku.baidu.com/link?url=B2U2f2vK5gHvVVUrMrgmEaS3N0xAampnfPj5-MNfJsF63BwL3qU7bNOhf0Jmwrqx75y5LSk8C13ppaHvfw24YOjtf6GppFOI25INP7WVKOO

A close look at ALSA
http://www.volkerschatz.com/noise/alsa.html


Linux音频子系统 - DroidPhone的专栏 - 博客频道 - CSDN.NET.htm
http://blog.csdn.net/droidphone/article/category/1118446


Linux ALSA声卡驱动之一:ALSA架构简介
http://blog.csdn.net/droidphone/article/details/6271122

Linux ALSA声卡驱动之二:声卡的创建
http://blog.csdn.net/droidphone/article/details/6289712

Linux ALSA声卡驱动之三:PCM设备的创建
http://blog.csdn.net/droidphone/article/details/6308006

Linux ALSA声卡驱动之四:Control设备的创建
http://blog.csdn.net/droidphone/article/details/6409983

Linux ALSA声卡驱动之五:移动设备中的ALSA(ASoC)
http://blog.csdn.net/droidphone/article/details/7165482

Linux ALSA声卡驱动之六:ASoC架构中的Machine
http://blog.csdn.net/droidphone/article/details/7231605

Linux ALSA声卡驱动之七:ASoC架构中的Codec
http://blog.csdn.net/droidphone/article/details/7283833

Linux ALSA声卡驱动之八:ASoC架构中的Platform
http://blog.csdn.net/droidphone/article/details/7316061

snd_soc_register_codec(,snd_soc_codec_driver,snd_soc_dai_driver,num_dai)

alsa:

 

alsa分析:

 

alsa驱动:

 

从零写alsa驱动:

 

原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/8933272.html