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

xiaoxiao2021-02-28  111

设备号是在驱动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_t MAJOR(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 MKDEV from 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()是老的接口,这一步在新的接口中并不需要。

#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 -l total 0 crw-rw---- 1 system audio 14, 12 2017-08-29 09:59 adsp crw-rw---- 1 system audio 14, 4 2017-08-29 09:59 audio crw-rw---- 1 system audio 116, 40 2017-08-29 09:59 comprC0D23 crw-rw---- 1 system audio 116, 2 2017-08-29 09:59 controlC0 crw-rw---- 1 system audio 14, 3 2017-08-29 09:59 dsp crw-rw---- 1 system audio 14, 0 2017-08-29 09:59 mixer crw-rw---- 1 system audio 116, 3 2017-08-29 09:59 pcmC0D0p crw-rw---- 1 system audio 116, 19 2017-08-29 09:59 pcmC0D10p crw-rw---- 1 system audio 116, 20 2017-08-29 09:59 pcmC0D11p crw-rw---- 1 system audio 116, 21 2017-08-29 09:59 pcmC0D12c crw-rw---- 1 system audio 116, 22 2017-08-29 09:59 pcmC0D13c crw-rw---- 1 system audio 116, 23 2017-08-29 09:59 pcmC0D14p crw-rw---- 1 system audio 116, 24 2017-08-29 09:59 pcmC0D15c crw-rw---- 1 system audio 116, 25 2017-08-29 09:59 pcmC0D16c crw-rw---- 1 system audio 116, 27 2017-08-29 09:59 pcmC0D17c crw-rw---- 1 system audio 116, 26 2017-08-29 09:59 pcmC0D17p crw-rw---- 1 system audio 116, 28 2017-08-29 09:59 pcmC0D18p crw-rw---- 1 system audio 116, 29 2017-08-29 09:59 pcmC0D19p crw-rw---- 1 system audio 116, 4 2017-08-29 09:59 pcmC0D1c crw-rw---- 1 system audio 116, 30 2017-08-29 09:59 pcmC0D20p crw-rw---- 1 system audio 116, 31 2017-08-29 09:59 pcmC0D21p crw-rw---- 1 system audio 116, 34 2017-08-29 09:59 pcmC0D22c crw-rw---- 1 system audio 116, 35 2017-08-29 09:59 pcmC0D24p crw-rw---- 1 system audio 116, 37 2017-08-29 09:59 pcmC0D25c crw-rw---- 1 system audio 116, 36 2017-08-29 09:59 pcmC0D25p crw-rw---- 1 system audio 116, 39 2017-08-29 09:59 pcmC0D26c crw-rw---- 1 system audio 116, 38 2017-08-29 09:59 pcmC0D26p crw-rw---- 1 system audio 116, 6 2017-08-29 09:59 pcmC0D2c crw-rw---- 1 system audio 116, 5 2017-08-29 09:59 pcmC0D2p crw-rw---- 1 system audio 116, 8 2017-08-29 09:59 pcmC0D3c crw-rw---- 1 system audio 116, 7 2017-08-29 09:59 pcmC0D3p crw-rw---- 1 system audio 116, 10 2017-08-29 09:59 pcmC0D4c crw-rw---- 1 system audio 116, 9 2017-08-29 09:59 pcmC0D4p crw-rw---- 1 system audio 116, 12 2017-08-29 09:59 pcmC0D5c crw-rw---- 1 system audio 116, 11 2017-08-29 09:59 pcmC0D5p crw-rw---- 1 system audio 116, 14 2017-08-29 09:59 pcmC0D6c crw-rw---- 1 system audio 116, 13 2017-08-29 09:59 pcmC0D6p crw-rw---- 1 system audio 116, 16 2017-08-29 09:59 pcmC0D7c crw-rw---- 1 system audio 116, 15 2017-08-29 09:59 pcmC0D7p crw-rw---- 1 system audio 116, 17 2017-08-29 09:59 pcmC0D8p crw-rw---- 1 system audio 116, 18 2017-08-29 09:59 pcmC0D9c crw-rw---- 1 system audio 116, 1 2017-08-29 09:59 seq crw-rw---- 1 system audio 14, 1 2017-08-29 09:59 sequencer crw-rw---- 1 system audio 14, 8 2017-08-29 09:59 sequencer2 crw-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_class static 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/pcmCxxDxxc 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); 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 -l total 0 lrwxrwxrwx 1 root root 0 2017-09-01 15:44 adsp -> ../../devices/platform/soc-audio/sound/card0/adsp lrwxrwxrwx 1 root root 0 2017-09-01 15:44 audio -> ../../devices/platform/soc-audio/sound/card0/audio lrwxrwxrwx 1 root root 0 2017-09-01 15:44 card0 -> ../../devices/platform/soc-audio/sound/card0 lrwxrwxrwx 1 root root 0 2017-09-01 15:44 comprC0D23 -> ../../devices/platform/soc-audio/sound/card0/comprC0D23 lrwxrwxrwx 1 root root 0 2017-09-01 15:44 controlC0 -> ../../devices/platform/soc-audio/sound/card0/controlC0 lrwxrwxrwx 1 root root 0 2017-09-01 15:44 dsp -> ../../devices/platform/soc-audio/sound/card0/dsp lrwxrwxrwx 1 root root 0 2017-09-01 15:44 mixer -> ../../devices/platform/soc-audio/sound/card0/mixer lrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D0p -> ../../devices/platform/soc-audio/sound/card0/pcmC0D0p lrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D10p -> ../../devices/platform/soc-audio/sound/card0/pcmC0D10p lrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D11p -> ../../devices/platform/soc-audio/sound/card0/pcmC0D11p lrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D12c -> ../../devices/platform/soc-audio/sound/card0/pcmC0D12c lrwxrwxrwx 1 root root 0 2017-09-01 15:44 pcmC0D13c -> ../../devices/platform/soc-audio/sound/card0/pcmC0D13c lrwxrwxrwx 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

转载请注明原文地址: https://www.6miu.com/read-65652.html

最新回复(0)