程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

Rockchip RK3399 - ASoC 声卡之Jack设备

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板
eMMC :16GB
LPDDR3 :4GB
显示屏 :15.6英寸HDMI接口显示屏
u-boot :2023.04
linux   :6.3
----------------------------------------------------------------------------------------------------------------------------

jack字面上就是插孔,什么是插孔,我理解就是我们音频设备上的麦克风、耳机等设备的插孔。

那jack设备是用来实现什么功能的呢?其主要目的是为了实现耳机、麦克风等设备插入和拔出的检测。

我们以耳机检测为例来说说,一般的耳机检测包括普通的耳机检测和带有麦克风的耳机检测两种,这两种耳机统称为Headset,而对于不带麦克风的耳机,一般统称为Headphone。

对于Headset装置的插入检测,一般是通过jack来完成,大致原理是使用带检测机械结构的耳机插座,将检测引脚连接到SoC的GPIO口,如下图所示,当耳机插入时。耳机插头的金属会碰到检测引脚,使得检测引脚的电平发生改变,从而触发中断。这样就可以在中断处理函数中读取GPIO口的值,进一步判断是耳机插入还是拔出。

ALSA CORE已经实现了jack中间层,在文件include/sound/jack.h中提供了访问jack中间层代码的API。

一、ALSA核心数据结构

1.1 struct snd_jack 

Jack使用struct snd_jack数据结构来描述;

复制代码
struct snd_jack {
        struct list_head kctl_list;
        struct snd_card *card;
        const char *id;
#ifdef CONFIG_SND_JACK_INPUT_DEV
        struct input_dev *input_dev;
        struct mutex input_dev_lock;
        int registered;
        int type;
        char name[100];
        unsigned int key[6];   /* Keep in sync with definitions above */
#endif /* CONFIG_SND_JACK_INPUT_DEV */
        int hw_status_cache;
        void *private_data;
        void (*private_free)(struct snd_jack *);
};
复制代码

其中:

  • kctl_list:保存jack kcontrol的链表,链表中的每一个元素都是struct snd_jack_kctl;
  • card:所属声卡设备;
  • id:jack的唯一标识符;
  • input_dev:输入设备结构体(struct input_dev)指针,用于处理与jack相关的输入事件;
  • input_dev_lock:输入设备锁,用于保护对输入设备的操作;
  • registered:jack是否已经注册的标志;
  • type:jack能够上报的类型,参考enum snd_jack_types;
  • name:jack的名称字符串,长度为100字节;
  • key:如果jack能够上报SND_JACK_BTN_0、SND_JACK_BTN_1等类型,数组元素key[x]存放SND_JACK_BTN_x对应的EV_KEY事件的事件编码;
  • hw_status_cache:硬件状态的缓存值;
  • private_data:私有数据指针,可以用于存储与jack相关的其他数据;
  • private_free:私有数据释放函数指针,用于在释放snd_jack时释放相关的私有数据;

1.2 enum snd_jack_types 

jack能够上报的类型如结构体定义描述;

复制代码
/**
 * enum snd_jack_types - Jack types which can be reported
 * @SND_JACK_HEADPHONE: Headphone
 * @SND_JACK_MICROPHONE: Microphone
 * @SND_JACK_HEADSET: Headset
 * @SND_JACK_LINEOUT: Line out
 * @SND_JACK_MECHANICAL: Mechanical switch
 * @SND_JACK_VIDEOOUT: Video out
 * @SND_JACK_AVOUT: AV (Audio Video) out
 * @SND_JACK_LINEIN:  Line in
 * @SND_JACK_BTN_0: Button 0
 * @SND_JACK_BTN_1: Button 1
 * @SND_JACK_BTN_2: Button 2
 * @SND_JACK_BTN_3: Button 3
 * @SND_JACK_BTN_4: Button 4
 * @SND_JACK_BTN_5: Button 5
 *
 * These values are used as a bitmask.
 *
 * Note that this must be kept in sync with the lookup table in
 * sound/core/jack.c.
 */
enum snd_jack_types {
        SND_JACK_HEADPHONE      = 0x0001,  // 如果jack->type & 0x001=0x001,则设置input设备可以上报EV_SW事件,事件编码为0x02
        SND_JACK_MICROPHONE     = 0x0002,  // 同上,设置input设备可以上报EV_SW事件,事件编码为0x04
        SND_JACK_HEADSET        = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
        SND_JACK_LINEOUT        = 0x0004,  // 同上,设置input设备可以上报EV_SW事件,事件编码为0x04
        SND_JACK_MECHANICAL     = 0x0008, /* If detected separately */ 同上,设置input设备可以上报EV_SW事件,事件编码为0x07
        SND_JACK_VIDEOOUT       = 0x0010,  // 同上,设置input设备可以上报EV_SW事件,事件编码为0x08
        SND_JACK_AVOUT          = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT,
        SND_JACK_LINEIN         = 0x0020,  // 同上,则设置input设备可以上报EV_SW事件,事件编码为0x0d

        /* Kept separate from switches to facilitate implementation */
        SND_JACK_BTN_0          = 0x4000,   // 如果jack->type & 0x4000=0x4000,则设置input设备可以上报EV_KEY事件,事件编码为0x100
        SND_JACK_BTN_1          = 0x2000,   // 同上,设置input设备可以上报EV_KEY事件,事件编码为0x101
        SND_JACK_BTN_2          = 0x1000,   // 同上,设置input设备可以上报EV_KEY事件,事件编码为0x102
        SND_JACK_BTN_3          = 0x0800,   // 同上,设置input设备可以上报EV_KEY事件,事件编码为0x103
        SND_JACK_BTN_4          = 0x0400,   // 同上,设置input设备可以上报EV_KEY事件,事件编码为0x104
        SND_JACK_BTN_5          = 0x0200,   // 同上。设置input设备可以上报EV_KEY事件,事件编码为0x105
};
复制代码

1.3 struct snd_jack_kctl

ALSA中使用struct snd_jack_kctl数据结构来描述jack中的kcontrol,定义在sound/core/jack.c:

复制代码
struct snd_jack_kctl {
        struct snd_kcontrol *kctl;
        struct list_head list;  /* list of controls belong to the same jack */
        unsigned int mask_bits; /* only masked status bits are reported via kctl */
        struct snd_jack *jack;  /* pointer to struct snd_jack */
        bool sw_inject_enable;  /* allow to inject plug event via debugfs */
#ifdef CONFIG_SND_JACK_INJECTION_DEBUG
        struct dentry *jack_debugfs_root; /* jack_kctl debugfs root */
#endif
};
复制代码

其中:

  • kctl:指向struct snd_kcontrol的指针;
  • list:链表节点,用于将当前节点链接到snd_jack的kctl_list链表;
  • mask_bits:当前jack control能够上报的类型,参考enum snd_jack_types;比如我们设置了mask_bits值为SND_JACK_HEADPHONE,当耳机插入或者拔出的时候HP_DET引脚就会接收到中断,中断处理程序读取引脚电平,并通过input设备上报SND_JACK_HEADPHONE对应的EV_SK事件,事件编码为0x02,值为插入/拔出;
  • jack:指向struct snd_jack的指针,表示与snd_jack_kctl相关联的jack;
  • sw_inject_enable:允许通过debugfs注入插头事件;
  • jack_debugfs_root:jack_kctl debugfs根目录;

1.4 关系图

为了更加清晰的了解struct snd_jack 、struct snd_kcontrol、struct snd_jack_kctl 等数据结构的关系,我们绘制了如下关系图:

其中我们比较关注的点是:

  • jack是挂在snd_card成员devices链表下面的一个snd_device;
  • snd_jack中的字段:kctl_list,该链表保存所有的jack kcontrol,每个jack kcontrol都关联一个kcontrol;
  • snd_jack中的字段:input_dev是一个input设备,用于上报耳机插入/拔出、麦克风插入/拔出等事件。

二、ALSA核心API

2.1 创建Jack设备

jack设备的创建可以通过snd_jack_new函数来完成,函数接收6个参数:

  • card:ALSA声卡设备;
  • id:jack设备唯一标识;
  • type:由枚举类型snd_jack_type的位掩码组成,表示该jack能够上报的类型;这里我们可以理解为jack能够检测的类型,比如耳机插入/拔出;
  • jack:jack指针的指针,用于保存分配的snd_jack;
  • initial_kctl: 如果为真,则创建一个jack kcontrol并将其添加到jack的kctl_list链表中;
  • phantom_jack:对于虚拟jack,不创建输入设备;

函数定义在sound/core/jack.c;

复制代码
/**
 * snd_jack_new - Create a new jack
 * @card:  the card instance
 * @id:    an identifying string for this jack
 * @type:  a bitmask of enum snd_jack_type values that can be detected by
 *         this jack
 * @jjack: Used to provide the allocated jack object to the caller.
 * @initial_kctl: if true, create a kcontrol and add it to the jack list.
 * @phantom_jack: Don't create a input device for phantom jacks.
 *
 * Creates a new jack object.
 *
 * Return: Zero if successful, or a negative error code on failure.
 * On success @jjack will be initialised.
 */
int snd_jack_new(struct snd_card *card, const char *id, int type,
                 struct snd_jack **jjack, bool initial_kctl, bool phantom_jack)
{
        struct snd_jack *jack;
        struct snd_jack_kctl *jack_kctl = NULL;
        int err;
        static const struct snd_device_ops ops = {     // jack设备操作集
                .dev_free = snd_jack_dev_free,
#ifdef CONFIG_SND_JACK_INPUT_DEV  // 这个宏,默认是配置的
                .dev_register = snd_jack_dev_register,
                .dev_disconnect = snd_jack_dev_disconnect,
#endif /* CONFIG_SND_JACK_INPUT_DEV */
        };

        if (initial_kctl) {  // 如果为真,动态创建一个jack kcontrol ,包含一个名称为id的kcontrol
                jack_kctl = snd_jack_kctl_new(card, id, type); // jack kcontrol的mask字段设置为type
                if (!jack_kctl)
                        return -ENOMEM;
        }

        jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);  // 动态申请内存,申请一个snd_jack数据结构
        if (jack == NULL)
                return -ENOMEM;

        jack->id = kstrdup(id, GFP_KERNEL); // 复制id,并返回
        if (jack->id == NULL) {
                kfree(jack);
                return -ENOMEM;
        }
#ifdef CONFIG_SND_JACK_INPUT_DEV
        mutex_init(&jack->input_dev_lock); // 获取互斥锁

        /* don't create input device for phantom jack */
        if (!phantom_jack) {  // false时,创建输入设备
                int i;

                jack->input_dev = input_allocate_device(); // 动态创建输入设备
                if (jack->input_dev == NULL) {
                        err = -ENOMEM;
                        goto fail_input;
                }

                jack->input_dev->phys = "ALSA";

                jack->type = type;  // 初始化jack类型

                for (i = 0; i < SND_JACK_SWITCH_TYPES; i++) // 6
                        if (type & (1 << i))
                                input_set_capability(jack->input_dev, EV_SW, // 设置输入设备可以上报哪些输入事件 EV_SW代表开关事件
                                                     jack_switch_types[i]);   // 事件编码

        }
#endif /* CONFIG_SND_JACK_INPUT_DEV */
    //  创建一个新的snd_device实例,并添加到声卡设备的devices链表中
        err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);  // device_data设置为jack
        if (err < 0)
                goto fail_input;

        jack->card = card;  // 设置声卡设备
        INIT_LIST_HEAD(&jack->kctl_list); // 初始化链表节点

        if (initial_kctl)  // 如果为真,将jack kcontrol添加到jack的kctl_list链表中
                snd_jack_kctl_add(jack, jack_kctl);

        *jjack = jack;  // 写回

        return 0;

fail_input:
#ifdef CONFIG_SND_JACK_INPUT_DEV
        input_free_device(jack->input_dev);
#endif
        kfree(jack->id);
        kfree(jack);
        return err;
}
复制代码

jack_switch_types是全局数组,存放的是EV_SW类型事件的编码,长度为6,定义在sound/core/jack.c:

static const int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
        SW_HEADPHONE_INSERT,     // 0x02
        SW_MICROPHONE_INSERT,    // 0x04  
        SW_LINEOUT_INSERT,       // 0x06 
        SW_JACK_PHYSICAL_INSERT, // 0x07
        SW_VIDEOOUT_INSERT,      // 0x80
        SW_LINEIN_INSERT,        // 0x0d
};
2.1.1 snd_jack_kctl_new

snd_jack_kctl_new定义在sound/core/jack.c,用于动态创建一个jack kcontrol ,包含一个名称为name的kcontrol;函数参数包括:

  • card:指向snd_card结构的指针,即指向ALSA中的声卡设备;
  • name:jack kcontrol 中包含的kcontrol的名称;
  • mask:新创建的jack kcontrtol可以上报的类型,其值取自enum snd_jack_type;比如我们设置了mask值为SND_JACK_HEADPHONE,当耳机插入或者拔出的时候HP_DET引脚就会接收到中断,中断处理程序读取引脚电平,并通过input设备上报SND_JACK_HEADPHONE对应的EV_SK事件,事件编码为0x02,值为插入/拔出;

 函数定义如下:

复制代码
static struct snd_jack_kctl * snd_jack_kctl_new(struct snd_card *card, const char *name, unsigned int mask)
{
        struct snd_kcontrol *kctl;
        struct snd_jack_kctl *jack_kctl;
        int err;

        kctl = snd_kctl_jack_new(name, card); // 动态创建一个kcontrol
        if (!kctl)
                return NULL;

        err = snd_ctl_add(card, kctl); // 将kcontrol添加到声卡card的controls链表
        if (err < 0)
                return NULL;

        jack_kctl = kzalloc(sizeof(*jack_kctl), GFP_KERNEL); // 动态创建snd_jack_kctl

        if (!jack_kctl)
                goto error;

        jack_kctl->kctl = kctl;       // 初始化成员,设置关联的kcontrol
        jack_kctl->mask_bits = mask;  // 设置需要上报的事件

        kctl->private_data = jack_kctl;  // 设置私有数据
        kctl->private_free = snd_jack_kctl_private_free;

        return jack_kctl;
error:
        snd_ctl_free_one(kctl);
        return NULL;
}
复制代码

其中snd_kctl_jack_new定义在sound/core/ctljack.c,用于动态创建一个kcontrol;

复制代码
struct snd_kcontrol *
snd_kctl_jack_new(const char *name, struct snd_card *card)
{
        struct snd_kcontrol *kctl;

        kctl = snd_ctl_new1(&jack_detect_kctl, NULL);  // 会分配一个kcontrol,并把kcontrol模板中相应的值复制到该kcontrol中
        if (!kctl)
                return NULL;

        jack_kctl_name_gen(kctl->id.name, name, sizeof(kctl->id.name));
        kctl->id.index = get_available_index(card, kctl->id.name);
        kctl->private_value = 0;   // 保存kcontrol的状态
        return kctl;
}
复制代码

jack_detect_kctl是一个全局变量,为struct snd_kcontrol_new类型,即kcontrol模板:

static const struct snd_kcontrol_new jack_detect_kctl = {
        /* name is filled later */
        .iface = SNDRV_CTL_ELEM_IFACE_CARD,
        .access = SNDRV_CTL_ELEM_ACCESS_READ,
        .info = jack_detect_kctl_info,
        .get = jack_detect_kctl_get,
};
2.1.2 snd_jack_kctl_add

snd_jack_kctl_add定义在sound/core/jack.c,用于将jack kcontrol添加到jack的kctl_list链表中;

static void snd_jack_kctl_add(struct snd_jack *jack, struct snd_jack_kctl *jack_kctl)
{
        jack_kctl->jack = jack;  // 设置所属jack
        list_add_tail(&jack_kctl->list, &jack->kctl_list); // 添加到jack的kctl_list链表中
        snd_jack_debugfs_add_inject_node(jack, jack_kctl);
}

2.2 snd_jack_add_new_kctl

snd_jack_add_new_kctl函数定义在sound/soc/jack.c,这个函数实际上就是对 snd_jack_kctl_new和snd_jack_kctl_add函数做了一层包装,用于创建一个新的snd_jack_kctl,并将其添加到jack;

复制代码
/**
 * snd_jack_add_new_kctl - Create a new snd_jack_kctl and add it to jack
 * @jack:  the jack instance which the kctl will attaching to
 * @name:  the name for the snd_kcontrol object
 * @mask:  a bitmask of enum snd_jack_type values that can be detected
 *         by this snd_jack_kctl object.
 *
 * Creates a new snd_kcontrol object and adds it to the jack kctl_list.
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask)
{
        struct snd_jack_kctl *jack_kctl;

        jack_kctl = snd_jack_kctl_new(jack->card, name, mask);  // 动态创建一个jack kcontrol ,包含一个名称为name的kcontrol
        if (!jack_kctl)
                return -ENOMEM;

        snd_jack_kctl_add(jack, jack_kctl);  // 用于将jack kcontrol添加到jack的kctl_list链表中;
        return 0;
}
View Code
复制代码

2.3 snd_jack_dev_register

在注册声卡设备card时会遍历声卡设备的逻辑设备链表devices,并调用声卡逻辑设备操作集中的dev_register函数,对于jack设备也就是snd_jack_dev_register函数。

snd_jack_dev_register函数定义在sound/core/jack.c:

复制代码
static int snd_jack_dev_register(struct snd_device *device)
{
        struct snd_jack *jack = device->device_data;  // 获取jack
        struct snd_card *card = device->card;    // 获取声卡
        int err, i;

        snprintf(jack->name, sizeof(jack->name), "%s %s",  // 设置jack名称 
                 card->shortname, jack->id);

        mutex_lock(&jack->input_dev_lock);  // 获取互斥锁
        if (!jack->input_dev) {
                mutex_unlock(&jack->input_dev_lock);
                return 0;
        }

        jack->input_dev->name = jack->name;  // 设置intput设备的名称

        /* Default to the sound card device. */
        if (!jack->input_dev->dev.parent)
                jack->input_dev->dev.parent = snd_card_get_device_link(card);  // 设置输入设备的父设备

        /* Add capabilities for any keys that are enabled */
        for (i = 0; i < ARRAY_SIZE(jack->key); i++) { // 设置jack可以上报的EV_KEY类型的事件,并将事件编码保存在jack->key[i]中
                int testbit = SND_JACK_BTN_0 >> i;  // 0x4000 >> i  

                if (!(jack->type & testbit)) // 如果已经设置了上报BTN_x类型,则不会进入
                        continue;

                if (!jack->key[i])
                        jack->key[i] = BTN_0 + i;  //  转换为EV_KEY类型事件的编码

                input_set_capability(jack->input_dev, EV_KEY, jack->key[i]); // 设置输入设备可以上报哪些输入事件 EV_KEY代表按键事件
        }

        err = input_register_device(jack->input_dev);   // 注册输入设备,会在/sys/class/input创建input%d文件
        if (err == 0)
                jack->registered = 1;  // 注册标志位

        mutex_unlock(&jack->input_dev_lock);   // 释放互斥锁
        return err;
}
复制代码

2.4 snd_jack_report

snd_jack_report定义在sound/core/jack.c,用于向输入子系统上报jack的插拔状态;函数内部根据jack的上报类型调用input_report_key/input_report_switch来向上层汇报EV_KEY、EV_SE事件;

复制代码
/**
 * snd_jack_report - Report the current status of a jack
 * Note: This function uses mutexes and should be called from a
 * context which can sleep (such as a workqueue).
 *
 * @jack:   The jack to report status for
 * @status: The current status of the jack
 */
void snd_jack_report(struct snd_jack *jack, int status)
{
        struct snd_jack_kctl *jack_kctl;
        unsigned int mask_bits = 0;
#ifdef CONFIG_SND_JACK_INPUT_DEV
        int i;
#endif

        if (!jack)
                return;

        jack->hw_status_cache = status;   // 缓存状态

        list_for_each_entry(jack_kctl, &jack->kctl_list, list) // 遍历jack kctl_list链表中的jack kcontrol,赋值给jack_kctl
                if (jack_kctl->sw_inject_enable)   // 这个应该没有设置
                        mask_bits |= jack_kctl->mask_bits;
                else
                        snd_kctl_jack_report(jack->card, jack_kctl->kctl,
                                             status & jack_kctl->mask_bits); // 需要设置的kcontrol的状态

#ifdef CONFIG_SND_JACK_INPUT_DEV
        mutex_lock(&jack->input_dev_lock);    // 获取互斥锁
        if (!jack->input_dev) {
                mutex_unlock(&jack->input_dev_lock);
                return;
        }

        for (i = 0; i < ARRAY_SIZE(jack->key); i++) {  // 遍历EV_KEY事件编码
                int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);   // 0x4000 >> i  ....

                if (jack->type & testbit)   // 如果已经设置了上报BTN_x类型
                        input_report_key(jack->input_dev, jack->key[i],  //上报EV_KEY类型,事件编码为jack->key[i],值为status & testbit
                                         status & testbit);
        }

        for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {  // 遍历EV_SW事件编码
                int testbit = ((1 << i) & ~mask_bits);  

                if (jack->type & testbit)    // 如果已经设置了上报SND_JACK_HEADPHONE等类型
                        input_report_switch(jack->input_dev,    //上报EV_SW类型,事件编码为jack_switch_types[i],值为status & testbit
                                            jack_switch_types[i],
                                            status & testbit);
        }

        input_sync(jack->input_dev);
        mutex_unlock(&jack->input_dev_lock);  // 释放互斥锁
#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
复制代码

其中函数snd_kctl_jack_report定义在sound/core/ctljack.c,用于发送kcontrol状态变更通知;

void snd_kctl_jack_report(struct snd_card *card,
                          struct snd_kcontrol *kctl, bool status)
{
        if (kctl->private_value == status)  // 状态没有发生改变
                return;
        kctl->private_value = status;  // 更新kcontrol的状态
        snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
}

函数调用了snd_ctl_notify,其第2个参数为事件掩码(event-mask),第3个参数为该通知的kcontrol元素id指针;如下代码定义的事件掩码SNDRV_CTL_EVENT_MASK_VALUE意味着kcontrol值的改变被通知;

三、ASoC核心数据结构

3.1 struct snd_soc_jack 

在ASoC中使用struct snd_soc_jack来描述jack,并提供了对其状态、引脚等进行管理和通知的功能,定义在include/sound/soc-jack.h:

复制代码
struct snd_soc_jack {
        struct mutex mutex;
        struct snd_jack *jack;
        struct snd_soc_card *card;
        struct list_head pins;
        int status;
        struct blocking_notifier_head notifier;
        struct list_head jack_zones;
};
复制代码

其中:

  • mutex:一个互斥锁,用于保护对当前结构体的并发访问;
  • jack:指向snd_jack结构的指针,即指向ALSA中的jack;
  • card:指向snd_soc_card结构的指针,表示ASoC声卡设备;
  • pins:保存jack pin信息的链表,每一个元素都是struct snd_soc_jack_pin;
  • status:表示该jack的插拔状态;
  • notifier:一个阻塞通知器头,用于在jack状态发生变化时通知关联的观察者;
  • jack_zones:用于保存该jack的区域信息的链表头;

3.2 struct snd_soc_jack_gpio

在ASoC中使用struct snd_soc_jack_gpio来描述用于描述声音插孔检测时使用的GPIO引脚的属性和配置,包括GPIO编号、报告值、消抖时间等;

复制代码
/**
 * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
 *
 * @gpio:         legacy gpio number
 * @idx:          gpio descriptor index within the function of the GPIO
 *                consumer device
 * @gpiod_dev:    GPIO consumer device
 * @name:         gpio name. Also as connection ID for the GPIO consumer
 *                device function name lookup
 * @report:       value to report when jack detected
 * @invert:       report presence in low state
 * @debounce_time: debounce time in ms
 * @wake:         enable as wake source
 * @jack_status_check: callback function which overrides the detection
 *                     to provide more complex checks (eg, reading an
 *                     ADC).
 */
struct snd_soc_jack_gpio {
        unsigned int gpio;
        unsigned int idx;
        struct device *gpiod_dev;
        const char *name;
        int report;
        int invert;
        int debounce_time;
        bool wake;

        /* private: */
        struct snd_soc_jack *jack;
        struct delayed_work work;
        struct notifier_block pm_notifier;
        struct gpio_desc *desc;

        void *data;
        /* public: */
        int (*jack_status_check)(void *data);
};
复制代码

该结构体包含以下成员:

  • gpio:GPIO编号,全局唯一;
  • idx:表示GPIO描述符在GPIO消费者设备的功能中的索引;
  • gpiod_dev:指向GPIO消费者设备的指针;
  • name:GPIO的名称,也用作GPIO消费者设备函数名称查找的连接ID;
  • report:GPIO可以上报的类型,比如耳机插入/拔出对应的值为SND_JACK_HEADPHONE;
  • invert:反转的意思,正常情况下是GPIO高电平认为插孔有设备插入,如果设置了invert,则GPIO为低电平认为插孔有设备插入;
  • debounce_time:消抖时间,以毫秒为单位;
  • wake:是否将该GPIO引脚启用为唤醒源;
  • jack:指向snd_soc_jack结构的指针,表即ASoC的jack;
  • work:延迟工作,用于处理插孔检测的延迟处理;
  • pm_notifier:电源管理通知器块,用于处理系统电源事件;
  • desc:指向GPIO描述符的指针;
  • data:指向任意数据的指针,用于存储私有数据;
  • jack_status_check:回调函数,用于覆盖检测过程,提供更复杂的检测方法(例如,读取ADC);

3.3 struct snd_soc_jack_pin

struct snd_soc_jack_pin定义在include/sound/soc-jack.h,用于描述在插孔检测时要进行上下电操作的widget;

复制代码
/**
 * struct snd_soc_jack_pin - Describes a pin to update based on jack detection
 *
 * @pin:    name of the pin to update
 * @mask:   bits to check for in reported jack status
 * @invert: if non-zero then pin is enabled when status is not reported
 * @list:   internal list entry
 */
struct snd_soc_jack_pin {
        struct list_head list;
        const char *pin;
        int mask;
        bool invert;
};
复制代码

结构体成员包括:

  • pin:引脚的名称,实际上就是widget的名字;在设置引脚状态时,会到声卡widgets链表查找名称为pin的widget,然后对其进行上下电操作;
  • mask:当前jack pin可以上报的类型,其值取自enum snd_jack_type;
  • invert:在设置引脚状态时默认0:下电,1:上电;如果设置了invert非0,则表示1:下电,0:上电;
  • list:链表节点,用于将该节点添加到ASoC Jack的pins链表中;

四、ASoC核心API

4.1 snd_soc_card_jack_new_pins

snd_soc_card_jack_new_pins函数定义在sound/soc/soc-card.c,用于在ASoC声卡中创建一个带有pin的jack。该函数的参数包括:

  • card:指向ASoC声卡结构的指针;
  • id:用于标识此jack的字符串;
  • type:一个位掩码,表示可以由此插孔检测到的snd_jack_type枚举值;
  • jack:指向ASoC jack结构的指针;
  • pins:要添加到ASoC jack的引脚数组,或者为NULL;
  • num_pins:pins数组中元素的数量。

函数定义如下:

复制代码
/**
 * snd_soc_card_jack_new_pins - Create a new jack with pins
 * @card:  ASoC card
 * @id:    an identifying string for this jack
 * @type:  a bitmask of enum snd_jack_type values that can be detected by
 *         this jack
 * @jack:  structure to use for the jack
 * @pins:  Array of jack pins to be added to the jack or NULL
 * @num_pins: Number of elements in the @pins array
 *
 * Creates a new jack object with pins. If not adding pins,
 * snd_soc_card_jack_new() should be used instead.
 *
 * Returns zero if successful, or a negative error code on failure.
 * On success jack will be initialised.
 */
int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id,
                               int type, struct snd_soc_jack *jack, // ASoC jack
                               struct snd_soc_jack_pin *pins,   // jack引脚
                               unsigned int num_pins)      // jack引脚数目
{
        int ret;

        ret = jack_new(card, id, type, jack, false);  // 创建一个新的snd_jack实例,赋值给jack->jack
        if (ret)
                goto end;

        if (num_pins)
                ret = snd_soc_jack_add_pins(jack, num_pins, pins);  // 为每个jack pin创建一个新的snd_jack_kctl,并将其添加到jack->jack的kctl_list中。
end:
        return soc_card_ret(card, ret);
}
复制代码

该函数内部首先调用jack_new函数创建一个新的snd_jack实例赋值给jack->jack,并将jack其与ASoC声卡设备关联起来。

然后,如果提供了引脚,就调用snd_soc_jack_add_pins为每个jack pin创建一个新的snd_jack_kctl,并将其添加到jack->jack的kctl_list中。

4.1.1 jack_new

jack_new定义如下:

复制代码
static int jack_new(struct snd_soc_card *card, const char *id, int type,
                    struct snd_soc_jack *jack, bool initial_kctl)
{
        mutex_init(&jack->mutex);         // 初始化互斥锁
        jack->card = card;                // 设置ASoC声卡设备
        INIT_LIST_HEAD(&jack->pins);      // 初始化链表节点
        INIT_LIST_HEAD(&jack->jack_zones);  // 初始化链表节点
        BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);

        return snd_jack_new(card->snd_card, id, type, &jack->jack, initial_kctl, false);  // 创建一个snd_jack设备,赋值给jack->jack
}
复制代码
4.1.2 snd_soc_jack_add_pins

snd_soc_jack_add_pins函数定义在sound/soc/soc-jack.c,用于为每个jack pin创建一个新的snd_jack_kctl,并将其添加到jack的kctl_list中,函数定义如下:

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(694)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示