linux音频子系统 - control设备

孟俊发
2023-12-01

control设备主要是能控制音频设备的音量、开关等,在驱动中有两个主要的结构体:snd_kcontrol_new和snd_kcontrol,驱动开发者主要是定义这两个结构体,来进行control设备的开发

结构体

struct snd_kcontrol_new

此结构体类似模板,最终还是要构成一个snd_kcontrol结构体来进行注册

(include/sound/control.h)

struct snd_kcontrol_new {
    snd_ctl_elem_iface_t iface; /* interface identifier */
    unsigned int device;        /* device/client number */
    unsigned int subdevice;     /* subdevice (substream) number */
    const unsigned char *name;  /* ASCII name of item */
    unsigned int index;     /* index of item */
    unsigned int access;        /* access rights */
    unsigned int count;     /* count of same elements */
    snd_kcontrol_info_t *info;
    snd_kcontrol_get_t *get;--------相当于read
    snd_kcontrol_put_t *put;--------相当于write
    union {
        snd_kcontrol_tlv_rw_t *c;
        const unsigned int *p;
    } tlv;
    unsigned long private_value;
};

struct snd_kcontrol

(include/sound/control.h)

struct snd_kcontrol {
    struct list_head list;      /* list of controls */
    struct snd_ctl_elem_id id;
    unsigned int count;     /* count of same elements */
    snd_kcontrol_info_t *info;
    snd_kcontrol_get_t *get;
    snd_kcontrol_put_t *put;
    union {
        snd_kcontrol_tlv_rw_t *c;
        const unsigned int *p;
    } tlv;
    unsigned long private_value;
    void *private_data;
    void (*private_free)(struct snd_kcontrol *kcontrol);
    struct snd_kcontrol_volatile vd[0]; /* volatile data */
};

control设备注册

驱动架构文章中,我们知道声卡注册最终会调用到每个子设备的注册函数,control设备属于声卡的子设备,所以control设备得先注册到声卡中

control注册进声卡

snd_ctl_create会把control注册到声卡中

(sound/core/control.c)

int snd_ctl_create(struct snd_card *card)
{
    static struct snd_device_ops ops = {
        .dev_free = snd_ctl_dev_free,
        .dev_register = snd_ctl_dev_register,------------control注册函数
        .dev_disconnect = snd_ctl_dev_disconnect,
    };

    if (snd_BUG_ON(!card))
        return -ENXIO;
    return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);---把control加入到声卡的devices链表中
}

card注册时,最终调用此函数来进行control注册:

static int snd_ctl_dev_register(struct snd_device *device)
{
    struct snd_card *card = device->device_data;
    int err, cardnum;
    char name[16];

    if (snd_BUG_ON(!card))
        return -ENXIO;
    cardnum = card->number;
    if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
        return -ENXIO;
    sprintf(name, "controlC%i", cardnum);
    if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
                       &snd_ctl_f_ops, card, name)) < 0)------最终调用device_create进行设备注册
        return err;
    return 0;
}

control单元加入到control设备

control这个设备是有好几种单元来组成的,每个单元相对应一个snd_kcontrol,所以上层要操作的control都是要先找到对应的control单元,再来进行操作,所以在注册的时候,要把control单元都加入到声卡中

int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
    struct snd_ctl_elem_id id;
    unsigned int idx;
    unsigned int count;
    int err = -EINVAL;

    if (! kcontrol)
        return err;
    if (snd_BUG_ON(!card || !kcontrol->info))
        goto error;
    id = kcontrol->id;
    if (id.index > UINT_MAX - kcontrol->count)
        goto error;

    down_write(&card->controls_rwsem);
    if (snd_ctl_find_id(card, &id)) {
        up_write(&card->controls_rwsem);
        snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n",
                    id.iface,
                    id.device,
                    id.subdevice,
                    id.name,
                    id.index);
        err = -EBUSY;
        goto error;
    }
    if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
        up_write(&card->controls_rwsem);
        err = -ENOMEM;
        goto error;
    }
    list_add_tail(&kcontrol->list, &card->controls);---------加入声卡的controls链表中
    card->controls_count += kcontrol->count;
    kcontrol->id.numid = card->last_numid + 1;
    card->last_numid += kcontrol->count;
    count = kcontrol->count;
    up_write(&card->controls_rwsem);
    for (idx = 0; idx < count; idx++, id.index++, id.numid++)
        snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
    return 0;

 error:
    snd_ctl_free_one(kcontrol);
    return err;
}

而对于snd_kcontrol_new结构体可以用下面的函数重定义成结构体snd_kcontrol

struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data);

control文件操作

(sound/core/control.c)

static const struct file_operations snd_ctl_f_ops =
{
    .owner =    THIS_MODULE,
    .read =     snd_ctl_read,
    .open =     snd_ctl_open,
    .release =  snd_ctl_release,
    .llseek =   no_llseek,
    .poll =     snd_ctl_poll,
    .unlocked_ioctl =   snd_ctl_ioctl,
    .compat_ioctl = snd_ctl_ioctl_compat,
    .fasync =   snd_ctl_fasync,
};

这些ops操作函数最终要调用kcontrol的info/get/put函数,这些函数原型为:

typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo);
typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol,
                    int op_flag, /* 0=read,1=write,-1=command */
                    unsigned int size,
                    unsigned int __user *tlv);

change log

datecontentlinux
2017/11/24origin3.10
 类似资料: