alsa子系统 <一>

入口函数
static int __init alsa_sound_init(void)
{
    //116
    snd_major = major;
    snd_ecards_limit = cards_limit;
    /**
        注册一个字符设备,主设备号为116.
        这里设计的和input.c(input子系统的核心层)类似。
        snd_fops中只提供了一个open函数,
        在input子系统中 也是只提供了一个open函数。(input_open()函数的具体实现请见我以前的博文分析)
        在分析完成snd_open()之后,会比较他们之间的一些差异。
    */  
    if (register_chrdev(major,"alsa",&snd_fops))
    {
        snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
        return -EIO;
    }
...
    return 0;
}

static const struct file_operations snd_fops =
{
    .owner =    THIS_MODULE,
    .open =     snd_open,
    .llseek =   noop_llseek,
};

/**
    这个函数的实现和input_open()的实现在逻辑上及其类似。
    这种在注册的时候,根据次设备号填充到数组中,在open的时候根据次设备号从数组中取出来
    在input子系统、LCD子系统、v4l2子系统中都大量的使用。
*/
static int snd_open(struct inode *inode, struct file *file)
{
    /**
        获取次设备号,是要根据这里获取到的次设备号从数组中取出对应的snd_minor 
        那么,这里的次设备号就是snd_minor[]被填充的索引值,
        这一操作是再snd_register_device_for_dev()函数中 实现的,下面会有分析。
    */
    unsigned int minor = iminor(inode);
    struct snd_minor *mptr = NULL;

    const struct file_operations *old_fops;
    int err = 0;

    /**
        static struct snd_minor *snd_minors[256];
        看获取到的次设备号是否大于256。
    */
    if (minor >= ARRAY_SIZE(snd_minors))
    {
        return -ENODEV;
    }   
    mutex_lock(&sound_mutex);
    /**
        根据上面获取到的次设备号,从snd_minors[]中取出对应的snd_minor 
        那么,要问了,snd_minors[]是在什么时候被填充的?接着往下看
    */
    mptr = snd_minors[minor];

    if (mptr == NULL) 
    {
        mptr = autoload_device(minor);
        if (!mptr) {
            mutex_unlock(&sound_mutex);
            return -ENODEV;
        }
    }
    old_fops = file->f_op;
    /**
        将打开文件的file_operation被赋予从数组中获取的那个snd_minor的file_operations
        问题 : 
                那么,这里替换打开文件的fops,这个fops是什么。
        这个问题也是在下面的一个函数snd_register_device_for_dev()中来实现的。
        这里替换了打开文件的fops,以后的函数 使用file->f_op中定义的函数,就是snd_pcm_f_ops中提供的函数了
        这一技巧,在内核大多数open函数中都有使用
    */
    file->f_op = fops_get(mptr->f_ops);
    if (file->f_op == NULL) 
    {
        file->f_op = old_fops;
        err = -ENODEV;
    }
    mutex_unlock(&sound_mutex);
    if (err < 0)
        return err;

    /**
        如果从数组中取出的这个snd_minor 提供了file_operations,并且在file_operations中
        提供了open函数,那么就调用它的open函数
    */
    if (file->f_op->open) 
    {
        /**
            调用open函数
        */
        err = file->f_op->open(inode, file);
        if (err) {
            fops_put(file->f_op);
            file->f_op = fops_get(old_fops);
        }
    }
    fops_put(old_fops);
    return err;
}
现在的问题是: snd_minors[]数组在什么时候被填充
/**
    看第三个参数  f_ops
    [0] :播放
    [1] :录音 
        const struct file_operations snd_pcm_f_ops[2] = {
    {                                               
        .owner =        THIS_MODULE,
        .write =        snd_pcm_write,
        .aio_write =        snd_pcm_aio_write,
        .open =         snd_pcm_playback_open,
        .release =      snd_pcm_release,
        .llseek =       no_llseek,
        .poll =         snd_pcm_playback_poll,
        .unlocked_ioctl =   snd_pcm_playback_ioctl,
        .compat_ioctl =     snd_pcm_ioctl_compat,
        .mmap =         snd_pcm_mmap,
        .fasync =       snd_pcm_fasync,
        ...
    },
    {
        .owner =        THIS_MODULE,
        .read =         snd_pcm_read,
        .aio_read =     snd_pcm_aio_read,
        .open =         snd_pcm_capture_open,
        .release =      snd_pcm_release,
        .llseek =       no_llseek,
        .poll =         snd_pcm_capture_poll,
        .unlocked_ioctl =   snd_pcm_capture_ioctl,
        .compat_ioctl =     snd_pcm_ioctl_compat,
        .mmap =         snd_pcm_mmap,
        .fasync =       snd_pcm_fasync,
        .get_unmapped_area =    snd_pcm_get_unmapped_area,
    }
};

在看一下它的使用 
            err = snd_register_device_for_dev(devtype, pcm->card,
                          pcm->device,
                          &snd_pcm_f_ops[cidx],
                          pcm, str, dev);

    在这个函数中有下面一句代码:
                          preg->f_ops = f_ops;      
    所以,这个过程 : snd_pcm_f_ops 作为snd_register_device_for_dev的一个参数被传递
    进来,最终被赋值给了 snd_minors[minor]->f_ops中,
    那么,snd_pcm_f_ops中的这些指针函数什么时候被使用呢?   下面有分析                            

*/
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;

    //必需提供name参数
    if (snd_BUG_ON(!name))
    {
        return -EINVAL;
    }
    /**
        为snd_minor分配内存空间
    */
    preg = kmalloc(sizeof(*preg),GFP_KERNEL);
    if (preg == NULL)
    {
        return -ENOMEM;
    }
    /**
        赋值初始化 snd_minor
    */

    preg->type = type;

    preg->card = card ? card->number : -1;

    preg->device = dev;

    preg->f_ops = f_ops;  //看这个传入的参数已经赋值给了snd_minors->f_ops了

    preg->private_data = private_data;

    mutex_lock(&sound_mutex);
#indef CONFIG_SND_DYNAMIC_MINORS;
    minor = snd_find_free_minor();
#else
    /**
        静态 分配次设备号,这个函数在下面进行分析
    */
    minor = snd_kernel_minor(type,card,dev);
    /**

        检查分配的minor,snd_minors[minor]是否为空缺
        为下面将snd_minors放入奥snd_minors[]中做准备
    */
    if (minor >= 0 && snd_minors[minor])
    {
        minor = -EBUSY;
    }
#endif
    if (minor < 0)
    {
        mutex_unlock(&sound_mutex);
        kfree(preg);
        return minor;
    }
    /**
        #define SNDRV_OS_MINORS  256

        static struct snd_minor *snd_minors[SNDRV_OS_MINORS];

        将该snd_minor放到全局数组snd_minors[]中,位置是minor为索引值
        问题:
            snd_minors[]一定会在以后的其他函数中根据minor为索引值取出来,(如:上面的额open函数)

            这个指针数组的设计在内核的其他子系统中都有,注册的时候往里放,open或者其他函数从里面依据minor取出来
            lcd子系统中的fb_info、input子系统中的input_table、v4l2的video_device 都是这样的设计
            既然是共享的资源,那么一定要使用互斥机制来保护这些临界区,发现他们都是使用的互斥锁来保护的。
    */
    snd_minors[minor] = preg;
    /**
        创建设备文件节点
        那么,对应的class_create()创建类是再什么时候被调用的?
    */
    preg->dev = device_create(sound_class,device,MKDEV(major,minor)
                private_data,"%s",name);

    if (IS_ERR(preg->dev)) //出错的情况下,该释放的释放,该置空的置空
    {
        snd_minors[minor] = NULL;
        mutex_unlock(&sound_mutex);
        minor = PTR_ERR(preg->dev);
        kfree(preg);

        return minor;
    }
    mutex_unlock(&soun_mutex);

    return 0;
}
那么是谁调用了snd_register_device_for_dev()这个函数呢,源头在哪里?

调用流程图
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值