/dev/dsp與alsa框架下設(shè)備節(jié)點打開和創(chuàng)建簡易流程
淺析ASoC-audio驅(qū)動oss框架下/dev/dsp與alsa框架下設(shè)備節(jié)點打開和創(chuàng)建簡易流程
對于oss設(shè)備節(jié)點
1. soundcore_fops?????? --? 提供主設(shè)備號為14的oss節(jié)點open("/dev/dsp")操作soundcore_open,最后將調(diào)用snd_pcm_oss_open
2. snd_pcm_oss_f_reg??? --? 提供最終的file->f_op應(yīng)用程序調(diào)用方法集
對于alsa設(shè)備節(jié)點
1. snd_fops???????????? --? 提供主設(shè)備號為116的alsa節(jié)點open("/dev/snd/pcmC0D0c")操作snd_open
2. snd_pcm_f_ops[2]???? --? 提供最終的file->f_op應(yīng)用程序調(diào)用方法集snd_pcm_f_ops[0]用于放音,snd_pcm_f_ops[1]用于錄音.
可能后面的流程都是混雜的,不能區(qū)分很清楚,所以先來看最直觀的oss設(shè)備節(jié)點"/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的實現(xiàn),現(xiàn)在很多集中管理的驅(qū)動都這樣
};????????????????????????????????????????????????????????????????????? // 來界定設(shè)備[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字符設(shè)備節(jié)點的流程[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) {?? // 主設(shè)備號為14的所有256個字符設(shè)備節(jié)點都將調(diào)用該方法集
?? ??? ?printk(KERN_ERR "soundcore: sound device already in use.n");?? // 比如打開/dev/dsp設(shè)備,那么將首先執(zhí)行這里的soundcore_open
?? ??? ?return -EBUSY;
?? ?}
?? ?sound_class = class_create(THIS_MODULE, "sound");?????????????????? // 創(chuàng)建/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);?????????????????????????????????????????? //根據(jù)inode節(jié)點的minor次設(shè)備號鎖定聲卡設(shè)備,對于inode節(jié)點的自動創(chuàng)建在后面我們會慢慢談到[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函數(shù)集
??? 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設(shè)備節(jié)點的流程,下面我們繼續(xù)看看創(chuàng)建/dev/dsp設(shè)備節(jié)點的流程是怎么樣的[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設(shè)備執(zhí)行snd_pcm_oss_register_minor函數(shù)
==*>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遍歷已經(jīng)注冊登記到snd_pcm_devices鏈表上的的pcm設(shè)備
??? ??? ??? notify->n_register(pcm);??????????????????? // 為他們分別執(zhí)行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?????????????????????????????? // 當檢測到新的聲卡設(shè)備時,就會調(diào)用該notifer函數(shù),為其注冊登記生成設(shè)備節(jié)點
==> 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,// 實際完成控制設(shè)備的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設(shè)備數(shù)組中,這樣在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,分別代表主設(shè)備類型,每個主設(shè)備類型下可以追加n個同類型的音頻設(shè)備.
??? 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,表示動態(tài)獲取一個可用的設(shè)備節(jié)點號.
??? ???
??? spin_lock(&sound_loader_lock);????????????????????????????????????? // 每16個設(shè)備為一組,index表示第幾組.
??? r = __sound_insert_unit(s, list, fops, index, low, top);??????????? // 插入到上面提到的chains[3]中,inode節(jié)點的minor設(shè)備號
??? spin_unlock(&sound_loader_lock);??????????????????????????????????? // 從最小值3開始按i*16方式遞增,
??????????????????????????????????????????????????????????????????????? // 即/dev/dsp的節(jié)點號為(14,3),
??????????????????????????????????????????????????????????????????????? // /dev/dsp1的節(jié)點號為(14,19),
??????????????????????????????????????????????????????????????????????? // /dev/dsp2的節(jié)點號為(14,35)依次類推[luther.gliethttp].
??? ??????????????????????????????????????????????????????????????????? // 最后s->unit_minor=動態(tài)獲取的一個空閑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);
??????????????????????????????????????????????????????????????????????? // 調(diào)用device_create廣播設(shè)備信息到user space,udev創(chuàng)建
??????????????????????????????????????????????????????????????????????? // 相應(yīng)的字符設(shè)備節(jié)點/dev/dsp等[luther.gliethttp].
??? device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),? // MKDEV(SOUND_MAJOR, s->unit_minor)為/dev/dsp設(shè)備的
??? ??? ????? NULL, s->name+6);???????????????????????????????????????? // 節(jié)點號,主節(jié)點號SOUND_MAJOR等于14,子節(jié)點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注冊時主動執(zhí)行的,
那在設(shè)備注冊的時候又是怎么被動的引用n_register方法的呢?下面我們來看看,
先來看看設(shè)備注冊,
/* 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設(shè)備初始化入口
{
??? TLG_snd_device = platform_device_alloc("soc-audio", -1);??? // 他將被名為"soc-audio"的platform總線下的驅(qū)動程序驅(qū)動[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