开启成长之旅!这是我参与「日新方案 2 月更文挑战」的第 2 天,点击检查活动详情

1、struct snd_card

1.1、snd_card是啥

  snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开端于该结构,简直一切与声响相关的逻辑设备都是在snd_card的办理之下,声卡驱动的第一个动作通常就是创立一个snd_card结构体。因此咱们也从 struct snd_card的讲解开端。

1.2、snd_card界说

struct snd_card {
    int number;			            /* number of soundcard (index to snd_cards) */
    char id[16];			        /* id string of this card */
    char driver[16];		        /* driver name */
    char shortname[32];		        /* short name of this soundcard */
    char longname[80];		        /* name of this soundcard */
    char irq_descr[32];		        /* Interrupt description */
    char mixername[80];		        /* mixer name */
    char components[128];		    /* card components delimited with space */
    struct module *module;		    /* top-level module */
    void *private_data;		        /* private data for soundcard */
    void (*private_free) (struct snd_card *card); /* callback for freeing of
                                                            private data */
    struct list_head devices;	    /* devices */
    struct device ctl_dev;		    /* control device */
    unsigned int last_numid;	    /* last used numeric ID */
    struct rw_semaphore controls_rwsem;	/* controls list lock */
    rwlock_t ctl_files_rwlock;	    /* ctl_files list lock */
    int controls_count;		        /* count of all controls */
    size_t user_ctl_alloc_size;	// current memory allocation by user controls.
    struct list_head controls;	    /* all controls for this card */
    struct list_head ctl_files;	    /* active control files */
    struct snd_info_entry *proc_root;	/* root for soundcard specific files */
    struct proc_dir_entry *proc_root_link;	/* number link to real id */
    struct list_head files_list;	/* all files associated to this card */
    struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown
                                                            state */
    spinlock_t files_lock;		    /* lock the files for this card */
    int shutdown;			        /* this card is going down */
    struct completion *release_completion;
    struct device *dev;		        /* device assigned to this card */
    struct device card_dev;		    /* cardX object for sysfs */
    const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
    bool registered;		        /* card_dev is registered? */
    bool managed;			        /* managed via devres */
    bool releasing;			        /* during card free process */
    int sync_irq;			        /* assigned irq, used for PCM sync */
    wait_queue_head_t remove_sleep;
    size_t total_pcm_alloc_bytes;	/* total amount of allocated buffers */
    struct mutex memory_mutex;	    /* protection for the above */
#ifdef CONFIG_SND_DEBUG
    struct dentry *debugfs_root;    /* debugfs root for card */
#endif
#ifdef CONFIG_PM
    unsigned int power_state;	    /* power state */
    atomic_t power_ref;
    wait_queue_head_t power_sleep;
    wait_queue_head_t power_ref_sleep;
#endif
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
    struct snd_mixer_oss *mixer_oss;
    int mixer_oss_change_count;
#endif
};

  关于每个声卡,都需求有一个snd_card结构体来描绘。它记载着声卡的信息并办理声卡的一切设备。其中几个比较重要的成员:

  int number声卡的序号,通常为0。

  struct list_head devices 记载该声卡下一切逻辑设备的链表

  struct list_head controls 记载该声卡下一切的操控单元的链表。

  void *private_data 声卡的私有数据,可以在创立声卡时通过参数指定数据的大小。

  bool registered 声卡是否在系统中注册了。

2、声卡创立流程

2.1、创立一个card实例

struct snd_card *card;
snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);

2.2、创立声卡的芯片专用数据

  设置该声卡的一些资源信息,例如:中止、IO、DMA等。有两种方式进行创立。

2.2.1、作为声卡的private_data

  在创立声卡的时分传入外部数据长度。

struct mychip {
    struct snd_card *card;
    ....
};
err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
                     sizeof(struct mychip), &card);
struct mychip *chip = card->private_data;
chip->card = card;

2.2.2、作为声卡的一个子设备

  在snd_device_new中指定extra_size为0。

static int snd_mychip_dev_free(struct snd_device *device)
{
    return snd_mychip_free(device->device_data);
}
struct snd_card *card;
struct mychip *chip;
err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,0, &card);
.....
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
chip->card = card;
static struct snd_device_ops ops = {
    .dev_free = snd_mychip_dev_free,
};
....
snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

  声卡注销时,会调用snd_mychip_dev_free, 主动开释内存。

  snd_device_new不会给芯片专用数据device_data分配空间,因此在调用之前,必须为芯片专用分配空间,在ops的dev_free中界说析构函数对芯片专用数据进行析构。dev_free会在调用snd_card_free时主动调用。关于用户自界说的 device、type可以使用SNDRV_DEV_LOWLEVEL。

  snd_mychip_dev_free() 是用来free前面kzmalloc的空间。

2.3、设置驱动ID和姓名

    strcpy(card->driver, “My Chip”);
    strcpy(card->shortname, “My Own Chip 123”);
    sprintf(card->longname, “%s at 0x%lx irq %i”,card->shortname, chip->ioport, chip->irq)
或
    strncpy(card->driver, shortname, sizeof(card->driver));
    strncpy(card->shortname, shortname, sizeof(card->shortname));
    strncpy(card->longname, longname, sizeof(card->longname));

2.4、创立声卡功用逻辑部件,如PCM,mixer, MIDI

  每一种部件的创立终究会调用snd_device_new()来生成一个snd_device实例,并把该实例链接到snd_card的devices链表中。通常,alsa-driver的现已提供了一些常用的部件的创立函数,而不用直接调用snd_device_new(),比如: snd_pcm_new()。

2.5、注册声卡

/* register it */
err = snd_card_register(card);
if (err < 0) {
    pk_error("failed to register pc-midi sound card: error %d\n", err);
    goto fail_register;
}

3、snd_card_new函数详解

  用于创立并初始化一个声卡的结构体

/**
 *  snd_card_new - create and initialize a soundcard structure
 *  @parent: the parent device object
 *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
 *  @xid: card identification (ASCII string)
 *  @module: top level module for locking
 *  @extra_size: allocate this extra size after the main soundcard structure
 *  @card_ret: the pointer to store the created card instance
 *
 *  The function allocates snd_card instance via kzalloc with the given
 *  space for the driver to use freely.  The allocated struct is stored
 *  in the given card_ret pointer.
 *
 *  Return: Zero if successful or a negative error code.
 */
int snd_card_new(struct device *parent, int idx, const char *xid,
		    struct module *module, int extra_size,
		    struct snd_card **card_ret)
{
    struct snd_card *card;
    int err;
    if (snd_BUG_ON(!card_ret))
        return -EINVAL;
    *card_ret = NULL;
    if (extra_size < 0)
        extra_size = 0;
    /* 1. 分配snd_card和private_data的空间
       在snd_card后面的空间分配,card->private_data指向该空间
    */
    card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
    if (!card)
        return -ENOMEM;
    err = snd_card_init(card, parent, idx, xid, module, extra_size);
    if (err < 0) {
        kfree(card);
        return err;
    }
    *card_ret = card;
    return 0;
}
static int snd_card_init(struct snd_card *card, struct device *parent,
			 int idx, const char *xid, struct module *module,
			 size_t extra_size)
{
    int err;
#ifdef CONFIG_SND_DEBUG
    char name[8];
#endif
    /* 1、依据传入的参数赋值xid, idx, module, parent */
    /* (1). 为 card->private_datad 赋值 */
	if (extra_size > 0)
            card->private_data = (char *)card + sizeof(struct snd_card);
    /* (2). 为 card->id 赋值 */
    if (xid)
        strscpy(card->id, xid, sizeof(card->id));
    err = 0;
    mutex_lock(&snd_card_mutex);
    if (idx < 0) /* first check the matching module-name slot */
        idx = get_slot_from_bitmask(idx, module_slot_match, module);
    if (idx < 0) /* if not matched, assign an empty slot */
        idx = get_slot_from_bitmask(idx, check_empty_slot, module);
    if (idx < 0)
        err = -ENODEV;
    else if (idx < snd_ecards_limit) {
        if (test_bit(idx, snd_cards_lock))
            err = -EBUSY;	/* invalid */
    } else if (idx >= SNDRV_CARDS)
        err = -ENODEV;
    if (err < 0) {
        mutex_unlock(&snd_card_mutex);
        dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n", idx, snd_ecards_limit - 1, err);
        return err;
    }
    set_bit(idx, snd_cards_lock);		/* lock it */
    if (idx >= snd_ecards_limit)
        snd_ecards_limit = idx + 1; /* increase the limit */
    mutex_unlock(&snd_card_mutex);
    /* (3). 赋值parent */
    card->dev = parent;
    /* (4). 分配snd_card的序号 */
    card->number = idx;
#ifdef MODULE
    WARN_ON(!module);
    /* (5). 赋值module */
    card->module = module;
#endif
/* 2、初始化结构体和变量 */
    INIT_LIST_HEAD(&card->devices);
    init_rwsem(&card->controls_rwsem);
    rwlock_init(&card->ctl_files_rwlock);
    INIT_LIST_HEAD(&card->controls);
    INIT_LIST_HEAD(&card->ctl_files);
    spin_lock_init(&card->files_lock);
    INIT_LIST_HEAD(&card->files_list);
    mutex_init(&card->memory_mutex);
#ifdef CONFIG_PM
    init_waitqueue_head(&card->power_sleep);
    init_waitqueue_head(&card->power_ref_sleep);
    atomic_set(&card->power_ref, 0);
#endif
    init_waitqueue_head(&card->remove_sleep);
    card->sync_irq = -1;
/* 设置设备文件节点的姓名 */
    device_initialize(&card->card_dev);
    card->card_dev.parent = parent;
    card->card_dev.class = sound_class;
    card->card_dev.release = release_card_device;
    card->card_dev.groups = card->dev_groups;
    card->dev_groups[0] = &card_dev_attr_group;
    err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
    if (err < 0)
        goto __error;
    snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s", dev_driver_string(card->dev), dev_name(&card->card_dev));
    /* the control interface cannot be accessed from the user space until */
    /* snd_cards_bitmask and snd_cards are set with snd_card_register */
    /* 创立一个control设备 */
    err = snd_ctl_create(card);
    if (err < 0) {
        dev_err(parent, "unable to register control minors\n");
        goto __error;
    }
    /* 生成声卡的proc文件 */
    err = snd_info_card_create(card);
    if (err < 0) {
        dev_err(parent, "unable to create card info\n");
        goto __error_ctl;
    }
#ifdef CONFIG_SND_DEBUG
    sprintf(name, "card%d", idx);
    card->debugfs_root = debugfs_create_dir(name, sound_debugfs_root);
#endif
    return 0;
__error_ctl:
    snd_device_free_all(card);
__error:
    put_device(&card->card_dev);
    return err;
}

  这函数会履行如下操作:

    1、分配snd_card和private_data的空间。

    2、初始化结构体、变量、创立control设备、生成声卡的proc文件等全部都在snd_card_init函数中完结。

    3、获取private_data的地址等。

    4、sound_class会在init_soundcore中做初始化操作。

static int __init init_soundcore(void)
{
    int rc;
    rc = init_oss_soundcore();
    if (rc)
        return rc;
    sound_class = class_create(THIS_MODULE, "sound");
    if (IS_ERR(sound_class)) {
        cleanup_oss_soundcore();
        return PTR_ERR(sound_class);
    }
    sound_class->devnode = sound_devnode;
    return 0;
}

    5、创立card的control设备。依据注释control接口在snd_card_register之后,用户空间才可以访问。

    6、调用snd_info_card_create函数在proc下创立card0目录,最終会依据entry的mode,创立目录。

/*
 * create a card proc file
 * called from init.c
 */
int snd_info_card_create(struct snd_card *card)
{
    char str[8];
    struct snd_info_entry *entry;
    if (snd_BUG_ON(!card))
        return -ENXIO;
    sprintf(str, "card%i", card->number);
    entry = create_subdir(card->module, str);
    if (!entry)
        return -ENOMEM;
    card->proc_root = entry;
    return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
}

4、snd_card_register函数详解

  用来注册声卡,主要完结了如下操作:

    1、创立声卡的设备节点。

    2、注册一切的逻辑设备。

    3、增加当前的声卡到声卡数组。

    4、注册声卡的proc文件

/**
 *  snd_card_register - register the soundcard
 *  @card: soundcard structure
 *
 *  This function registers all the devices assigned to the soundcard.
 *  Until calling this, the ALSA control interface is blocked from the
 *  external accesses.  Thus, you should call this function at the end
 *  of the initialization of the card.
 *
 *  Return: Zero otherwise a negative error code if the registration failed.
 */
int snd_card_register(struct snd_card *card)
{
    int err;
    /* 合法性判别,如果此处card不存在,panic。 */
    if (snd_BUG_ON(!card))
        return -EINVAL;
    /* 1、依据card的registered判别是否现已注册,如果注册持续。否则调用device_add增加设备,设置registered标志。创立声卡的sysfs设备节点。其中card->card_dev在创立声卡结构体的时分被赋值。
       card->card_dev.class = sound_class;
       sound_class在sound模块被加载的时分创立
       设备节点:/dev/snd/cartd%i
    */
    if (!card->registered) {
        err = device_add(&card->card_dev);
        if (err < 0)
            return err;
        card->registered = true;
    } else {
        if (card->managed)
            devm_remove_action(card->dev, trigger_card_free, card);
    }
    if (card->managed) {
        err = devm_add_action(card->dev, trigger_card_free, card);
        if (err < 0)
            return err;
    }
    /* 2、调用snd_device_register_all注册一切card的设备,包括pcm, control等 */
    err = snd_device_register_all(card);
    if (err < 0)
        return err;
    mutex_lock(&snd_card_mutex);
    /* 3、增加当前的声卡到声卡数组 */
    if (snd_cards[card->number]) {
        /* already registered */
        mutex_unlock(&snd_card_mutex);
        return snd_info_card_register(card); /* register pending info */
    }
    if (*card->id) {
        /* make a unique id name from the given string */
        char tmpid[sizeof(card->id)];
        memcpy(tmpid, card->id, sizeof(card->id));
        snd_card_set_id_no_lock(card, tmpid, tmpid);
    } else {
        /* create an id from either shortname or longname */
        const char *src;
        src = *card->shortname ? card->shortname : card->longname;
        snd_card_set_id_no_lock(card, src,retrieve_id_from_card_name(src));
    }
    snd_cards[card->number] = card;
    mutex_unlock(&snd_card_mutex);
    /* 4、注册声卡的proc文件 */
    err = snd_info_card_register(card);
    if (err < 0)
        return err;
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
    if (snd_mixer_oss_notify_callback)
        snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
    return 0;
}
/*
 * register all the devices on the card.
 * called from init.c
 */
int snd_device_register_all(struct snd_card *card)
{
    struct snd_device *dev;
    int err;
    if (snd_BUG_ON(!card))
        return -ENXIO;
    /* 遍历注册一切的snd_device,调用__snd_device_register函数完结注册 */
    list_for_each_entry(dev, &card->devices, list) {
        err = __snd_device_register(dev);
        if (err < 0)
            return err;
    }
    return 0;
}
static int __snd_device_register(struct snd_device *dev)
{
    if (dev->state == SNDRV_DEV_BUILD) {
        if (dev->ops->dev_register) {
            int err = dev->ops->dev_register(dev);
            if (err < 0)
                return err;
        }
        dev->state = SNDRV_DEV_REGISTERED;
    }
    return 0;
}

  此函数终究会调用各个devices的snd_device_ops中的dev_register函数。

  声卡注册完结之后,声卡的软件逻辑结果如下:

Linux ALSA驱动之二:声卡的创建流程