ALSA子系统(十三)------snd_pcm_hw_refine硬件参数重定义

你好!这里是风筝的博客,
欢迎和我一起交流。

hw parameter表示当前音频流使用的硬件参数,比如当前音频流使用的format,声道数,使用的哪个音频输出端口等等。
因为一个codec或者一个dai可以支持多种格式,比如一个dai可以传16bit pcm、24bit pcm,那么现在究竟是使用的哪一种格式呢,这个问题就由上层应用来指定。
比如说上层应用需要播放一个16bit的pcm,那么就会打开一个pcm逻辑设备,并通过ioctrl来通知音频驱动当前播放的音频流的参数,音频驱动根据这些配置来进行对应的操作,不过硬件到底支不支持16bit,需要进行硬件参数重新规范,会调用到snd_pcm_hw_refine函数。

struct snd_mask {
        __u32 bits[(SNDRV_MASK_MAX+31)/32];
};

struct snd_interval {
		unsigned int min, max;
		//openmin和openmax代表开集:
		//openmin和openmax都为1时,代表开区间,range范围为(min,max)
		//openmin=1,openmax=0时,range范围为(min,max] 即开区间和闭区间
		unsigned int openmin:1,
					 openmax:1,
					 //integer等于1,表示it不是一个范围区间,而是一个固定的interger整型值
					 integer:1,
					 empty:1;
}

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];
        unsigned int stamp = 2;
        int changed, again;

        params->info = 0;
        params->fifo_size = 0;
        //requested masks
        if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
                params->msbits = 0;
        if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) {
                params->rate_num = 0;
                params->rate_den = 0;
        }
		//分别是access type、format、subformat
        for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
        		//用mask来表示,mask[]数组下标代表类型,以k=1为例,mask[1]就是format,mask[]->bits[]代表支持当格式
        		//mask[]->bits[]数组中每个元素的bits代表一个格式
                m = hw_param_mask(params, k);
                if (snd_mask_empty(m))
                        return -EINVAL;
                if (!(params->rmask & (1 << k)))
                        continue;
                //mask重定义,mask取自上层params,初始时mask[]->bits[]是0xff,这里需要与硬件判断支持哪些参数
                //mask->bits[i] &= constrs->bits[i];
                //constrs->bits[]也就是硬件支持的参数,只有constrs支持的相应bit才能设置有效
                changed = snd_mask_refine(m, constrs_mask(constrs, k));
                //changed masks,如果成功重定义了参数,cmask记录下是哪个参数被改变
                if (changed)
                        params->cmask |= 1 << k;
                if (changed < 0)
                        return changed;
        }
		//分别是bits per sample、bits per frams、channels、rate、period size等
        for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
        		//不同于format等,像rate是一个范围值,不能用每个bits表示支持的参数,所以用intervals
        		//intervals->min ~ intervals->max 代表范围
                i = hw_param_interval(params, k);
                if (snd_interval_empty(i))
                        return -EINVAL;
                if (!(params->rmask & (1 << k)))
                        continue;
                //interval重定义,interval取自上层params,这里和constrs对比,也就是和硬件hw参数对比
                //取interval、constrs中最大的min,最小的max进行赋值
                changed = snd_interval_refine(i, constrs_interval(constrs, k));
                //如果成功重定义了参数,cmask记录下是哪个参数被改变
                if (changed)
                        params->cmask |= 1 << k;
                if (changed < 0)
                        return changed;
        }

        for (k = 0; k < constrs->rules_num; k++)
                rstamps[k] = 0;
        //快速建立有变动的数组表
        for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
                vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0;
        do {
                again = 0;
                //rule规则检查遍历,硬件参数hw约束规则rule
                for (k = 0; k < constrs->rules_num; k++) {
                        struct snd_pcm_hw_rule *r = &constrs->rules[k];
                        unsigned int d;
                        int doit = 0;
                        //rule的约束cond,上层在配置param时设置同样的flags,可以跳过这条规则
                        if (r->cond && !(r->cond & params->flags))
                                continue;
                        //dependence,依赖
                        for (d = 0; r->deps[d] >= 0; d++) {
                                if (vstamps[r->deps[d]] > rstamps[k]) {
                                        doit = 1;
                                        break;
                                }
                        }
                        if (!doit)
                                continue;
                        //规则检查,对params施加约束,snd_pcm_hw_constraints_init函数里添加了一些简单基础当规则
                        //各个pcm逻辑设备open时也可以通过snd_pcm_hw_rule_add添加自己的rule
                        changed = r->func(params, r);
                        //rstamps记录当前rule被最后一次更新对应的时间戳
                        rstamps[k] = stamp;
                        //如果rule规则检查成功改写了params参数
                        //r->var实际上就是参数标识,标识该rule是针对的哪一个参数
                        if (changed && r->var >= 0) {
                                params->cmask |= (1 << r->var);
                                //vstamps记录当前var(参数标识,例如SNDRV_PCM_HW_PARAM_FORMAT)被最后一次更新对应的时间戳
                                vstamps[r->var] = stamp;
                                //again置位
                                again = 1;
                        }
                        if (changed < 0)
                                return changed;
                        //时间戳,每次都会执行stamp++递增时间戳,让"时间"跳动
                        stamp++;
                }
        //当多条rule约束同一参数时,如果参数被rule给修改了,则表示该参数被此rule约束了
        //一旦一个rule修改了该参数的值,那么所有其他的rule都必须重新对该参数调用一次约束函数
        //直到所有约束该参数的rule都不再会修改该参数的值为止
        } while (again);
        //设置msbits
        if (!params->msbits) {
                i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
                if (snd_interval_single(i))
                        params->msbits = snd_interval_value(i);
        }
		//rate denominator
        if (!params->rate_den) {
                i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
                if (snd_interval_single(i)) {
                        params->rate_num = snd_interval_value(i);
                        params->rate_den = 1;
                }
        }

        hw = &substream->runtime->hw;
        if (!params->info) {
                params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES |
                                            SNDRV_PCM_INFO_DRAIN_TRIGGER);
                if (!hw_support_mmap(substream))
                        params->info &= ~(SNDRV_PCM_INFO_MMAP |
                                          SNDRV_PCM_INFO_MMAP_VALID);
        }
        //设置fifo size
        if (!params->fifo_size) {
                m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
                i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
                if (snd_mask_min(m) == snd_mask_max(m) &&
                    snd_interval_min(i) == snd_interval_max(i)) {
                        changed = substream->ops->ioctl(substream,
                                        SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
                        if (changed < 0)
                                return changed;
                }
        }
        params->rmask = 0;
        return 0;
}

代码详解如注释所示。

snd_pcm_hw_refine函数里面有个很重要的参数:constrs = &substream->runtime->hw_constraints
hw_constraints即硬件约束条件,这里是在哪里赋值的呢?
其实是在snd_pcm_hw_constraints_complete函数里面:

snd_pcm_open_substream
	snd_pcm_hw_constraints_init
	substream->ops->open
	snd_pcm_hw_constraints_complete
		snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask)
		snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats)
		snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD)
		snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, hw->channels_min, hw->channels_max)
		snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, hw->rate_min, hw->rate_max)
		//等等......	

例如对于SNDRV_PCM_HW_PARAM_FORMAT,支持的格式就是hw->formats,由硬件而来。
对于SNDRV_PCM_HW_PARAM_CHANNELS,通道数范围在[hw->channels_min, hw->channels_max],由硬件决定。
对于SNDRV_PCM_HW_PARAM_RATE,速率在[hw->rate_min, hw->rate_max],由硬件决定。
上层params传下来的参数,如果不在这些范围之内,会进行重定义。

关于rule检查,还有个地方需要注意下:

处理多条rule约束同一参数的大致的执行过程:
当多条rule约束同一参数时,每条rule都会调用约束函数来处理该参数,如果
参数被rule给修改了,则表示该参数被此rule约束了,一旦一个rule修改了
该参数的值,那么所有其他的rule都必须重新对该参数调用一次约束函数,直到
所有约束该参数的rule都不再会修改该参数的值为止
rstamps、vstamps、stamp、again这几个变量都是转为此服务的

里面的rule是在哪里add添加的呢?
其实也是在snd_pcm_hw_constraints_init函数里:

int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
{
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
        int k, err;

        for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
                snd_mask_any(constrs_mask(constrs, k));
        }

        for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
                snd_interval_any(constrs_interval(constrs, k));
        }

        snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS));
        snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE));
        snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES));
        snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS));
        snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));

        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
                                   snd_pcm_hw_rule_format, NULL,
                                   SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
                                  snd_pcm_hw_rule_sample_bits, NULL,
                                  SNDRV_PCM_HW_PARAM_FORMAT,
                                  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
                                  snd_pcm_hw_rule_div, NULL,
                                  SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
                                  snd_pcm_hw_rule_mul, NULL,
                                  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
                                  snd_pcm_hw_rule_mulkdiv, (void*) 8,
                                  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
                                  snd_pcm_hw_rule_mulkdiv, (void*) 8,
                                  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
                                  snd_pcm_hw_rule_div, NULL,
                                  SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS,
                                  snd_pcm_hw_rule_div, NULL,
                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
                                  snd_pcm_hw_rule_div, NULL,
                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
        if (err < 0)
                return err;
        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);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
                                  snd_pcm_hw_rule_muldivk, (void*) 1000000,
                                  SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
                                  snd_pcm_hw_rule_mul, NULL,
                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
                                  snd_pcm_hw_rule_mulkdiv, (void*) 8,
                                  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
                                  snd_pcm_hw_rule_muldivk, (void*) 1000000,
                                  SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
                                  snd_pcm_hw_rule_muldivk, (void*) 8,
                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
                                  snd_pcm_hw_rule_muldivk, (void*) 8,
                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
                                  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
        if (err < 0)
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
                                  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
        if (err < 0)
                return err;
        return 0;
}

这里挖个坑吧~ 之后有时间再啃下rule之间的计算过程。


参考:
浅析alsa声卡驱动snd_pcm_hw_refine函数
高通msm8996平台的ASOC音频路径分析(基于androidN及linux3.1x)

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值