为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

声卡驱动原理浅析

2017-12-29 17页 doc 132KB 22阅读

用户头像

is_036899

暂无简介

举报
声卡驱动原理浅析声卡驱动原理浅析 :1:聲卡驅動原理淺析 聲卡驅動結構概覽: 聲卡設備模型建立的幾個必要文件是:s3c24xx_uda134x.c,s3c24xx-i2s.c,s3c-dma.c ,uda134x.c,soc-core.c。 s3c24xx-i2s.c : 該文件主要實現了配置cpu上iis接口寄存器的一些操作函數,填充了結構 體 s3c24xx_i2s_dai。並調用函數snd_soc_register_dai(&s3c24xx_i2s_dai)將其掛到鏈表頭dai_list上list_add(&dai->list...
声卡驱动原理浅析
声卡驱动原理浅析 :1:聲卡驅動原理淺析 聲卡驅動結構概覽: 聲卡設備模型建立的幾個必要文件是:s3c24xx_uda134x.c,s3c24xx-i2s.c,s3c-dma.c ,uda134x.c,soc-core.c。 s3c24xx-i2s.c : 該文件主要實現了配置cpu上iis接口寄存器的一些操作函數,填充了結構 體 s3c24xx_i2s_dai。並調用函數snd_soc_register_dai(&s3c24xx_i2s_dai)將其掛到鏈頭dai_list上list_add(&dai->list, &dai_list); s3c-dma.c : 該文件主要實現了錄音,放音等數據流操作函數。填充了結構體 s3c24xx_soc_platform。並調用函數 snd_soc_register_platform(&s3c24xx_soc_platform);將其掛到鏈表頭 platform_list上,list_add(&platform->list, &platform_list);。 uda134x.c : 該文件主要實現了對編解碼芯片uda1341寄存器的設置,聲音調節,靜音設 置等操 作函數。填充了結構體uda134x_dai。並調用函數 snd_soc_register_dai(&uda134x_dai)將其掛 到鏈表頭dai_list上 list_add(&dai->list, &dai_list);。該文件還實現了一些重要的初 始化,比如 創 建結構體類型為snd_card的card實例,創建pcm實例等。 s3c24xx_uda134x.c : 該文件是設備模型建立要執行的第一個文件。它聯繫了以上三個文 件,導 致了以 上三文件中的初始化函數的調用執行。填充了結構體 s3c24xx_uda134x_ops, s3c24xx_uda134x_dai_link, snd_soc_s3c24xx_uda134x, s3c24xx_uda134x, s3c24xx_uda134x_snd_devdata。添加了平臺設備 s3c24xx_uda134x_snd_device到內 核。註冊了與平臺設備 "s3c24xx_uda134x"相匹配的驅動s3c24xx_uda134x_driver。 soc-core.c: 該文件主要實現比以上文件更高一級的通用函數。實現與設備 s3c24xx_uda134x_snd_device相應的驅動。 聲卡驅動設備模型建立圖: 聲卡設備驅動程序: 聲卡驅動設備模型的建立應該從文件 sound/soc/s3c24xx/s3c24xx_uda134x.c 開始。這個文件中實現了聲卡uda134x平臺設備驅動。 static struct platform_driver s3c24xx_uda134x_driver = { .probe = s3c24xx_uda134x_probe, .remove = s3c24xx_uda134x_remove, .driver = { .name = "s3c24xx_uda134x", .owner = THIS_MODULE, }, }; 一旦聲卡平臺設備添加到內核與該驅動相匹配,探測函數 s3c24xx_uda134x_probe將被調用。 static int s3c24xx_uda134x_probe(struct platform_device *pdev) { ……………………………… //創建平臺設備結構體s3c24xx_uda134x_snd_device s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1); ……………………………… //結構體s3c24xx_uda134x_snd_devdata是一個很重要的結構,其中包含了很多信//息 platform_set_drvdata(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x_snd_devdata); //註冊名為"soc-audio"的平臺設備,這將導致與文件sound/soc/soc-core.c中實現 //的平臺設備驅動soc_driver的匹配 ret = platform_device_add(s3c24xx_uda134x_snd_device); ……………………………… } 在文件sound/soc/soc-core.c中: static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", .owner = THIS_MODULE, .pm = &soc_pm_ops, }, .probe = soc_probe, .remove = soc_remove, }; 成功匹配之後將調用函數soc_probe(), static int soc_probe(struct platform_device *pdev) { ret = snd_soc_register_card(card); } static int snd_soc_register_card(struct snd_soc_card *card) { ……………………………… list_add(&card->list, &card_list); snd_soc_instantiate_cards(); ……………………………… } static void snd_soc_instantiate_cards(void) { struct snd_soc_card *card; list_for_each_entry(card, &card_list, list) snd_soc_instantiate_card(card); } static void snd_soc_instantiate_card(struct snd_soc_card *card) { ……………………………… //函數cpu_dai->probe(pdev, cpu_dai)就是文件s3c24xx-i2s.c 中實現的 函數 //s3c24xx_i2s_probe(),這個函數主要實現了I2S管腳,時鐘,相關寄存器配置。 for (i = 0; i < card->num_links; i++) { struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; if (cpu_dai->probe) { ret = cpu_dai->probe(pdev, cpu_dai); if (ret < 0) goto cpu_dai_err; } } //函數 codec_dev->probe(pdev);即是文件uda134x.c中實現的函數uda134x_soc_probe() //這個函數完成了一些很重要的工作,將在後面分析。 if (codec_dev->probe) { ret = codec_dev->probe(pdev); if (ret < 0) goto cpu_dai_err; } ……………………………… //下面函數完成了聲卡相關設備的註冊,後面將有分析 ret = snd_card_register(codec->card); ……………………………… } static int uda134x_soc_probe(struct platform_device *pdev) { ……………………………… //這是一個重要函數,下面將有講解。 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { printk(KERN_ERR "UDA134X: failed to register pcms\n"); goto pcm_err; } switch (pd->model) { case UDA134X_UDA1340: case UDA134X_UDA1344: ……………………………… case UDA134X_UDA1341: //將數組uda1341_snd_controls中各結構體元素掛到card->controls鏈表上,這些結構體實 現 //了音頻播放時各個參數的設置。主要的操作函數有三個.info,.get,.put。 ret = snd_soc_add_controls(codec, uda1341_snd_controls, ARRAY_SIZE(uda1341_snd_controls)); break; case UDA134X_UDA1345: ……………………………… default: ……………………………… } ……………………………… } int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) { ……………………………… ret = snd_card_create(idx, xid, codec->owner, 0, &codec->card); ……………………………… //PCM實例中包含播放流和錄音流,card->num_links表示pcm實例的個數。 for (i = 0; i < card->num_links; i++) { ret = soc_new_pcm(socdev, &card->dai_link[i], i); ……………………………… } } int snd_card_create(int idx, const char *xid, struct module *module, int extra_size, struct snd_card **card_ret) { //分配一個聲卡設備結構 card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); ……………………………… err = snd_ctl_create(card); ……………………………… } int snd_ctl_create(struct snd_card *card) { static struct snd_device_ops ops = { .dev_free = snd_ctl_dev_free, .dev_register = snd_ctl_dev_register, .dev_disconnect = snd_ctl_dev_disconnect, }; /* 在下面函數中將會做這麼幾件事: dev = kzalloc(sizeof(*dev), GFP_KERNEL); dev->ops = ops; list_add(&dev->list, &card->devices); 將dev掛到card上,在後面將會有這樣的調用 dev->ops->dev_register(dev),這將導 致註冊一個聲卡控制設備並創建設備節點。 */ return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); } 在上面講到的函數 snd_soc_new_pcms中還有如下調用: int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const c har *xid) { ……………………………… ret = soc_new_pcm(socdev, &card->dai_link[i], i); ……………………………… } static int soc_new_pcm(struct snd_soc_device *socdev, struct snd_soc_dai_link *dai_link, int num) { ……………………………… //函數snd_pcm_new::後面講解 ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback , ……………………………… //將播放流的所有子流的操作函數都設為soc_pcm_ops if (playback) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); //將錄音流的所有子流的操作函數都設為soc_pcm_ops if (capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); //下面函數即是函數s3c_dma_new(),在文件s3c-dma.c 中實現,這個函數主要 是為播放流錄 //音流分配緩存區。 ret = platform->pcm_new(codec->card, codec_dai, pcm); } 在上面函數中調用了函數snd_pcm_new()下面將是對這個函數的分析。 int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm ** rpcm) { static struct snd_device_ops ops = { .dev_free = snd_pcm_dev_free, .dev_register = snd_pcm_dev_register, .dev_disconnect = snd_pcm_dev_disconnect, }; ……………………………… //創建播放流的子流 if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playba ck_count)) < 0) { snd_pcm_free(pcm); return err; } //創建錄音流的子流 if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture _count)) < 0) { snd_pcm_free(pcm); return err; } ……………………………… /* 在下面函數中將會做這麼幾件事: dev = kzalloc(sizeof(*dev), GFP_KERNEL); dev->ops = ops; list_add(&dev->list, &card->devices); 將dev掛到card上,在後面將會有這樣的調用 dev->ops->dev_register(dev),這 將導致註冊一個聲卡錄音流設備和播放流設備並創建設備節點。 */ if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { snd_pcm_free(pcm); return err; } ……………………………… } 在前面函數snd_soc_instantiate_card()中調用了函數 int snd_card_register(struct snd_card *card) { ……………………………… /* 調用上面提到的那幾個設備註冊函數,創建設備節點。 list_for_each_entry(dev, &card->devices, list) { dev->ops->dev_register(dev) } */ if ((err = snd_device_register_all(card)) < 0) ……………………………… } ……………………………… } 在聲卡uda1341的聲卡體系中總共創建了四個設備節點: "controlC%i", "pcmC%iD%ip","pcmC%iD%ic","timer", 分別代表控制設備,播放流設備,錄音流設備,定時設備。下面討論這幾個設備節點的創建。 控制設備: 在文件control.c中實現函數 static int snd_ctl_dev_register(struct snd_device *device) { struct snd_card *card = device->device_data; int err, cardnum; char name[16]; ……………………………… cardnum = card->number; //下面函數將調用snd_register_device_for_dev()最終創建 //名為"controlC%i"的設備節點,並關聯文件操作函數集snd_ctl_f_ops,其結//構類型為file_operations,其成員函數在文件control.c中實現。 // sprintf(name, "controlC%i", cardnum); if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, - 1, &snd_ctl_f_ops, card, name)) < 0) return err; ……………………………… } 播放流設備 和 錄音流設備: static int snd_pcm_dev_register(struct snd_device *device) { ……………………………… for (cidx = 0; cidx < 2; cidx++) { //有播放流和錄音流所以是兩次循環 switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK: //設備節點名為"pcmC%iD%ip" sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; break; case SNDRV_PCM_STREAM_CAPTURE: //設備節點名為"pcmC%iD%ic" sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; break; } ……………………………… //創建設備節點,關聯的文件操作函數集snd_pcm_f_ops[cidx]。該函數集結構 //的結構類型為file_operations。該函數集在文件pcm_native.c中實現。 err = snd_register_device_for_dev(devtype, pcm->card, pcm->device,&snd_pcm_f_ops[cidx],pcm, str, dev); ……………………………… } 定時設備: 在文件timer.c中 static int __init alsa_timer_init(void) { ……………………………… //下面函數將調用snd_register_device_for_dev()最終創建 //名為"timer"的設備節點,其關聯的文件操作函數集snd_timer_f_ops。 if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, &snd_timer_f_ops, NULL, "timer")) < 0) ……………………………… } 由上面分析可知,上面四個設備的註冊都將調用同一個函數 snd_register_device_for_dev(type, card, dev, f_ops, private_data, name, snd_card_get_device_link(card)); 該函數的實現如下: 可以看出,下面函數是將傳入的各參數包裝成結構體snd_minor並根據次 設備號存入數組snd_minors[minor] = preg;然後創建相應設備節點。一個設備 對應一個snd_minor結構。 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) { …………………… preg->type = type; preg->card = card ? card->number : -1; preg->device = dev; preg->f_ops = f_ops; preg->private_data = private_data; mutex_lock(&sound_mutex); …………………… minor = snd_kernel_minor(type, card, dev); …………………… snd_minors[minor] = preg; preg->dev = device_create(sound_class, device, MKDEV(major, minor), private_data, "%s", name); ……………………………… } 上面聲卡相關設備都有一個共同的主設備號: 在文件include/sound/core.h中有如下定義 #define CONFIG_SND_MAJOR 116 在文件sound/core/sound.c中又有: static int major = CONFIG_SND_MAJOR; 主設備號CONFIG_SND_MAJOR的註冊也是在文件sound/core/sound.c 中進行的。 static int __init alsa_sound_init(void) { //註冊主設備號為CONFIG_SND_MAJOR的字符設備並關聯一個字符設備操作//函數集snd_fops if (register_chrdev(major, "alsa", &snd_fops)) { ……………………………… } ……………………………… } 上面提到的字符設備操作函數集只有一個操作函數 static const struct file_operations snd_fops = { .owner = THIS_MODULE, .open = snd_open }; 在這個打開函數中根據打開設備的次設備號在數組snd_minors[]中獲取設備對應的snd_minors結構,並實現了操作函數集的切換。 static int snd_open(struct inode *inode, struct file *file) { ……………………………… mptr = snd_minors[minor]; ……………………………… file->f_op = fops_get(mptr->f_ops); ……………………………… } 各操作函數相互調用關係圖
/
本文档为【声卡驱动原理浅析】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索