一.前言
在alsa架构中,当应用程序调用open、read、write时,将调用驱动程序中的相应接口,本篇博客就从驱动程序中的snd_fops结构体开始说起。
二. 入口函数alsa_sound_init
kernel/linux-3.4.2/sound/core/Sound.c
1. snd_fops结构体
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open,
.llseek = noop_llseek,
};
static int __init alsa_sound_init(void)
{
snd_major = major;
snd_ecards_limit = cards_limit;
if (register_chrdev(major, "alsa", &snd_fops)) {
snd_printk(KERN_ERR "unable to register native major device number %d
", major);
}
snp_fops结构体最终通过register_chrdev函数注册到系统中。从这个地方可以看出alsa驱动也是字符设备驱动。
在snd_fops中只有open函数,并没有读写函数。可以猜测的出来,这个snd_fops中的open函数只是起到一个中转的作用,它肯定会找到一个新的file_operation结构体。
static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
const struct file_operations *old_fops;
//以次设备号minor在数组snd_minors中找到一项。
mptr = snd_minors[minor];
old_fops = file->f_op;
//取出file_operation结构体
file->f_op = fops_get(mptr->f_ops);
if (file->f_op->open) {
err = file->f_op->open(inode, file);
}
2.数组snd_minors数组的设置
/**
* snd_register_device_for_dev - Register the ALSA device file for the card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
* @card: the card instance
* @dev: the device index
* @f_ops: the file operations
* @private_data: user pointer for f_ops->open()
* @name: the device file name
* @device: the &struct device to link this new device to
*
* Registers an ALSA device file for the given card.
* The operators have to be set in reg parameter.
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
void *private_data,
const char *name, struct device *device)
{
int minor;
struct snd_minor *preg;
preg = kmalloc(sizeof *preg, GFP_KERNEL);
preg->type = type;
preg->card = card ? card->number : -1;
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
#ifdef CONFIG_SND_DYNAMIC_MINORS
minor = snd_find_free_minor(type);
#else
minor = snd_kernel_minor(type, card, dev);
#endif
snd_minors[minor] = preg;
preg->dev = device_create(sound_class, device, MKDEV(major, minor),
private_data, "%s", name);
return 0;
}
3.函数snd_register_device_for_dev
函数snd_register_device_for_dev在两个地方被调用:
1)snd_register_device:声卡设备的控制接口
2)snd_pcm_dev_register:声卡设备的数据接口
3.1 snd_register_device
kernel/linux-3.4.2/include/sound/Core.h
static inline int snd_register_device(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
void *private_data,
const char *name)
{
return snd_register_device_for_dev(type, card, dev, f_ops,
private_data, name,
snd_card_get_device_link(card));
}
3.1.1函数snd_register_device
kernel/linux-3.4.2/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;
int err, cardnum;
char name[16];
cardnum = card->number;
sprintf(name, "controlC%i", cardnum);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, name)) < 0)
return err;
return 0;
}
3.1.2函数snd_ctl_dev_register
/*
* 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,
};
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
3.1.3 snd_ctl_create
/**
* snd_card_create - create and initialize a soundcard structure
* @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.
*
* Returns zero if successful or a negative error code.
*/
int snd_card_create(int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret)
{
struct snd_card *card;
card->number = idx;
card->module = module;
/* 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);
}
3.1.4 函数snd_card_create
3.2 函数snd_pcm_dev_register
kernel/linux-3.4.2/sound/core/Pcm.c
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,
};
}
3.2.1 函数_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().
*
* Returns 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);
}
3.2.2函数snd_pcm_new
有很多文件调用了snd_pcm_new接口,对应不同的声卡。某个声卡驱动程序中调用了snd_pcm_new接口。