当前位置:首页 > 芯闻号 > 充电吧
[导读]浅析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

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

May 14, 2024 ---- TrendForce集邦咨询研究最新显示,OLED桌上型显示器(Monitor)2024年第一季出货总量约为20万台,年成长率121%。第二季在品牌新机陆续上市后,当季成长幅度预估将达...

关键字: OLED 显示器

业内消息,近日日本软件银行集团(SoftBank Group)旗下安谋国际科技公司(Arm)计划研发人工智能(AI)芯片,先成立一个AI芯片部门,目标是明年春季建立AI芯片原型产品,然后将量产工作交由代工厂制造,预估20...

关键字: ARM AI芯片

《芯片与科学法案》(CHIPS)为美国芯片研究、开发、制造和劳动力发展提供了527亿美元的资助。

关键字: 美国芯片法案 芯片与科学法案 芯片

援引彭博社消息,近日新当选的熊本县知事木村隆(Takashi Kimura)表示,他已准备好确保获得广泛的支持,以吸引台积电在当地建立第三家日本芯片工厂。

关键字: 日本 台积电 芯片工厂

5 月 13 日消息,从“上海临港”微信公众号获悉,特斯拉上海储能超级工厂建设项目已完成施工许可证核发。这是特斯拉在美国本土以外的首个储能超级工厂项目,工厂计划于今年 5 月开工,明年一季度实现量产。

关键字: 特斯拉 储能

据消息源 jasonwill101 透露,高通公司目前正在重新设计骁龙 8 Gen 4 处理器,新的目标频率为 4.26GHz,这一变化主要是为了应对苹果 M4 / A18 / Pro 处理器。

关键字: 高通 骁龙 8 Gen 4 芯片

最新消息,今天凌晨 OpenAI 在春季更新直播官宣发布最新旗舰生成式 AI 模型 GPT-4o,GPT-4o 将 ChatGPT 变成一名带有文本、「视觉」与语音互动的实时语音助手。OpenAI 表示升级版的 Chat...

关键字: OpenAI 生成式 AI大模型 GPT-4o

三星电子最近进行了重大的组织重组,以增强其在下一代机器人业务方面的能力,并将其视为关键增长领域。作为重组的一部分,该公司解散了负责开发三星首款可穿戴机器人“Bot Fit”的机器人业务团队。

关键字: 三星电子 解散 Bot Fit 机器人

NAS这些年可吸引了不少数码发烧友的注意,但也渐渐在家庭用户中风靡。究其原因,大概还是因为太多人因为现在数据过于庞大,而一个NAS基本上就能解决一个家庭的数据存储难题。在这一背景下,铁威马F4-424 Pro凭借其出色的...

关键字: NAS 数据存储 处理器

央视《今日说法》栏目近期报道了一名90后程序员通过开发非法视频搬运软件在不到一年的时间里获利超700万,最终获刑的案例。

关键字: 程序员 软件
关闭
关闭