ALSA驱动框架——从程序的角度分析

 

本节博客开始介绍,alsa声卡的软件框架。

1.入口函数alsa_sound_init

kernel/linux-3.4.2/sound/core/Sound.c

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\n", major);
}

在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又是在哪被调用的呢?

ALSA驱动框架——从程序的角度分析

 

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调用的呢?

ALSA驱动框架——从程序的角度分析

有很多文件调用了snd_pcm_new接口,对应不同的声卡。某个声卡驱动程序中调用了snd_pcm_new接口。

 

 



上一篇:ALSA-hda开发笔记


下一篇:Linux ALSA