当前位置:首页 > 芯闻号 > 充电吧
[导读]浅析ASoC-audio驱动oss框架下/dev/dsp与alsa框架下设备节点打开和创建简易流程对于oss设备节点1. soundcore_fops       --  提供主设备号为14的oss节

浅析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(&notify->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

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭