天天看点

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

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

继续阅读