一:初始化/注册声卡设备
(1)注册ALSA
kernel\sound\core:sound.cint __init alsa_sound_init(void)
{... ...if (register_chrdev(major, "alsa", &snd_fops)) ... ...
}static const struct file_operations snd_fops =
{.owner = THIS_MODULE,.open = snd_open,.llseek = noop_llseek,
};static int snd_open(struct inode *inode, struct file *file)
{unsigned int minor = iminor(inode);struct snd_minor *mptr = NULL;const struct file_operations *new_fops;mptr = snd_minors[minor];... ...new_fops = fops_get(mptr->f_ops);... ...
}static struct snd_minor *snd_minors[SNDRV_OS_MINORS];struct snd_minor {int type; /* SNDRV_DEVICE_TYPE_XXX */int card; /* card number */int device; /* device number */const struct file_operations *f_ops; /* file operations */void *private_data; /* private data for f_ops->open */struct device *dev; /* device for sysfs */struct snd_card *card_ptr; /* assigned card instance */
};
通过追踪代码可以发现声卡初始化中最终调用的file_operations对象是通过snd_minors[minor]结构成员获取。继续追踪snd_minors[minor]来源。
int snd_register_device(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data, struct device *device)
{int minor;int err = 0;struct snd_minor *preg;if (snd_BUG_ON(!device))return -EINVAL;preg = kmalloc(sizeof *preg, GFP_KERNEL);if (preg == NULL)return -ENOMEM;preg->type = type;preg->card = card ? card->number : -1;preg->device = dev;preg->f_ops = f_ops;preg->private_data = private_data;preg->card_ptr = card;mutex_lock(&sound_mutex);minor = snd_find_free_minor(type, card, dev);if (minor < 0) {err = minor;goto error;}preg->dev = device;device->devt = MKDEV(major, minor);err = device_add(device);if (err < 0)goto error;snd_minors[minor] = preg;error:mutex_unlock(&sound_mutex);if (err < 0)kfree(preg);return err;
}
可以看到在snd_register_device声卡注册接口中对snd_minor对象preg的成员分别进行了赋值(设备类型,设备号,file_operations等等)。最后将其赋值给需要用到的snd_minors[minor]。后续我们就需要用snd_register_device接口来分别对不同设备进行注册,最终会通过总线将它们匹配起来。
(2)ALSA接口信息
kernel\sound\core:sound.cint __init alsa_sound_init(void)
{... ...if (snd_info_init() < 0) { ... ...
}kernel\sound\core:info.cint __init snd_info_init(void)
{snd_proc_root = snd_info_create_entry("asound", NULL);if (!snd_proc_root)return -ENOMEM;snd_proc_root->mode = S_IFDIR | 0555;snd_proc_root->p = proc_mkdir("asound", NULL);if (!snd_proc_root->p)goto error;... ...if (snd_info_version_init() < 0 ||snd_minor_info_init() < 0 ||snd_minor_info_oss_init() < 0 ||snd_card_info_init() < 0 ||snd_info_minor_register() < 0)goto error;return 0;error:snd_info_free_entry(snd_proc_root);return -ENOMEM;
}
同样在声卡入口函数中对ALSA的接口信息进行了初始化,并通过“proc_mkdir”内核函数在根目录proc下创建了“asound”目录作为存储路径,见下图:
card0/card7:其中0和7代表的是声卡号。其子目录包含了pcmp,pcmc
该节点是由snd_card_new接口调用创建,详细见后续对于snd_card_new的分析。
cards:列出系统中可用的,注册的声卡。
devices:列出系统card下所有注册的device,包括control,pcm,timer,seq等等。
pcm:系统的pcm设备,包括capture和playback。
timers: 描述一些ALSA相关的定时器信息。
version: 描述ALSA版本信息,详细可追踪接口snd_info_version_init
二:注册声卡功能部件
(1)control
kernel\sound\core:control.c/** registration of the control device*/
static int snd_ctl_dev_register(struct snd_device *device)
{struct snd_card *card = device->device_data;return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, &card->ctl_dev);
}/** create control core:* called from init.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,.dev_disconnect = snd_ctl_dev_disconnect,};int err;if (snd_BUG_ON(!card))return -ENXIO;if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))return -ENXIO;snd_device_initialize(&card->ctl_dev, card);dev_set_name(&card->ctl_dev, "controlC%d", card->number);err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);if (err < 0)put_device(&card->ctl_dev);return err;
}
可以看到ctl部件通过snd_ctl_create函数中的snd_device_ops成员对象间接调用snd_ctl_dev_register-->>snd_register_device接口来注册相应接口。
下面逐一分析snd_ctl_create的内容。
(1.1)snd_device_initialize
kernel\sound\core:init.c/*** snd_device_initialize - Initialize struct device for sound devices* @dev: device to initialize* @card: card to assign, optional*/
void snd_device_initialize(struct device *dev, struct snd_card *card)
{device_initialize(dev);if (card)dev->parent = &card->card_dev;dev->class = sound_class;dev->release = default_release;
}
EXPORT_SYMBOL_GPL(snd_device_initialize);
device_initialize(dev)接口用来准备后续用到的device数据,重点在于sound_class
kernel\sound:sound_core.cstruct class *sound_class;
EXPORT_SYMBOL(sound_class);static char *sound_devnode(struct device *dev, umode_t *mode)
{if (MAJOR(dev->devt) == SOUND_MAJOR)return NULL;return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
}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;
}subsys_initcall(init_soundcore);
分析sound_core.c文件可知最终会创建“snd/%s”在“/dev”目录下,“%s”为要注册的功能部件,后续会讲到。
(1.2)dev_set_name(&card->ctl_dev, "controlC%d", card->number)
kernel\drivers\base: core.c/*** dev_set_name - set a device name* @dev: device* @fmt: format string for the device's name*/
int dev_set_name(struct device *dev, const char *fmt, ...)
{va_list vargs;int err;va_start(vargs, fmt);err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);va_end(vargs);return err;
}
EXPORT_SYMBOL_GPL(dev_set_name);
分析dev_set_name可了解到该函数用来设置声卡功能部件的名称“controlC%d”
(1.3)snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops)
kernel\sound\core:device.c/*** snd_device_new - create an ALSA device component* @card: the card instance* @type: the device type, SNDRV_DEV_XXX* @device_data: the data pointer of this device* @ops: the operator table** Creates a new device component for the given data pointer.* The device will be assigned to the card and managed together* by the card.** The data pointer plays a role as the identifier, too, so the* pointer address must be unique and unchanged.** Return: Zero if successful, or a negative error code on failure.*/
int snd_device_new(struct snd_card *card, enum snd_device_type type,void *device_data, struct snd_device_ops *ops)
{struct snd_device *dev;struct list_head *p;if (snd_BUG_ON(!card || !device_data || !ops))return -ENXIO;dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return -ENOMEM;INIT_LIST_HEAD(&dev->list);dev->card = card;dev->type = type;dev->state = SNDRV_DEV_BUILD;dev->device_data = device_data;dev->ops = ops;/* insert the entry in an incrementally sorted list */list_for_each_prev(p, &card->devices) {struct snd_device *pdev = list_entry(p, struct snd_device, list);if ((unsigned int)pdev->type <= (unsigned int)type)break;}list_add(&dev->list, p);return 0;
}
EXPORT_SYMBOL(snd_device_new);
最终通过该接口完成声卡功能部件ctl节点的创建,如下图所示:
继续追踪会发现snd_ctl_create接口是由snd_card_new调用,其中还涉及开篇讲到的card info的创建snd_info_card_create。
kernel\sound\core:init.c/*** 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** Creates and initializes a soundcard structure.** 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;... ...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 */err = snd_ctl_create(card);if (err < 0) {dev_err(parent, "unable to register control minors\n");goto __error;}err = snd_info_card_create(card);if (err < 0) {dev_err(parent, "unable to create card info\n");goto __error_ctl;}*card_ret = card;return 0;__error_ctl:snd_device_free_all(card);__error:put_device(&card->card_dev);return err;
}
EXPORT_SYMBOL(snd_card_new);
综上,后续对于ctl声卡部件的创建,我们直接调用snd_card_new即可。
(2)pcm
kernel\sound\core:pcm.cstatic int snd_pcm_dev_register(struct snd_device *device)
{struct snd_pcm *pcm;pcm = device->device_data;mutex_lock(®ister_mutex);err = snd_pcm_add(pcm);if (err)goto unlock;for (cidx = 0; cidx < 2; cidx++) {... .../* register pcm */err = snd_register_device(devtype, pcm->card, pcm->device,&snd_pcm_f_ops[cidx], pcm,&pcm->streams[cidx].dev);if (err < 0) {list_del_init(&pcm->list);goto unlock;}for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)snd_pcm_timer_init(substream);}pcm_call_notify(pcm, n_register);unlock:mutex_unlock(®ister_mutex);return err;
}static int _snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, bool internal,struct snd_pcm **rpcm)
{struct snd_pcm *pcm;int err;static struct snd_device_ops ops = {.dev_free = snd_pcm_dev_free,.dev_register = snd_pcm_dev_register,.dev_disconnect = snd_pcm_dev_disconnect,};static struct snd_device_ops internal_ops = {.dev_free = snd_pcm_dev_free,};if (snd_BUG_ON(!card))return -ENXIO;if (rpcm)*rpcm = NULL;pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);if (!pcm)return -ENOMEM;pcm->card = card;pcm->device = device;pcm->internal = internal;mutex_init(&pcm->open_mutex);init_waitqueue_head(&pcm->open_wait);INIT_LIST_HEAD(&pcm->list);if (id)strlcpy(pcm->id, id, sizeof(pcm->id));err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,playback_count);if (err < 0)goto free_pcm;err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);if (err < 0)goto free_pcm;err = snd_device_new(card, SNDRV_DEV_PCM, pcm,internal ? &internal_ops : &ops);if (err < 0)goto free_pcm;if (rpcm)*rpcm = pcm;return 0;free_pcm:snd_pcm_free(pcm);return err;
}
可以看到与ctl部件一致,pcm也是通过“_snd_pcm_new”接口函数的成员对象snd_device_ops来间接调用。阅读代码可知道设备的节点"pcmC%iD%i%c"是在snd_pcm_new_stream中创建。
/*** snd_pcm_new_stream - create a new PCM stream* @pcm: the pcm instance* @stream: the stream direction, SNDRV_PCM_STREAM_XXX* @substream_count: the number of substreams** Creates a new stream for the pcm.* The corresponding stream on the pcm must have been empty before* calling this, i.e. zero must be given to the argument of* snd_pcm_new().** Return: Zero if successful, or a negative error code on failure.*/
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{int idx, err;struct snd_pcm_str *pstr = &pcm->streams[stream];struct snd_pcm_substream *substream, *prev;... ...snd_device_initialize(&pstr->dev, pcm->card);pstr->dev.groups = pcm_dev_attr_groups;dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');... ...
}
EXPORT_SYMBOL(snd_pcm_new_stream);
最终声卡pcm部件的创建是由接口snd_pcm_new来完成。
/*** snd_pcm_new - create a new PCM instance* @card: the card instance* @id: the id string* @device: the device index (zero based)* @playback_count: the number of substreams for playback* @capture_count: the number of substreams for capture* @rpcm: the pointer to store the new pcm instance** Creates a new PCM instance.** The pcm operators have to be set afterwards to the new instance* via snd_pcm_set_ops().** Return: Zero if successful, or a negative error code on failure.*/
int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, struct snd_pcm **rpcm)
{return _snd_pcm_new(card, id, device, playback_count, capture_count,false, rpcm);
}
EXPORT_SYMBOL(snd_pcm_new);
(3)其他
timer, seq于ctl和pmc类似不再缀叙。