这些天大刀阔斧修改了我们的alsa音频驱动,更深切理解了机制和策略分离的重要性。
说来惭愧,Linux Device Drivers一书翻来覆去查阅了无数遍,却忽视了绪论中的一句话:区分机制和策略是Unix设计背后隐含的最好思想之一。
“需要提供什么功能”即机制,“如何使用这些功能”即策略。一年多前,初接手音频驱动开发时,就混淆了机制和策略,主要表现在音频通道方面。
最典型的例如:当codec检测到有headset插入时,就关闭speaker,声音转向headset输出;而检测到headset拔下时,关闭headset通道,声音转向speaker输出。
这个做法,我当时就认为是正确的场景。可是却没有考虑到一些特殊的场景:如在headset插入的状态下,这时如果是闹钟铃声或电话铃声的话,声音还是必须要speaker出来的。
各种应用场景千奇百怪,作为底层的不能假设什么策略是正确的,更不应该擅自去实现这些策略。只需要提供相关的功能即可,上层根据需要来调用这些接口。
还是以上述的音频通道为例:
1、在codec驱动中会实现snd_kcontrol(mixer control)、dapm、audio_map等,如
SOC_SINGLE("Headset Switch", xxx_HEADSET_REG, 0, 1, 0), SOC_SINGLE("Headfree Switch", xxx_HEADFREE_REG, 0, 1, 0),上面两个mixer control可对headset和headfree这两个通道进行切换控制。
2、当Android系统跑起来后,可用tinymix命令(Android4.0版本,如果是之前版本的话,用alsa_amixer命令)可看到:
17 BOOL 1 Headset Switch Off 18 BOOL 1 Headfree Switch Off因为目前没有声音需要输出,因此这两个通道都是关闭的。
3、在Android的音频抽象层audio_hw.c可这样定义:
struct route_setting hf_output[] = { { .ctl_name = "Headfree Switch", .intval = 1, }, /* end of the route_setting */ { .ctl_name = NULL, }, }; struct route_setting hs_output[] = { { .ctl_name = "Headset Switch", .intval = 1, }, /* end of the route_setting */ { .ctl_name = NULL, }, };这样程序可以根据AudioPolicyService提供的音频策略来控制底层的音频通路:
headset_on = adev->devices & AUDIO_DEVICE_OUT_WIRED_HEADSET; headphone_on = adev->devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; speaker_on = adev->devices & AUDIO_DEVICE_OUT_SPEAKER; earpiece_on = adev->devices & AUDIO_DEVICE_OUT_EARPIECE; bt_on = adev->devices & AUDIO_DEVICE_OUT_ALL_SCO; /* select output stage */ set_route_by_array(adev->mixer, hs_output, headset_on | headphone_on); set_route_by_array(adev->mixer, hf_output, speaker_on);