Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92

这部分内容比较繁琐,,先从初始化开始了解里面参数意义,以period size

这个参数为线索来追终代码流程.

pcm_native.c
snd_pcm_hw_constraints_init():

int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
{struct snd_pcm_runtime *runtime = substream->runtime;//ASOC对应hw params有参数大小限制,一定要在它的范围内,否则设置无效.//相关信息都保存在hw_constraints中.struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;int k, err;
......//每个hw参数规则都会被添加进去来作追踪.err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,snd_pcm_hw_rule_mulkdiv, (void*) 8,SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
......
}
struct snd_pcm_hw_constraints {//mask用来表示后面的intervals是否能访问,没有设置是不行的.struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK -SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];//保存所有hw param值.struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL -SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];//目前已有规则总数unsigned int rules_num;//存规则势必要分配空间,当rules_num达到rules_all的时候,//会每次分配16个存储空间,而不是一次分配一个,这样可以提高效率,//因此rules_all总是>=rules_num.unsigned int rules_all;//所有规则用此结构表示.struct snd_pcm_hw_rule *rules;
};

snd_pcm_hw_rule_add():

int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,int var,snd_pcm_hw_rule_func_t func, void *private,int dep, ...)
{struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;struct snd_pcm_hw_rule *c;
......//每添加一个interval,constrs->rules_num就会随着增加,//当超过constrs->rules_all,就需要分配新空间用于存储interval.if (constrs->rules_num >= constrs->rules_all) {struct snd_pcm_hw_rule *new;//基于效率,每次直接分配16个.unsigned int new_rules = constrs->rules_all + 16;//分配的结构是struct snd_pcm_hw_rulenew = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
......}//填充rules,下面的值会以snd_pcm_hw_constraints_init()调用中的实际值为例.c = &constrs->rules[constrs->rules_num];//为0c->cond = cond;//后面会回调snd_pcm_hw_rule_mulkdiv()来计算period size.c->func = func;//SNDRV_PCM_HW_PARAM_PERIOD_SIZEc->var = var;//为8,后面计算period size会当系数用.c->private = private;k = 0;while (1) {
......//SNDRV_PCM_HW_PARAM_PERIOD_BYTES和SNDRV_PCM_HW_PARAM_FRAME_BITS//存到deps数组中,后面通过这两个来重计算period size.c->deps[k++] = dep;
.....constrs->rules_num++;
......
}

有些参数如period count, channel等,一开始它的值会根据硬件限制定好大范围了,如rk_pcm.c

static const struct snd_pcm_hardware rockchip_pcm_hardware = {.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,.formats        =   SNDRV_PCM_FMTBIT_S24_LE |SNDRV_PCM_FMTBIT_S20_3LE |SNDRV_PCM_FMTBIT_S16_LE,//最少双通道,最大八通道..channels_min        = 2,.channels_max        = 8,.buffer_bytes_max    = 2*1024*1024,/*128*1024,*/.period_bytes_min    = 64,.period_bytes_max    = 512*1024,/*32*1024,//2048*4,///PAGE_SIZE*2,*/.periods_min        = 3,.periods_max        = 128,.fifo_size        = 16,
};

然后会被保存到hw interval中:

int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
{
......err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,hw->rate_min, hw->rate_max);
......
}
int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,unsigned int min, unsigned int max)
{struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;struct snd_interval t;t.min = min;t.max = max;t.openmin = t.openmax = 0;t.integer = 0;//新的配置更新到旧的interval中.return snd_interval_refine(constrs_interval(constrs, var), &t);
}

这样默认就有个范围值了,这样,当用户空间设置时,就不能超过这个硬件设置范围,否则就会出错了!

用户空间对period size的配置:

struct pcm_config pcm_config_in = {.channels = 2,.rate = 44100,.period_size = 16,.period_count = 32,.format = PCM_FORMAT_S16_LE,
};

然后会被传到pcm_open()中

struct pcm *pcm_open(unsigned int card, unsigned int device,unsigned int flags, struct pcm_config *config)
{
......//config->period_size被传到了这里.//函数用来设置period size的最小值//然后会通过下面的ioctl传给kernel看是否超出范围了.param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
......if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {oops(pcm, errno, "cannot set hw params");goto fail_close;}//返回设置可以设置的值.config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
......
}

param_set_min():

static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
{//判断当前SNDRV_PCM_HW_PARAM_PERIOD_SIZE是否在interval列表范围内,肯定成立.if (param_is_interval(n)) {//取出对应的intervalstruct snd_interval *i = param_to_interval(p, n);//赋值最大最小都是config->period_sizei->min = val;i->max = val;i->integer = 1;}
}

看ioctl进入kernel
ioctl ->
    snd_pcm_common_ioctl1 ->
        snd_pcm_hw_params_user ->
            snd_pcm_hw_params ->
                snd_pcm_hw_refine    //参数处理都在这里完成
snd_pcm_hw_refine():

int snd_pcm_hw_refine(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
{unsigned int k;struct snd_pcm_hardware *hw;struct snd_interval *i = NULL;struct snd_mask *m = NULL;struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints;unsigned int rstamps[constrs->rules_num];unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
......//先对mask做处理,这里不太明白mask和后面提到的interval有什么联系?for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {//取出对应的maskm = hw_param_mask(params, k);//每个mask使用了两个字节总共64位//如果一个都没设置,那就不处理参数了,当前正常流程不会发生.if (snd_mask_empty(m))return -EINVAL;//rmask用于存每个interval对应的位,如果没设置就不能往下走.if (!(params->rmask & (1 << k)))continue;//调试信息.
#ifdef RULES_DEBUGprintk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
#endif//更新旧的mask信息changed = snd_mask_refine(m, constrs_mask(constrs, k));
#ifdef RULES_DEBUGprintk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
#endif//如果前后有变化,则标记.if (changed)params->cmask |= 1 << k;if (changed < 0)return changed;}//依次处理每个intervalfor (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {//取出i = hw_param_interval(params, k);
......//替换旧的intervalchanged = snd_interval_refine(i, constrs_interval(constrs, k));
......}
......do {again = 0;for (k = 0; k < constrs->rules_num; k++) {struct snd_pcm_hw_rule *r = &constrs->rules[k];
......//执行之前注册的回调函数,这里是snd_pcm_hw_rule_mulkdivchanged = r->func(params, r);
......}} while (again);
......
}

snd_pcm_hw_rule_mulkdiv():

static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params,struct snd_pcm_hw_rule *rule)
{struct snd_interval t;//参数一对应SNDRV_PCM_HW_PARAM_PERIOD_BYTES的配置//参数二的是值是8//参数三对应SNDRV_PCM_HW_PARAM_FRAME_BITS的配置snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]),(unsigned long) rule->private,hw_param_interval_c(params, rule->deps[1]), &t);//更新配置return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
SNDRV_PCM_HW_PARAM_FRAME_BITS()的设置在snd_pcm_hw_constraints_init()中
snd_pcm_hw_constraints_init() {
......snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));
......
}

而具体的值是在HAL层设置的:

struct pcm *pcm_open(unsigned int card, unsigned int device,unsigned int flags, struct pcm_config *config)
{
......//对应pcm_config_in的配置,为16*2 = 32.param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS,pcm_format_to_bits(config->format) * config->channels);
......
}

而SNDRV_PCM_HW_PARAM_PERIOD_BYTES的值是在

int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
{
......//也就是rockchip_pcm_hardware变量中的值.err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,hw->period_bytes_min, hw->period_bytes_max);
......
}

snd_interval_mulkdiv():

void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,const struct snd_interval *b, struct snd_interval *c)
{
......    //c->min = a->min * k / b->max;c->min = muldiv32(a->min, k, b->max, &r);c->openmin = (r || a->openmin || b->openmax);if (b->min > 0) {c->max = muldiv32(a->max, k, b->min, &r);if (r) {c->max++;c->openmax = 1;} elsec->openmax = (a->openmax || b->openmin);} else {c->max = UINT_MAX;c->openmax = 0;}c->integer = 0;
}

参考:
http://blog.csdn.net/u012769691/article/details/46727543
http://blog.chinaunix.net/uid-20564848-id-74213.html
http://blog.csdn.net/crycheng/article/details/7095899

[RK3288][Android6.0] Audio中的HW Params设置流程相关推荐

  1. 声道切换 android,[RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 播放音乐是单声道,硬件用的是双声道. AudioFlinger::PlaybackThread:: ...

  2. [RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 播放音乐是单声道,硬件用的是双声道. AudioFlinger::PlaybackThread:: ...

  3. [RK3288][Android6.0] Audio录音frame rate设置流程小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 HAL: 默认的配置在 hardware/rockchip/audio/tinyalsa_hal/ ...

  4. [RK3288][Android6.0] Skia中的编解码小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 编解码器注册: SKIA的编解码部分通过一个模板类来实现,这样做的为了实现不同类型的codec兼容 ...

  5. [RK3288][Android6.0] Audio的音量设置流程小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 说明一: AudioManager提供了两个调节音量接口 adjustSuggestedStrea ...

  6. android6.0 wifi流程,[RK3288][Android6.0] WiFi之从Linkspeed看获取流程

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 WiFi的(Link speed)连接速度可以从Settings里查看 从连接速度来看下获取WiF ...

  7. [RK3288][Android6.0] 设置中通过Sensor旋转显示画面小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 Settings -> Display有个选项控制旋转屏幕时内容是否跟着旋转 这个功能是通过 ...

  8. Android系统(127)---Android6.0存储中加入总内存和系统内存项和在西语下把,换成.

    Android6.0存储中加入总内存和系统内存项 阅读数:651 平台下patches/packages/apps/Settings/里面 1.存储中加入总内存和系统内存项 在 res/values- ...

  9. [RK3288][Android6.0] 调试笔记 --- WiFi芯片AP6356S添加

    Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 背景: RK3288 Android6.0平台对AP6356S的支持不是很完善,直接修改wifi ch ...

  10. [RK3288][Android6.0] 调试笔记 --- touch无法获取坐标点

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 有网友遇到调试touch的时候能触发中断,但无法获取坐标点 具体可参考文章 [RK3288][An ...

最新文章

  1. 如何 SQL Server 2005 实例之间传输登录和密码
  2. 通过Webservice查询手机号码归属地
  3. 2019北京高考分数分布一览表(成绩分布统计)
  4. 【快乐水题】747. 至少是其他数字两倍的最大数
  5. C++字符串类型和数字之间的转换
  6. [html] 在主框架下引入的iframe,如果检测这个iframe是否能打开,如果打不开则跳到404页面
  7. Xamarin:安卓通过“第三发应用打开”实现文件跨应用传输
  8. Java 并发:第三部分 - 同步锁
  9. android 横盘方向传感器,横盘震荡选择方向!
  10. 什么是软件著作权,怎么申请软件著作权
  11. ISO27001认证适用领域及认证流程
  12. VGG-16网络结构详解
  13. CTA策略01_dualThrust
  14. 计算机转换外界信息原理,高级文秘及办公自动化教程-计算机基础
  15. [****ViewController scrollViewDidScroll:]: message sent to deallocated instance 0x12d6c22f0
  16. php获取小米手环数据,小米运动App数据提取
  17. kso经验积累 -- c#发送邮件
  18. android socket通讯
  19. 互联网与达尔文进化论的结合
  20. 解决Android 模拟机开机黑屏问题、npm内存溢出问题

热门文章

  1. 【信息系统项目管理师】干系人管理
  2. 标题 多媒体计算机的标准是什么,多媒体技术习题答案
  3. java将Word转换成PDF
  4. Linux led子系统分析之三 led设备驱动与ledtrigger驱动实现
  5. mini2440 LED设备驱动开发源代码(宋宝华框架)
  6. zynq之ps端开发
  7. PR导入视频失败、没有音频解决方法
  8. 推荐玩游戏的计算机及型号,想买个3000左右的台式电脑,主要玩游戏,求推荐品牌还有型号。...
  9. 两年数据对比柱形图_对比数据还在用千篇一律的柱形图?试试wifi对比图,让人过目不忘...
  10. centOS7搭建DNS服务器配置详解