writing-an-alsa-driver(编写一个ALSA驱动)翻译稿 第六章

1648阅读 0评论2012-07-13 zhou991
分类:

文件:编写一个ALSA驱动1-6章.pdf
大小:237KB
下载:下载

第六章 控制接口

翻译:creator

概要

控制接口非常广泛的应用在许多转换,变调等场合,可以从用户空间进行控制。混音器接口是一个最重要的接口。换句话说,在ALSA0.9.x版本,所有的混音器的工作都是通过控制接口API实现的(在0.5.x版本混音器内核API是独立出来的)。

ALSA有一个定义很好的AC97的控制模块。如果你的声卡仅仅支持AC97,你可以忽略这章。


控制接口定义

为了创建一个新的控制接口,需要定义三个函数:info,getput。然后定义一个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,通常情况都是MIXERCARD表示一个全局控制,而不是混音器的一部分。假如control和声卡设备联系的非常紧密,如HWDEP,RAMDIDI,TIMERSEQUENCER,需要特别通过devicesubdevice字段标识出。

name字段是表示名称的标识符。在ALSA0.9.X,控制单元名字是非常重要的,因为的角色就是通过它的名字进行分类。有一些预定义的标准的control名字。详细描述请参考下节的“控制单元名字”。

index字段存放这个control的索引号。假如一个名字下面有多个不同的control,就要通过index(索引)来区分了。这是当一个声卡拥有多个解码的时候才会用到。加入索引设定为零,就可以忽略定义。

access字段包括了控制接口的存取控制。提供了一些位的组合,如:SNDRV_CTL_ELEM_XXX。详细描述请参考“接口标志位”一节。

private_value字段这个记录的一个专有的的长整型变量。当调用info,getput函数的时候,可以通过这个字段传递一些参数值。如果参数都是些很小的数,可以把它们通过移位来组合,也可以存放一个指向一个记录的指针(因为它是长整型的)。

另外三个在回调函数一节介绍。

控制接口名字

有一些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标志。还有LOCKOWNER标志用来改变写权限。

回调函数

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里面的元素的数量。如:一个立体声音量拥有的count2value字段是一个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 << 16) | (mask << 24)

可以通过以下函数重新得到:

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,就必须填充所有的元素。上面的例子中,因为我们假定count1,所以我们仅仅填充了一个元素(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))) < 0)

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.ces1968.c中关于硬件的卷的中断部分有相关示例。

上一篇:writing-an-alsa-driver(编写一个ALSA驱动)翻译稿 第四章
下一篇:framebuffer驱动全篇(一)