当前位置:嗨网首页>书籍在线阅读

16-控制接口

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

17.4.4 控制接口

1.control

控制接口对于许多开关(switch)和调节器(slider)而言应用相当广泛,它能从用户空间被存取。control的最主要用途是mixer,所有的mixer元素基于control内核API实现,在ALSA中,control用snd_kcontrol结构体描述。

ALSA有一个定义很好的AC97控制模块,对于仅支持AC97的芯片而言,不必实现本节的内容。

创建一个新的control至少需要实现snd_kcontrol_new中的info()、get()和put()这3个成员函数,snd_kcontrol_new结构体的定义如代码清单17.16所示。

代码清单17.16 snd_kcontrol_new结构体

1 struct snd_kcontrol_new {

2 snd_ctl_elem_iface_t iface; /接口ID,SNDRV_CTL_ELEM_IFACE_XXX /

3 unsigned int device; / 设备号 /

4 unsigned int subdevice; / 子流(子设备)号 /

5 unsigned char name; / 名称(ASCII格式) */

6 unsigned int index; / 索引 /

7 unsigned int access; / 访问权限 /

8 unsigned int count; / 享用元素的数量 /

9 snd_kcontrol_info_t *info;

10 snd_kcontrol_get_t *get;

11 snd_kcontrol_put_t *put;

12 unsigned long private_value;

13 };

iface字段定义了control的类型,形式为SNDRV_CTL_ELEM_IFACE_XXX,通常是MIXER,对于不属于mixer的全局控制,使用CARD。如果关联于某类设备,则使用HWDEP、PCM、RAWMIDI、TIMER或SEQUENCER。

name是名称标识字符串,control的名称非常重要,因为control的作用由名称来区分。对于名称相同的control,则使用index区分。name定义的标准是“SOURCE DIRECTION FUNCTION”即“源 方向 功能”,SOURCE定义了control的源,如“Master”、“PCM”、“CD”和“Line”,方向则为“Playback”、“Capture”、“Bypass Playback”或“Bypass Capture”,如果方向省略,意味着playback和capture双向,第3个参数可以是“Switch”、“Volume”和“Route”等。

“SOURCE DIRECTION FUNCTION”格式的名称例子如“Master Capture Switch”、“PCM Playback Volume”。

下面几种control的命名不采用“SOURCE DIRECTION FUNCTION”格式,属于例外。

(1)全局控制。

“Capture Source”、“Capture Switch”和“Capture Volume”用于全局录音源、输入开关和录音音量控制;“Playback Switch”、“Playback Volume”用于全局输出开关和音量控制。

(2)音调控制。

音调控制名称的形式为“Tone Control – XXX”,例如“Tone Control – Switch”、“Tone Control – Bas”和“Tone Control – Center”。

(3)3D控制。

3D控制名称的形式为“3D Control – XXX”,例如“3D Control – Switch”、“3D Control – Center”和“3D Control – Space”。

(4)麦克风增益(Mic boost)。

麦克风增益被设置为“Mic Boost”或“Mic Boost (6dB)”。

snd_kcontrol_new结构体的access字段是访问控制权限,形式如SNDRV_CTLELEM ACCESS_XXX。SNDRV_CTL_ELEM_ACCESS_READ意味着只读,这时put()函数不必实现;SNDRV_CTL_ELEM_ACCESS_WRITE意味着只写,这时get()函数不必实现。若control值频繁变化,则需定义VOLATILE标志。当control处于非激活状态时,应设置INACTIVE标志。

private_value字段包含一个长整型值,可以通过它给info()、get()和put()函数传递参数。

2.info()函数

snd_kcontrol_new结构体中的info()函数用于获得该control的详细信息,该函数必须填充传递给它的第二个参数snd_ctl_elem_info结构体,info()函数的形式如下:

static int snd_xxxctl_info(struct snd_kcontrol kcontrol, struct snd_ctl_elem_info uinfo);

snd_ctl_elem_info结构体的定义如代码清单17.17所示。

代码清单17.17 snd_ctl_elem_info结构体

1 struct snd_ctl_elem_info

2 {

3 struct snd_ctl_elem_id id; / W: 元素ID /

4 snd_ctl_elem_type_t type; / R: 值类型 - SNDRV_CTL_ELEMTYPE */

5 unsigned int access; / R: 值访问权限(位掩码) - SNDRV_CTL_ELEMACCESS */

6 unsigned int count; / 值的计数 /

7 pid_t owner; / 该control的拥有者PID /

8 union {

9 struct {

10 long min; / R: 最小值 /

11 long max; / R: 最大值 /

12 long step; / R: 值步进 (0 可变的) /

13 } integer;

14 struct {

15 long long min; / R: 最小值 /

16 long long max; / R: 最大值 /

17 long long step; / R: 值步进 (0 可变的) /

18 } integer64;

19 struct {

20 unsigned int items; / R: 项目数 /

21 unsigned int item; / W: 项目号 /

22 char name[64]; / R: 值名称 /

23 } enumerated; / 枚举 /

24 unsigned char reserved[128];

25 }

26 value;

27 union {

28 unsigned short d[4];

29 unsigned short *d_ptr;

30 } dimen;

31 unsigned char reserved[64-4 * sizeof(unsigned short)];

32 };

snd_ctl_elem_info结构体的type字段定义了control的类型,包括BOOLEAN、INTEGER、ENUMERATED、BYTES、IEC958和INTEGER64。count字段定义了这个control中包含的元素的数量,例如一个立体声音量control的count = 2。value是一个联合体,其所存储的值的具体类型依赖于type。代码清单17.18所示为一个info()函数填充snd_ctl_elem_info结构体的范例。

代码清单17.18 snd_ctl_elem_info结构体中的info()函数范例

1 static int snd_xxxctl_info(struct snd_kcontrol *kcontrol, struct

2 snd_ctl_elem_info *uinfo)

3 {

4 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;/ 类型为BOOLEAN /

5 uinfo->count = 1;/ 数量为1 /

6 uinfo->value.integer.min = 0;/ 最小值为0 /

7 uinfo->value.integer.max = 1;/ 最大值为1 /

8 return 0;

9 }

枚举类型和其他类型略有不同,对枚举类型,应为目前项目索引设置名称字符串,如代码清单17.19所示。

代码清单17.19 填充snd_ctl_elem_info结构体中的权举类型值

1 static int snd_xxxctl_info(struct snd_kcontrol *kcontrol, struct

2 snd_ctl_elem_info *uinfo)

3 {

4 / 值名称字符串/

5 static char *texts[4] = {

6 "First", "Second", "Third", "Fourth"

7 };

8 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;/ 枚举类型/

9 uinfo->count = 1;/ 数量为1 /

10 uinfo->value.enumerated.items = 4;/ 项目数量为1 /

11 / 超过3的项目号改为3 /

12 if (uinfo->value.enumerated.item > 3)

13 uinfo->value.enumerated.item = 3;

14 / 为目前项目索引复制名称字符串/

15 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);

16 return 0;

17 }

3.get()函数

get()函数用于得到control的目前值并返回用户空间,代码清单17.20所示为get()函数的范例。

代码清单17.20 snd_ctl_elem_info结构体中的get()函数范例

1 static int snd_xxxctl_get(struct snd_kcontrol *kcontrol, struct

2 snd_ctl_elem_value *ucontrol)

3 {

4 / 从snd_kcontrol获得xxxchip指针/

5 struct xxxchip *chip = snd_kcontrol_chip(kcontrol);

6 / 从xxxchip获得值并写入snd_ctl_elem_value /

7 ucontrol->value.integer.value[0] = get_some_value(chip);

8 return 0;

9 }

get()函数的第二个参数的类型为snd_ctl_elem_value,其定义如代码清单10.21所示。sndctl elem_ value结构体的内部也包含一个由integer、integer64、enumerated等组成的值联合体,它的具体类型依赖于control的类型和info()函数。

代码清单17.21 snd_ctl_elem_value结构体

1 struct snd_ctl_elem_value

2 {

3 struct snd_ctl_elem_id id; / W: 元素ID /

4 unsigned int indirect: 1; / W: 使用间接指针(xxx_ptr成员) /

5 / 值联合体/

6 union {

7 union {

8 long value[128];

9 long *value_ptr;

10 } integer;

11 union {

12 long long value[64];

13 long long *value_ptr;

14 } integer64;

15 union {

16 unsigned int item[128];

17 unsigned int *item_ptr;

18 } enumerated;

19 union {

20 unsigned char data[512];

21 unsigned char *data_ptr;

22 } bytes;

23 struct snd_aes_iec958 iec958;

24 }

25 value; / 只读 /

26 struct timespec tstamp;

27 unsigned char reserved[128-sizeof(struct timespec)];

28 };

4.put()函数

put()用于从用户空间写入值,如果值被改变,该函数返回1,否则返回0;如果发生错误,该函数返回一个错误码。代码清单17.22所示为一个put()函数的范例。

代码清单17.22 snd_ctl_elem_info结构体中的put()函数范例

1 static int snd_xxxctl_put(struct snd_kcontrol *kcontrol, struct

2 snd_ctl_elem_value *ucontrol)

3 {

4 / 从snd_kcontrol获得xxxchip指针/

5 struct xxxchip *chip = snd_kcontrol_chip(kcontrol);

6 int changed = 0;/ 默认返回值为0 /

7 / 值被改变/

8 if (chip->current_value != ucontrol->value.integer.value[0]) {

9 change_current_value(chip, ucontrol->value.integer.value[0]);

10 changed = 1;/ 返回值为1 /

11 }

12 return changed;

13 }

对于get()和put()函数而言,如果control有多于一个元素,即count >1,则每个元素都需要被返回或写入。

5.构造control

当所有事情准备好后,我们需要创建一个control,调用snd_ctl_add()和snd_ctl_new1()这两个函数来完成,这两个函数的原型为:

int snd_ctl_add(struct snd_card card, struct snd_kcontrol kcontrol);

struct snd_kcontrol snd_ctl_new1(const struct snd_kcontrol_new ncontrol,

void *private_data);

snd_ctl_new1()函数用于创建一个snd_kcontrol并返回其指针,snd_ctl_add()函数用于将创建的snd_kcontrol添加到对应的card中。

6.变更通知

如果驱动中需要在中断服务程序中改变或更新一个control,可以调用snd_ctl_notify()函数,此函数原型为:

void snd_ctl_notify(struct snd_card card, unsigned int mask, struct snd_ctl_elem_id id);

该函数的第二个参数为事件掩码(event-mask),第三个参数为该通知的control元素id指针。

例如,如下语句定义的事件掩码SNDRV_CTL_EVENT_MASK_VALUE意味着control值的改变被通知:

snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, id_pointer);