[RK3288][Android6.0] Audio中的HW Params设置流程

来源:互联网 发布:js移除onclick 编辑:程序博客网 时间:2024/06/06 16:39
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_rule        new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);......    }    //填充rules,下面的值会以snd_pcm_hw_constraints_init()调用中的实际值为例.    c = &constrs->rules[constrs->rules_num];    //为0    c->cond = cond;    //后面会回调snd_pcm_hw_rule_mulkdiv()来计算period size.    c->func = func;    //SNDRV_PCM_HW_PARAM_PERIOD_SIZE    c->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)) {        //取出对应的interval        struct snd_interval *i = param_to_interval(p, n);        //赋值最大最小都是config->period_size        i->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++) {        //取出对应的mask        m = hw_param_mask(params, k);        //每个mask使用了两个字节总共64位        //如果一个都没设置,那就不处理参数了,当前正常流程不会发生.        if (snd_mask_empty(m))            return -EINVAL;        //rmask用于存每个interval对应的位,如果没设置就不能往下走.        if (!(params->rmask & (1 << k)))            continue;        //调试信息.#ifdef RULES_DEBUG        printk(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_DEBUG        printk("%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;    }    //依次处理每个interval    for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {        //取出        i = hw_param_interval(params, k);......        //替换旧的interval        changed = 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_mulkdiv            changed = 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;        } else            c->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

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 天猫买东西地址写错了怎么办 天猫上买衣服收货电话写错了怎么办 电视遥控器不小心按了高清键怎么办 康佳电视打开右下角是红色的怎么办 天猫超市退款成功后收到货怎么办 天猫还没收到货就确认收货了怎么办 支付宝红包抵扣被关了怎么办 水貂绒大衣白色过色了怎么办 双面羊绒大衣袖子洗短了怎么办 特殊类型订单销量评价删除后怎么办 淘宝上卖家发货发错了地址怎么办 买完保险想换保险代理人怎么办 怀孕内裤两边磨的好疼怎么办 露肩连体裤穿着卡裆怎么办 魅族手机恋与制作人换诺基亚怎么办 蚊子叮咬后擦风油精了红肿怎么办 机动车已转让没过户出了事情怎么办 签好的合同如果甲方违约怎么办? 饭店没签合同辞职不给工资怎么办 两家为了带孩子闹翻了怎么办啊? 抵押后租赁的房屋被法院拍卖怎么办 房子买20年了没有过户怎么办 二手车没过户行驶证丢了怎么办 买的二手车行驶证丢了怎么办 在京东仓库做事把东西损坏了怎么办 微信显示该账号登陆环境异常怎么办 干洗店把衣服洗坏了怎么办 千牛上怎么改淘宝密码忘记了怎么办 公司收了代理商押金到期不退怎么办 淘宝显示签收但是我没收到货怎么办 淘宝东西没收到显示签收了怎么办 开拼多多店铺的密码忘了怎么办 拼多多密码跟店铺名忘了怎么办 闲鱼上卖出的宝贝被调包了怎么办 上传身份证照片说格式错误该怎么办 我给厂里打的款不给发货怎么办 净值接近不定期份额折算阀值怎么办 有锁电信4g掉了怎么办 在电脑中找不到想作废的发票怎么办 科目三补考费发票丢了怎么办 母婴店飞鹤奶粉突然厂家撤货怎么办