免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1586 | 回复: 0
打印 上一主题 下一主题

writing-an-alsa-driver(编写一个ALSA驱动)翻译稿 第六章 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-06-27 17:21 |只看该作者 |倒序浏览

       
       
第六章
控制接口
翻译:creator

sz111@126.com

概要
   
控制接口非常广泛的应用在许多转换,变调等场合,可以从用户空间进行控制。混音器接口是一个最重要的接口。换句话说,在ALSA0.9.x版本,所有的混音器的工作都是通过控制接口API实现的(在0.5.x版本混音器内核API是独立出来的)。
   
ALSA有一个定义很好的AC97的控制模块。如果你的声卡仅仅支持AC97,你可以忽略这章。
控制接口定义
   
为了创建一个新的控制接口,需要定义三个函数:info,get和put。然后定义一个snd_kcontrol_new类型的记录,例如:
   
Example6-1.定义一个控制接口
   
static struct
snd_kcontrol_new my_control __devinitdata = {
   
    .face =
SNDRV_CTL_ELEM_IFACE_MIXER,
   
    .name =
“PCM Playback Switch”,
   
    .index =
0,
   
    .access =
SNDRV_CTL_ELEM_ACCESS_READWRITE,
   
    .private_value
= 0xffff,
   
    .info =
my_control_info,
   
    .get =
my_control_get,
   
    .put =
my_control_put
   
};
   
大部分情况是通过snd_ctl_new1()来创建control,这种情况下,你可以像上面一样,在定义的前面加上__devinitdata前缀.
   
iface字段表示control的类型,如:SNDRV_CTL_ELEM_IFACE_XXX,通常情况都是MIXER。CARD表示一个全局控制,而不是混音器的一部分。假如control和声卡设备联系的非常紧密,如HWDEP,RAMDIDI,TIMER或SEQUENCER,需要特别通过device和subdevice字段标识出。
   
name字段是表示名称的标识符。在ALSA0.9.X,控制单元名字是非常重要的,因为的角色就是通过它的名字进行分类。有一些预定义的标准的control名字。详细描述请参考下节的“控制单元名字”。
   
index字段存放这个control的索引号。假如一个名字下面有多个不同的control,就要通过index(索引)来区分了。这是当一个声卡拥有多个解码的时候才会用到。加入索引设定为零,就可以忽略定义。
   
access字段包括了控制接口的存取控制。提供了一些位的组合,如:SNDRV_CTL_ELEM_XXX。详细描述请参考“接口标志位”一节。
   
private_value字段这个记录的一个专有的的长整型变量。当调用info,get和put函数的时候,可以通过这个字段传递一些参数值。如果参数都是些很小的数,可以把它们通过移位来组合,也可以存放一个指向一个记录的指针(因为它是长整型的)。
   
另外三个在回调函数一节介绍。
  
控制接口名字
   
有一些control名字的标准。一个control通常根据“源,方向,功能”三部分来命名。
   
首先,SOURCE定义了control的源,是一个字符串,如:“Master”,“PCM”,“CD”,“Line”。已经有很多预定义好的“源”了。
   
第二,“方向”则为“Playbck”,“Capture”,“Bypass
Playback”,“Bypass
Capture”。或者,它如果省略,那就表示播放和录音双向。
   
第三个,“功能”。根据控制接口的功能不同有下面三个:“Switch”,“volume”,“route”。
   
一些控制接口名字的范例如下:“Master
Capture Switch”,“PCM
Playback Volume”.
   
也有一些不是采用“源,方向,功能”三部分来命名方式:
   
全局录音和播放
   
“Capture
Source”,“Capture
Switch”和“Capture
Volume”用来做全局录音(输入)源,开关,和音量的控制。“Playback
Switch”和“Playback
Volume”用来做全局的输出开关和音量控制。
   
音调控制
   
音调开关和音量名称形式为“Tone
Control-XXX”。如:“Tone
Control-Switch”,“Tone
Control – Bass”,“Tone
Control – Center”。
   
3D控制
   
3D控制开关和音量命名形式为“3D
Control – XXX”。如:“3D
Control – Switch”,“3D
Control – Switch”,“3D
Control – Center”,“3D Control – Space”。
   
麦克风增益
   
麦克风增益命名如“Mic
Boost”或“Mic
Boost(6dB)”。
   
更精确的信息请参考文档(Documentation/sound/alsa/ControlNames.txt)
存取标志
   
存取标志是一个位标志,主要是区分给定的control的存取类型。缺省的存取类型是SNDRV_CTL_ELEM_ACCESS_READWRITE,意思是允许对control进行读写控制。当这个标志位被忽略的时候(为0),被认为是缺省读写(READWRITE)。
   
当这个control是只读的时候,需要传递SNDRV_CTL_ELEM_ACCESS_READ。这时候,可以不定义put函数。类似的,如果control是只写的话(虽然这种可能性很低),要设定为WRITE标志,也可以不必定义get函数。
   
加入control的值是经常改变的,应该加上VOLATILE标志,这意味着control可以不用显式通知就可以改变。应用程序应经常查询control。
   
当一个control是不活动的话,设定INACTIVE标志。还有LOCK和OWNER标志用来改变写权限。
   
回调函数
   
info函数
   
info函数可以得到对应control的详细信息。它必须存到一个给定的snd_ctl_elem_info对象中。例如,对于一个拥有一个元素的布尔型control的如下:
   
Example6-2.info函数示例
   
static int
snd_myctl_info(struct snd_kcontrol *kcontrol,
   
                          struct
snd_ctl_elem_info *uinfo)
   
{
   
     uinfo->type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN;
   
     uinfo->count
= 1;
   
     uinfo->value.integer.min
= 0;
   
     uinfo->value.integer.max
= 1;
   
     return
0;
   
}
   
type字段表示control的类型。有如下几种:布尔型,整形,枚举型,BYTES型,IEC958,64位整形。count字段表示这个control里面的元素的数量。如:一个立体声音量拥有的count为2。value字段是一个union(联合)类型。value的存储依赖于类型。布尔型和整形是一样的。
   
枚举型和其他类型有一点不同,需要为当前给定的索引项设定字符串。
   
Static int
snd_myctl_info(struct snd_kcontrol *kcontrol,
   
                          struct
snd_ctl_elem_info *uinfo)
   
{
   
    static
char *texts[4] ={
   
        “First”,“Second”,“Third”,“Fourth”
   
    };
   
    uinfo->type
= SNDRV_CTL_ELEM_TYPE_ENUMRATED;
   
    uinfo->count
= 1;
   
    uinfo->value.enumerated.items
= 4;
   
    if
(uinfo->value.enumerated.item > 3)
   
      
uinfo->value.enumerated.item
= 3;
      
   
    strcpy(
uinfo->value.enumerated.name,texts[
uinfo->value.enumerated.item]);
   
    return
0;
   
}
   
get函数
   
这个函数用来读取当前control的值并返回到用户空间。
   
例如:
   
Example6-3.get函数示例
   
static int
snd_myctl_get(struct snd_kcontrol *kcontrol,
   
                         struct
snd_ctl_ctl_elem_value *ucontrol)
   
{
   
    struct
mychip *chip = snd_kcontrol_chip(kcontrol);
   
    ucontrol->value.integer.value[0]
= get_some_value(chip);
   
    return 0;
   
}
   
value字段和control类型有关,这点和info类似。例如,子驱动和用这个字段来存储一些寄存器的偏移,位的屏蔽。private_value设定如下:
   
.private_value
= reg | (shift
   
可以通过以下函数重新得到:
   
static int
snd_sbmixer_get_single(struct snd_kcontrol *kcontrol,
   
                                   struct
snd_ctl_elem_value *ucontrol)
   
{
   
     int reg
= kcontrol->private_value & 0xff;
   
     int
shift = (kcontrol->private_value >> 16) & 0xff;
   
     int mask
= (kcontrol->private_value >> 24) & 0xff;
   
     ....
   
}
   
在get函数中,加入control拥有超过一个的元素。如count大于1,就必须填充所有的元素。上面的例子中,因为我们假定count为1,所以我们仅仅填充了一个元素(value.integer.value[0])。
   
   
put函数
   
这个函数主要是从用户空间写一个值
   
例如:
   
Example6-4.put函数示例
   
static int
snd_myctrl_put(struct snd_kcontrol *kcontrol,
   
                          struct
snd_ctl_elem_value *ucontrol)
   
{
   
    struct
mychip *chip = snd_kcontrol_chip(kcontrol);
   
    int
changed = 0;
   
    if
(chip->current_value != ucontrol->value.integer.value[0]){
   
        change_current_value(chip,ucontrol->value.integer.value[0]);
   
        changed
= 1;
   
    }
   
    return
changed;
   
}
   
如上,假如value被改变要返回1,没有改变返回0。假如有错误发生,通常返回一个带错误码的负数。
   
像get函数一样,如果control拥有超过一个的元素,在put函数中所有的元素都要比较一下。
   
回调函数不是原子的。
   
所有上面的3个回调函数都是非原子的。
构造器
   
当所有事情都准备好的时候,我们就可以创建一个新的control了。为了创建它,首先要调用两个函数,snd_ctl_new1()和snd_ctl_add()。
   
一个简单的方式如下:
   
if ((err =
snd_ctl_add(card, snd_ctl_new1(&my_control, chip)))
   
    return
err;
   
my_control是前面定义好的snd_kcontrol_new类型的对象,chip是一个指向kcontrol->private_data的指针,可以被回调函数调用。
   
snd_ctl_new1()分配了一个新的snd_kcontrol实例(这也是为何my_control可以带有__devinitdata前缀的原因了),snd_ctl_add会把给定的control组件添加到card里面。
   
更改通知
   
假如你需要在中断程序中改变或更新一个control,你需要调用snd_ctl_notify()。
    例如:
   
snd_ctl_notify(card,
SNDRV_CTL_EVENT_MASK_VALUE,id_pointer);
   
这个函数需要card指针,event_mask,和control
id指针作为参数。event-mask表示notification的类型,如上述示例,是通知改变control的值。id指针是指向一个snd_ctl_elem_id的结构体。在es1938.c或es1968.c中关于硬件的卷的中断部分有相关示例。
  
   
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/49088/showart_1018357.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP