/dev/dsp与alsa框架下设备节点打开和创建简易流程
扫描二维码
随时随地手机看文章
浅析ASoC-audio驱动oss框架下/dev/dsp与alsa框架下设备节点打开和创建简易流程
对于oss设备节点
1. soundcore_fops -- 提供主设备号为14的oss节点open("/dev/dsp")操作soundcore_open,最后将调用snd_pcm_oss_open
2. snd_pcm_oss_f_reg -- 提供最终的file->f_op应用程序调用方法集
对于alsa设备节点
1. snd_fops -- 提供主设备号为116的alsa节点open("/dev/snd/pcmC0D0c")操作snd_open
2. snd_pcm_f_ops[2] -- 提供最终的file->f_op应用程序调用方法集snd_pcm_f_ops[0]用于放音,snd_pcm_f_ops[1]用于录音.
可能后面的流程都是混杂的,不能区分很清楚,所以先来看最直观的oss设备节点"/dev/dsp"打开流程[luther.gliethttp].
static const struct file_operations soundcore_fops=
{
/* We must have an owner or the module locking fails */
.owner = THIS_MODULE,
.open = soundcore_open, // 类似chrdev_open的实现,现在很多集中管理的驱动都这样
}; // 来界定设备[luther.gliethttp].
static const struct file_operations snd_pcm_oss_f_reg =
{
.owner = THIS_MODULE,
.read = snd_pcm_oss_read,
.write = snd_pcm_oss_write,
.open = snd_pcm_oss_open,
.release = snd_pcm_oss_release,
.poll = snd_pcm_oss_poll,
.unlocked_ioctl = snd_pcm_oss_ioctl,
.compat_ioctl = snd_pcm_oss_ioctl_compat,
.mmap = snd_pcm_oss_mmap,
};
我们先来看看打开/dev/dsp字符设备节点的流程[luther.gliethttp].
luther@gliethttp:~$ ll /dev/dsp
crw-rw----+ 1 root audio 14, 3 2009-08-15 14:59 /dev/dsp
module_init(init_soundcore); // 模块人口
static int __init init_soundcore(void)
{
// #define SOUND_MAJOR 14
if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) { // 主设备号为14的所有256个字符设备节点都将调用该方法集
printk(KERN_ERR "soundcore: sound device already in use.n"); // 比如打开/dev/dsp设备,那么将首先执行这里的soundcore_open
return -EBUSY;
}
sound_class = class_create(THIS_MODULE, "sound"); // 创建/sys/class/sound类目录[luther.gliethttp]
if (IS_ERR(sound_class))
return PTR_ERR(sound_class);
return 0;
}
int soundcore_open(struct inode *inode, struct file *file)
{
int unit = iminor(inode); //根据inode节点的minor次设备号锁定声卡设备,对于inode节点的自动创建在后面我们会慢慢谈到[luther.gliethttp].
struct sound_unit *s;
......
/*
* Allocations
*
* 0 *16 Mixers
* 1 *8 Sequencers
* 2 *16 Midi
* 3 *16 DSP
* 4 *16 SunDSP
* 5 *16 DSP16
* 6 -- sndstat (obsolete)
* 7 *16 unused
* 8 -- alternate sequencer (see above)
* 9 *16 raw synthesizer access
* 10 *16 unused
* 11 *16 unused
* 12 *16 unused
* 13 *16 unused
* 14 *16 unused
* 15 *16 unused
static struct sound_unit *chains[SOUND_STEP];
*/
chain=unit&0x0F; // 当前不超过16个SOUND_STEP
s = __look_for_unit(chain, unit); // 从chains[chain]全局链表上寻找索引号为unit的sound_unit.
if (s)
new_fops = fops_get(s->unit_fops); //使用s->unit_fops=snd_pcm_oss_f_reg替换原有的soundcore_fops函数集
file->f_op = new_fops;
err = file->f_op->open(inode,file); // 使用snd_pcm_oss_open进一步打开
}
static struct sound_unit *__look_for_unit(int chain, int unit)
{
struct sound_unit *s;
s=chains[chain];
while(s && s->unit_minor <= unit)
{
if(s->unit_minor==unit)
return s; // ok,找到
s=s->next;
}
return NULL;
}
到目前为止我们粗略讨论了打开/dev/dsp设备节点的流程,下面我们继续看看创建/dev/dsp设备节点的流程是怎么样的[luther.gliethttp],
module_init(alsa_pcm_oss_init)还有一个module_init(alsa_mixer_oss_init)和alsa_pcm_oss_init过程差不多.
==>alsa_pcm_oss_init //登记snd_pcm_oss_notify,同时为snd_pcm_devices链表上的的pcm设备执行snd_pcm_oss_register_minor函数
==*>snd_pcm_notify(&snd_pcm_oss_notify, 0) // 将snd_pcm_oss_notify追加到snd_pcm_notify_list通知链表
list_add_tail(¬ify->list, &snd_pcm_notify_list);
list_for_each_entry(pcm, &snd_pcm_devices, list) // 同时为snd_pcm_oss_notify遍历已经注册登记到snd_pcm_devices链表上的的pcm设备
notify->n_register(pcm); // 为他们分别执行snd_pcm_oss_notify的n_register方法[luther.gliehtttp]
static struct snd_pcm_notify snd_pcm_oss_notify =
{
.n_register = snd_pcm_oss_register_minor,
.n_disconnect = snd_pcm_oss_disconnect_minor,
.n_unregister = snd_pcm_oss_unregister_minor,
};
snd_pcm_oss_register_minor // 当检测到新的声卡设备时,就会调用该notifer函数,为其注册登记生成设备节点
==> register_oss_dsp(pcm, 0);和register_oss_dsp(pcm, 1); // index=0或者index=1,即第0个16组或者第1个16组
static void register_oss_dsp(struct snd_pcm *pcm, int index)
{
char name[128];
sprintf(name, "dsp%i%i", pcm->card->number, pcm->device);
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
pcm->card, index, &snd_pcm_oss_f_reg,// 实际完成控制设备的fops,即:snd_pcm_oss_f_reg
pcm, name) < 0) {
snd_printk(KERN_ERR "unable to register OSS PCM device %i:%in",
pcm->card->number, pcm->device);
}
}
snd_register_oss_device(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops, void *private_data,
const char *name)
==>int minor = snd_oss_kernel_minor(type, card, dev); //minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 :SNDRV_MINOR_OSS_PCM));
==> preg->device = dev; // 我这里minor等于3
==> preg->f_ops = f_ops;
==> snd_oss_minors[minor] = preg; // 放到oss设备数组中,这样在snd_pcm_oss_open时可以打开
==> register_sound_special_device(f_ops, minor, carddev); // minor>=3
/**
* register_sound_special_device - register a special sound node
* @fops: File operations for the driver
* @unit: Unit number to allocate
* @dev: device pointer
*
* Allocate a special sound device by minor number from the sound
* subsystem. The allocated number is returned on succes. On failure
* a negative error code is returned.
*/
int register_sound_special_device(const struct file_operations *fops, int unit,
struct device *dev)
{
const int chain = unit % SOUND_STEP; // SOUND_STEP为16,分别代表主设备类型,每个主设备类型下可以追加n个同类型的音频设备.
int max_unit = 128 + chain;
const char *name;
char _name[16];
switch (chain) {
case 0:
name = "mixer";
break;
case 1:
name = "sequencer";
if (unit >= SOUND_STEP)
goto __unknown;
max_unit = unit + 1;
break;
case 2:
name = "midi";
break;
case 3:
name = "dsp";
break;
case 4:
name = "audio";
break;
case 8:
name = "sequencer2";
if (unit >= SOUND_STEP)
goto __unknown;
max_unit = unit + 1;
break;
case 9:
name = "dmmidi";
break;
case 10:
name = "dmfm";
break;
case 12:
name = "adsp";
break;
case 13:
name = "amidi";
break;
case 14:
name = "admmidi";
break;
default:
{
__unknown:
sprintf(_name, "unknown%d", chain);
if (unit >= SOUND_STEP)
strcat(_name, "-");
name = _name;
}
break;
}
return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,
name, S_IRUSR | S_IWUSR, dev); // 将方法集snd_pcm_oss_f_reg注册上去
}
staticint sound_insert_unit(struct sound_unit **list, const structfile_operations *fops, int index, int low, int top, const char *name,umode_t mode, struct device *dev)
{
struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL);
int r;
if (!s)
return -ENOMEM; // index等于-1,表示动态获取一个可用的设备节点号.
spin_lock(&sound_loader_lock); // 每16个设备为一组,index表示第几组.
r = __sound_insert_unit(s, list, fops, index, low, top); // 插入到上面提到的chains[3]中,inode节点的minor设备号
spin_unlock(&sound_loader_lock); // 从最小值3开始按i*16方式递增,
// 即/dev/dsp的节点号为(14,3),
// /dev/dsp1的节点号为(14,19),
// /dev/dsp2的节点号为(14,35)依次类推[luther.gliethttp].
// 最后s->unit_minor=动态获取的一个空闲id
// s->unit_fops=snd_pcm_oss_f_reg
if (r < 0)
goto fail;
else if (r < SOUND_STEP)
sprintf(s->name, "sound/%s", name);
else
sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
// 调用device_create广播设备信息到user space,udev创建
// 相应的字符设备节点/dev/dsp等[luther.gliethttp].
device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor), // MKDEV(SOUND_MAJOR, s->unit_minor)为/dev/dsp设备的
NULL, s->name+6); // 节点号,主节点号SOUND_MAJOR等于14,子节点minor等于s->unit_minor
return r;
fail:
kfree(s);
return r;
}
上面snd_pcm_oss_notify中的n_register方法即:snd_pcm_oss_register_minor是在snd_pcm_oss_notify注册时主动执行的,
那在设备注册的时候又是怎么被动的引用n_register方法的呢?下面我们来看看,
先来看看设备注册,
/* audio subsystem */
static struct snd_soc_device TLG_snd_devdata = {
.machine = &snd_soc_machine_TLG,
.platform = &ep93xx_soc_platform,
.codec_dev = &soc_codec_dev_xxxxx,
};
static struct platform_device *TLG_snd_device;
module_init(TLG_init);
static int __init TLG_init(void) // 平台audio设备初始化入口
{
TLG_snd_device = platform_device_alloc("soc-audio", -1); // 他将被名为"soc-audio"的platform总线下的驱动程序驱动[luther.gliethttp]
platform_set_drvdata(TLG_snd_device, &TLG_snd_devdata);
TLG_snd_devdata.dev = &TLG_snd_device->dev;
ret = platform_device_add(TLG_snd_device);
}
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
},
.probe = soc_probe,
.remove = soc_remove,
.suspend = soc_suspend,
.resume = soc_resume,
};
/* probes a new socdev */
static int soc_probe(struct platform_device *pdev)
{
// /* TLG audio machine driver */
// static struct snd_soc_machine snd_soc_machine_TLG = {
// .name = "TLG",
// .dai_link = TLG_dai, // /* CPU