(注意;内核上电的时候会把一些没运行的控制器模块的时钟都关掉,所有在写驱动的时候需要在使用的使用使用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: sound\soc\samsung\dma.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放入sound\soc\codecs目录
b. 修改sound\soc\codecs的Makefile, Kconfig
c. 把sound\soc\samsung\s3c24xx_uda134x.c复制为s3c2440_wm8976.c
修改codec, codec_dai的name
d. 修改sound\soc\samsung的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\n", __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

修改: arch\arm\mach-s3c24xx\mach-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.应用

sound\core\sound.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, "^\0\0\0\363\377\0\0\5\0\0\0\374\377\0\0\3\0\0\0\375\377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0 SNDRV_PCM_IOCTL_WRITEI_FRAMES
ioctl(4, 0xc0844123, 0x2e868) = 0 SNDRV_PCM_IOCTL_SYNC_PTR
read(3, "\222\3,\0\207\1%\1$\377\261\1\330\374\246\1\5\373\377\0"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, ",\5\246\374(\5\274\375\317\4\365\376\30\4N\0001\3\323\1"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "Z\n\214\6\345\n\353\6\265\n\26\7\36\n,\7+\t\21\7\3\10\354"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\262\n\20\rs\nJ\f\370\t;\vL\t\25\nF\10\350\10&\7\236\7"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\302\17\n\22\203\v\305\20\5\7\312\16\211\2\17\f1\376\313"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "Z\r4\0172\fh\16\356\nx\r\366\10E\fl\6\314\n.\3\340\10\224"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "^\0\303\10\371\0\273\t\361\1\245\n)\4u\v\\\6\30\f\263\7"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\362\365\331\27_\367\273\27g\367\373\26\323\366.\25\302"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\305\25N\t\202\22%\fS\20\223\16q\16C\20O\v\355\20/\7\342"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "&\3)\5\374\5y\2?\10\363\377w\10\303\374X\10\276\371\273"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "l\374\23\373g\370\16\371\345\363(\367\375\356.\365{\351"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "O\4G\6R\6-\0076\t\222\7\371\v\234\10\221\rK\n\363\r]\v"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\237\320d\327*\332\317\333\325\342\344\341\227\352\21\351"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\214\337*\342i\343\260\346\276\346B\352n\350\n\355j\353"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\340\326\354\3569\324b\357\24\324\305\356\270\321e\355"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\354\23\351\0M\21M\3\320\n\234\5\235\2\267\7\224\372m\t"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\377\6`\362\257\3\224\367\35\0\361\374;\373\31\1\2\365"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "Q\20\342\3\230\37t\5\37,]\6\311/U\00401\360\1\2410m\1w"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\353\371\f\370c\373\252\366\336\374[\364\316\373\317\361"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\305\363n\365i\371\216\365*\1e\367%\t\10\372\350\17\t\376"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "C\37\200\35\363\36|\32*\34:\25\21\30\325\17\6\26\235\n"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\266\16\305\2\177\f:\2A\v\311\1V\v\377\2\4\v\177\5\207"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "C\330m\0\330\331\262\377\376\335\224\376\234\345\324\374"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\344\0\333\27.\374\n\27\217\370\254\0246\367\261\21\207"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\214\17\251\373\326\27B\375>\35\327\376\202\37\311\0\262"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\\\f\357\374\213\t\244\375\254\6I\376x\5\224\376*\5\204"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\234\3T\363S\2\177\366+\0\253\371/\3765\373\203\3748\373"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\267\6\233\377\212\6>\1\333\5\217\1\n\5\17\2%\5k\4\274"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\373\10\222\351`\7\r\350\274\5\5\347O\0040\346%\3\213\345"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\241\373\336\372\317\373e\372r\374\225\371\"\375\231\370"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\t\367\301\370]\366m\370\315\365W\370\204\365\243\370\236"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "e\3\227\376f\3\260\376\245\3\324\376\345\3\353\376 \4\347"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\1\0\10\376\302\377\22\376o\377+\376\0\3770\376\205\376"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\334\372\317\376\305\372>\377\271\372\250\377\301\372\6"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\225\3770\4\234\377\27\4\254\377\342\3\251\377\235\3\220"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\316\0\v\0\345\0\364\377\366\0\326\377\373\0\255\377\371"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\307\1\350\1\336\1\247\1\346\1g\1\345\1!\1\333\1\321\0"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\267\377\34\1\270\377G\1\301\377p\1\320\377\216\1\344\377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\351\377 \0\2\0D\0\33\0e\0+\0\203\0009\0\234\0C\0\262\0"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\240\0R\377\232\0L\377\232\0O\377\237\0R\377\244\0Y\377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\257\377)\0\276\377\24\0\317\377\7\0\344\377\377\377\367"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\210\377\17\0\215\377\r\0\227\377\22\0\250\377\25\0\267"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\16\0x\0\6\0x\0\0\0y\0\373\377v\0\365\377r\0\361\377r\0"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\16\0\2\0\f\0\5\0\r\0\7\0\r\0\f\0\16\0\21\0\16\0\24\0\20"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\356\377\n\0\363\377\23\0\365\377\36\0\370\377$\0\373\377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\337\377\337\377\334\377\341\377\332\377\344\377\330\377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\"\0\353\377 \0\351\377\37\0\352\377\35\0\350\377\31\0"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\370\377\0\0\370\377\0\0\370\377\377\377\371\377\376\377"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\0\0\r\0\0\0\17\0\1\0\r\0\0\0\r\0\0\0\r\0\0\0\f\0\0\0\f"..., 8192) = 8192
ioctl(4, 0x400c4150, 0xbe9a7ab8) = 0
ioctl(4, 0xc0844123, 0x2e868) = 0
read(3, "\7\0\16\0\10\0\16\0\5\0\17\0\7\0\r\0\6\0\f\0\6\0\n\0\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, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 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:sound\soc\samsung\dma.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驱动:

36、ALSA声卡驱动和应用的更多相关文章

  1. Linux&nbsp;ALSA声卡驱动之一:ALS…

    声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢! 一.  概述 ALSA是Advanced Linux Sound Architecture ...

  2. ALSA声卡驱动中的DAPM详解之七:dapm事件机制(dapm event)

    前面的六篇文章,我们已经讨论了dapm关于动态电源管理的有关知识,包括widget的创建和初始化,widget之间的连接以及widget的上下电顺序等等.本章我们准备讨论dapm框架中的另一个机制:事 ...

  3. ALSA声卡驱动中的DAPM详解之六:精髓所在,牵一发而动全身

    设计dapm的主要目的之一,就是希望声卡上的各种部件的电源按需分配,需要的就上电,不需要的就下电,使得整个音频系统总是处于最小的耗电状态,最主要的就是,这一切对用户空间的应用程序是透明的,也就是说,用 ...

  4. ALSA声卡驱动中的DAPM详解之五:建立widget之间的连接关系

    前面我们主要着重于codec.platform.machine驱动程序中如何使用和建立dapm所需要的widget,route,这些是音频驱动开发人员必须要了解的内容,经过前几章的介绍,我们应该知道如 ...

  5. ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route

    前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path.之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开 ...

  6. ALSA声卡驱动中的DAPM详解之三:如何定义各种widget

    上一节中,介绍了DAPM框架中几个重要的数据结构:snd_soc_dapm_widget,snd_soc_dapm_path,snd_soc_dapm_route.其中snd_soc_dapm_pat ...

  7. ALSA声卡驱动中的DAPM详解之一:kcontrol

    DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态 ...

  8. Linux ALSA声卡驱动之八:ASoC架构中的Platform

    1.  Platform驱动在ASoC中的作用 前面几章内容已经说过,ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过C ...

  9. Linux ALSA声卡驱动之二:声卡的创建

    1. struct snd_card 1.1. snd_card是什么 snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都 ...

随机推荐

  1. logback 生成日志

    <?xml version="1.0" encoding="UTF-8"?> <configuration> <appender ...

  2. 转:Java的一道面试题----静态变量初始化过程

    public class Test{ private static Test tester = new Test(); //step 1 private static int count1; //st ...

  3. linux设置tab键的宽度为4

    先cd 到~目录 ~$ cd ~$ vi .vimrc set nu

  4. 16.REPL 命令

    转自:http://www.runoob.com/nodejs/nodejs-tutorial.html ctrl + c - 退出当前终端. ctrl + c 按下两次 - 退出 Node REPL ...

  5. 使用SqlBulkCopy进行批量数据插入

    Dim dt As DataTable = New DataTable() dt.Columns.Add("DtCostProductRuleGUID", GetType(Guid ...

  6. Elasticsearch之插件扩展

    Elasticsearch之插件介绍及安装 Elasticsearch之head插件安装之后的浏览详解 Elasticsearch之kopf插件安装之后的浏览详解 Elasticsearch-2.4. ...

  7. Maven学习笔记5

    Web项目的部署: web部署 配置步骤 生成项目方式不是quickstart,而是webapp. 默认目录结构,需要修改配置. 重新配置project facets和java compiler.并重 ...

  8. bzoj3307雨天的尾巴(权值线段树合并/DSU on tree)

    题目大意: 一颗树,想要在树链上添加同一物品,问最后每个点上哪个物品最多. 解题思路: 1.线段树合并 假如说物品数量少到可以暴力添加,且树点极少,我们怎么做. 首先在一个树节点上标记出哪些物品有多少 ...

  9. 一句话解决Ping问题(主机,开发板,虚拟机)

    PC机使用网卡A连接开发板,VMWare就要使用同一个网卡A作为桥接网卡 步骤: 1.确定网卡A 2.VMWare选择网卡A作为桥接网卡 3.设置三者IP在同一网段 a.Windows网卡A的IP b ...

  10. MySQL和SqlServer的区别

    一.查看表结构数量等mysql语句: -- 查看系统内所有数据库 show databases: -- 查询数据库内所有表 show tables; -- 显示表结构 desc 表名; sql ser ...