Android音频驱动-ASOC之主&从设备号

来源:互联网 发布:英语老师教学软件 编辑:程序博客网 时间:2024/05/20 02:26

设备号是在驱动module中分配并注册的,驱动module拥有这个设备号,而/dev目录下的设备文件是根据这个设备号创建的,
当访问/dev目录下的设备文件时,驱动module就知道,自己该出场服务了(当然是由内核通知)。
在Linux内核看来,主设备号标识设备对应的驱动程序,告诉Linux内核使用哪一个驱动程序为该设备(也就是/dev下的设备文件)服务;
而次设备号则用来标识具体且唯一的某个设备。

一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和次设备号统称为设备号。主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各设备。例如一个嵌入式系统,有两个LED指示灯,LED灯需要独立的打开或者关闭。那么,可以写一个LED灯的字符设备驱动程序,可以将其主设备号注册成5号设备,次设备号分别为1和2。这里,次设备号就分别表示两个LED灯。

设备文件通常都在 /dev 目录下。如:
beyes@linux-beyes:~/C/kernel/memory> ll /dev |more
总计 0
crw-rw—- 1 root uucp 4, 70 04-14 18:16 ttyS6
crw-rw—- 1 root uucp 4, 71 04-14 18:16 ttyS7
crw-rw—- 1 root tty 7, 0 08-08 18:58 vcs
crw-rw—- 1 root tty 7, 1 08-08 18:58 vcs1
crw-rw-rw- 1 root root 1, 7 08-08 18:58 full
crw-rw-rw- 1 root root 1, 3 04-14 18:16 null

如上,前面第一个字符为c 的表示字符设备。在字符设备里,有主设备号和次设备号。如上1,4,7 分别是主设备号,0,1,3,7,70,71都是次设备号。一般的,主设备号标识出与设备关联的设备驱动。如 /dev/null 和 /dev/full 由 1 号驱动来管理,/dev/vcs 和/dev/vcs1由 7 号驱动来管理,/dev/ttyS6 由 4 号驱动来管理。

现在的 Linux 内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。

内核由次设备号确定当前所指向的是哪个设备。根据所编写的驱动程序,可以从内核那里得到一个直接指向设备的指针,或者使用次设备号作为一个设备本地数组的索引。但不论如何,内核自身几乎不知道次设备号的什么事情。

对于Linux系统中,一般字符设备和驱动之间的函数调用关系如下图所示
一般字符设备和驱动之间的函数调用关系
上图描述了用户空间应用程序通过系统调用来调用程序的过程。一般而言在驱动程序的设计中,会关系 struct file 和 struct inode 这两个结构体。
用户空间使用open()系统调用函数打开一个字符设备时( int fd = open(“dev/demo”, O_RDWR) )大致有以下过程:

1、在虚拟文件系统VFS中的查找对应与字符设备对应 struct inode节点2、遍历字符设备列表(chardevs数组),根据inod节点中的 cdev_t设备号找到cdev对象3、创建struct file对象(系统采用一个数组来管理一个进程中的多个被打开的设备,每个文件秒速符作为数组下标标识了一个设备对象)4、初始化struct file对象,将 struct file对象中的 file_operations成员指向 struct cdev对象中的 file_operations成员(file->fops =  cdev->fops)5、回调file->fops->open函数

在文件(sound/core/sound.c)中,ALSA定义了一个字符设备(CONFIG_SND_MAJOR =116),所有AUDIO设备,都是该设备的字设备:
snd_open接口比较重要,snd_open接口通过文件节点inode得到了次设备号,通过snd_minors数组得到对应的声音逻辑设备的文件操作,
调用对应的open接口,并调用fops_put替换成了对应的逻辑设备的文件操作(snd_minors里维护).

<linux/kdev_t.h>//文件提供了处理设备号相关的宏:MKDEV(major, minor)  //  设备的dev_tMAJOR(dev_t)                     //  主设备号MINOR(dev_t)                     //  次设备号//需要注意的是这三个宏在在内核和用户态有不同的解释,下面截取kdev_t.h的相关片段:#ifndef _LINUX_KDEV_T_H#define _LINUX_KDEV_T_H#ifdef __KERNEL__#define MINORBITS     20#define MINORMASK     ((1U << MINORBITS) - 1)#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))//...........................................#else /* __KERNEL__ *//*Some programs want their definitions of MAJOR and MINOR and MKDEVfrom the kernel sources. These must be the externally visible ones.*/#define MAJOR(dev) ((dev)>>8)#define MINOR(dev) ((dev) & 0xff)#define MKDEV(ma,mi) ((ma)<<8 | (mi))#endif /* __KERNEL__ */#endif//可以看到如果定义了__KERNEL__宏,主次设备号占据的位数不同。
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;    int err = 0;    mptr = snd_minors[minor];//通过minor取得从设备    new_fops = fops_get(mptr->f_ops);    replace_fops(file, new_fops);    if (file->f_op->open)        err = file->f_op->open(inode, file);    return err;}static inline unsigned iminor(const struct inode *inode){    return MINOR(inode->i_rdev);}//注册alsa主设备,static int major = CONFIG_SND_MAJOR;static int __init alsa_sound_init(void){    snd_major = major;    snd_ecards_limit = cards_limit;    if (register_chrdev(major, "alsa", &snd_fops)) {//注册声卡主设备,major为主设备号        pr_err("ALSA core: unable to register native major device number %d\n", major);        return -EIO;    }    if (snd_info_init() < 0) {        unregister_chrdev(major, "alsa");        return -ENOMEM;    }    snd_info_minor_register();    return 0;}

register_chrdev( )申请指定的设备号,并且将其注册到字符设备驱动模型中.
  它所做的事情为:
1、注册设备号, 通过调用 __register_chrdev_region() 来实现
2、分配一个cdev, 通过调用 cdev_alloc() 来实现
3、将cdev添加到驱动模型中, 这一步将设备号和驱动关联了起来. 通过调用 cdev_add() 来实现
4、将第一步中创建的 struct char_device_struct 对象的 cdev 指向第二步中分配的cdev. 由于register_chrdev()是老的接口,这一步在新的接口中并不需要。
chrdevs数组

#define CHRDEV_MAJOR_HASH_SIZE 255  static DEFINE_MUTEX(chrdevs_lock);static struct char_device_struct {    struct char_device_struct *next; // 结构体指针    unsigned int major;              // 主设备号    unsigned int baseminor;          // 次设备起始号    int minorct;                     // 次备号个数    char name[64];    struct cdev *cdev; /* will die */  } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];// 只能挂255个字符主设备#include <linux.fs.h>int register_chardev (unsigned int major, const char *name, struct file_operations *fops)说明:register_chrdev 是注册设备驱动程序的内核函数。参数:     major 主设备号,该值为 0 时,自动运行分配。而实际值不是 0 。    name 设备名称;    fops file_operations 结构体变量地址(指针)。返回值:major 值为 0 ,正常注册后,返回分配的主设备号。如果分配失败,返回 EBUSY 的负值 ( -EBUSY ) 。major 值若大于 linux/major.h (2.4内核)中声明的最大值 (#define MAX_CHRDEV      255) ,则返回EINVAL 的负值 (-EINVAL) 。指定 major 值后,若有注册的设备,返回 EBUSY 的负值 (-EBUSY)。若正常注册,则返回 0 值。register_chrdev      __register_chrdev(major, 0, 256, name, fops);            struct char_device_struct *cd;            cd = __register_chrdev_region(major, baseminor, count, name);                    cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);                    //如果主设备号是0的话,就会从 char_device_struct数组中选择一个没有分配的char_device_struct结构体                    //如果找到的话,就以其下标作为主设备号,否则出错返回                    cd->major = major;                    cd->baseminor = baseminor;//第一个次设备号                    cd->minorct = minorct;          //此设备号的个数                    strlcpy(cd->name, name, sizeof(cd->name));//设置名字                    i = major_to_index(major);                          return major % CHRDEV_MAJOR_HASH_SIZE;//只有当指定的主设备号大于255时,i才会不等于major            //下面的工作比较难懂,可以参考注释1            cdev = cdev_alloc(); //分配字符设备结构体            cdev->owner = fops->owner; //设置字符设备结构体            cdev->ops = fops;            kobject_set_name(&cdev->kobj, "%s", name);            //向上注册,这里用到了cd->major和baseminor            err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);

Android N:/ # cat proc/devices
Character devices:
1 mem
2 pty
3 ttyp
5 /dev/tty
5 /dev/console
5 /dev/ptmx
10 misc
13 input
14 sound
29 fb
90 mtd
108 ppp
116 alsa //alsa主设备
128 ptm
136 pts
153 mtk_wmt_wifi_chrdev

Block devices:
259 blkext
7 loop
8 sd
31 mtdblock
65 sd

static int snd_pcm_dev_register(struct snd_device *device){        err = snd_register_device_for_dev(devtype, pcm->card,                          pcm->device,                          &snd_pcm_f_ops[cidx],                          pcm, str, dev);}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;//playback or capture    preg->card = card ? card->number : -1;    preg->device = dev;    preg->f_ops = f_ops;//文件操作函数,用来操作/dev/snd/pcmCxxDxxp和/dev/snd/pcmCxxDxxc    preg->private_data = private_data;//保存pcm对象    preg->card_ptr = card;    minor = snd_kernel_minor(type, card, dev);    snd_minors[minor] = preg;//使用全局变量保存pcm设备的上下文    //创建pcm的设备节点/dev/snd/pcmCxxDxxp和/dev/snd/pcmCxxDxxc,其中major为主设备号,minor为从设备号    preg->dev = device_create(sound_class, device, MKDEV(major, minor), private_data, "%s", name);    return 0;}static int snd_kernel_minor(int type, struct snd_card *card, int dev){    int minor;    switch (type) {    case SNDRV_DEVICE_TYPE_SEQUENCER:    case SNDRV_DEVICE_TYPE_TIMER:        minor = type;        break;    case SNDRV_DEVICE_TYPE_CONTROL:        if (snd_BUG_ON(!card))            return -EINVAL;        minor = SNDRV_MINOR(card->number, type);        break;    case SNDRV_DEVICE_TYPE_HWDEP:    case SNDRV_DEVICE_TYPE_RAWMIDI:    case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:    case SNDRV_DEVICE_TYPE_PCM_CAPTURE:    case SNDRV_DEVICE_TYPE_COMPRESS:        if (snd_BUG_ON(!card))            return -EINVAL;        minor = SNDRV_MINOR(card->number, type + dev);//生成minor,从设备号        break;    default:        return -EINVAL;    }    if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))        return -EINVAL;    return minor;}#define SNDRV_MINOR(card, dev)      (((card) << 5) | (dev))
Android N:/dev/snd # ls -ltotal 0crw-rw---- 1 system audio  14,  12 2017-08-29 09:59 adspcrw-rw---- 1 system audio  14,   4 2017-08-29 09:59 audiocrw-rw---- 1 system audio 116,  40 2017-08-29 09:59 comprC0D23crw-rw---- 1 system audio 116,   2 2017-08-29 09:59 controlC0crw-rw---- 1 system audio  14,   3 2017-08-29 09:59 dspcrw-rw---- 1 system audio  14,   0 2017-08-29 09:59 mixercrw-rw---- 1 system audio 116,   3 2017-08-29 09:59 pcmC0D0pcrw-rw---- 1 system audio 116,  19 2017-08-29 09:59 pcmC0D10pcrw-rw---- 1 system audio 116,  20 2017-08-29 09:59 pcmC0D11pcrw-rw---- 1 system audio 116,  21 2017-08-29 09:59 pcmC0D12ccrw-rw---- 1 system audio 116,  22 2017-08-29 09:59 pcmC0D13ccrw-rw---- 1 system audio 116,  23 2017-08-29 09:59 pcmC0D14pcrw-rw---- 1 system audio 116,  24 2017-08-29 09:59 pcmC0D15ccrw-rw---- 1 system audio 116,  25 2017-08-29 09:59 pcmC0D16ccrw-rw---- 1 system audio 116,  27 2017-08-29 09:59 pcmC0D17ccrw-rw---- 1 system audio 116,  26 2017-08-29 09:59 pcmC0D17pcrw-rw---- 1 system audio 116,  28 2017-08-29 09:59 pcmC0D18pcrw-rw---- 1 system audio 116,  29 2017-08-29 09:59 pcmC0D19pcrw-rw---- 1 system audio 116,   4 2017-08-29 09:59 pcmC0D1ccrw-rw---- 1 system audio 116,  30 2017-08-29 09:59 pcmC0D20pcrw-rw---- 1 system audio 116,  31 2017-08-29 09:59 pcmC0D21pcrw-rw---- 1 system audio 116,  34 2017-08-29 09:59 pcmC0D22ccrw-rw---- 1 system audio 116,  35 2017-08-29 09:59 pcmC0D24pcrw-rw---- 1 system audio 116,  37 2017-08-29 09:59 pcmC0D25ccrw-rw---- 1 system audio 116,  36 2017-08-29 09:59 pcmC0D25pcrw-rw---- 1 system audio 116,  39 2017-08-29 09:59 pcmC0D26ccrw-rw---- 1 system audio 116,  38 2017-08-29 09:59 pcmC0D26pcrw-rw---- 1 system audio 116,   6 2017-08-29 09:59 pcmC0D2ccrw-rw---- 1 system audio 116,   5 2017-08-29 09:59 pcmC0D2pcrw-rw---- 1 system audio 116,   8 2017-08-29 09:59 pcmC0D3ccrw-rw---- 1 system audio 116,   7 2017-08-29 09:59 pcmC0D3pcrw-rw---- 1 system audio 116,  10 2017-08-29 09:59 pcmC0D4ccrw-rw---- 1 system audio 116,   9 2017-08-29 09:59 pcmC0D4pcrw-rw---- 1 system audio 116,  12 2017-08-29 09:59 pcmC0D5ccrw-rw---- 1 system audio 116,  11 2017-08-29 09:59 pcmC0D5pcrw-rw---- 1 system audio 116,  14 2017-08-29 09:59 pcmC0D6ccrw-rw---- 1 system audio 116,  13 2017-08-29 09:59 pcmC0D6pcrw-rw---- 1 system audio 116,  16 2017-08-29 09:59 pcmC0D7ccrw-rw---- 1 system audio 116,  15 2017-08-29 09:59 pcmC0D7pcrw-rw---- 1 system audio 116,  17 2017-08-29 09:59 pcmC0D8pcrw-rw---- 1 system audio 116,  18 2017-08-29 09:59 pcmC0D9ccrw-rw---- 1 system audio 116,   1 2017-08-29 09:59 seqcrw-rw---- 1 system audio  14,   1 2017-08-29 09:59 sequencercrw-rw---- 1 system audio  14,   8 2017-08-29 09:59 sequencer2crw-rw---- 1 system audio 116,  33 2017-08-29 09:59 timer

写的一个字符设备驱动为例,在驱动初始化的代码里调用class_create为该设备创建一个class,
再为每个设备调用 device_create创建对应的设备,大致用法如下:
struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);
device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);
这样的module被加载时,就会自动在/dev下创建my_device设备文件。

//第一个参数指定类的所有者是哪个模块,第二个参数指定类名。 struct class *class_create(struct module *owner, const char *name){     struct class *cls;     int retval;     cls = kzalloc(sizeof(*cls), GFP_KERNEL);     if (!cls) {          retval = -ENOMEM;          goto error;     }     cls->name = name;     cls->owner = owner;     cls->class_release = class_create_release;     retval = class_register(cls);     if (retval)          goto error;     return cls;error:     kfree(cls);     return ERR_PTR(retval);}//第一个参数指定所要创建的设备所从属的类,//第二个参数是这个设备的父设备,如果没有就指定为NULL,//第三个参数是设备号,//第四个参数是设备名称,//第五个参数是从设备号。 struct device *device_create(struct class *class, struct device *parent,                        dev_t devt, const char *fmt, ...){     va_list vargs;     struct device *dev;     va_start(vargs, fmt);     dev = device_create_vargs(class, parent, devt, NULL, fmt, vargs);     va_end(vargs);     return dev;}

以创建pcm设备节点为例:

//注册主设备alsa,设置声卡的回调函数static int __init alsa_sound_init(void){    snd_major = major;    snd_ecards_limit = cards_limit;    if (register_chrdev(major, "alsa", &snd_fops)) {        pr_err("ALSA core: unable to register native major device number %d\n", major);        return -EIO;    }    return 0;}//创建类名为sound的sound_classstatic int __init init_soundcore(void){    int rc;    rc = init_oss_soundcore();    sound_class = class_create(THIS_MODULE, "sound");    sound_class->devnode = sound_devnode;    return 0;}//创建pcm的设备节点/dev/snd/pcmCxxDxxp和/dev/snd/pcmCxxDxxcint 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);    minor = snd_kernel_minor(type, card, dev);    snd_minors[minor] = preg;//使用全局变量保存pcm设备的上下文    preg->dev = device_create(sound_class, device, MKDEV(major, minor), private_data, "%s", name);    return 0;}
Android N:/sys/class/sound # ls -ltotal 0lrwxrwxrwx 1 root root 0 2017-09-01 15:44 adsp -> ../../devices/platform/soc-audio/sound/card0/adsplrwxrwxrwx 1 root root 0 2017-09-01 15:44 audio -> ../../devices/platform/soc-audio/sound/card0/audiolrwxrwxrwx 1 root root 0 2017-09-01 15:44 card0 -> ../../devices/platform/soc-audio/sound/card0lrwxrwxrwx 1 root root 0 2017-09-01 15:44 comprC0D23 -> ../../devices/platform/soc-audio/sound/card0/comprC0D23lrwxrwxrwx 1 root root 0 2017-09-01 15:44 controlC0 -> ../../devices/platform/soc-audio/sound/card0/controlC0lrwxrwxrwx 1 root root 0 2017-09-01 15:44 dsp -> ../../devices/platform/soc-audio/sound/card0/dsplrwxrwxrwx 1 root root 0 2017-09-01 15:44 mixer -> ../../devices/platform/soc-audio/sound/card0/mixerlrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D0p -> ../../devices/platform/soc-audio/sound/card0/pcmC0D0plrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D10p -> ../../devices/platform/soc-audio/sound/card0/pcmC0D10plrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D11p -> ../../devices/platform/soc-audio/sound/card0/pcmC0D11plrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D12c -> ../../devices/platform/soc-audio/sound/card0/pcmC0D12clrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D13c -> ../../devices/platform/soc-audio/sound/card0/pcmC0D13clrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D14p -> ../../devices/platform/soc-audio/sound/card0/pcmC0D14p

参考文档
http://www.cnblogs.com/chen-farsight/p/6177870.html

原创粉丝点击