编写声卡驱动(框架)
在前面兩篇文章中,我們分別講了嵌入式Linux系統聲卡注冊的過程和調用的過程:
https://blog.csdn.net/qq_37659294/article/details/104748747
https://blog.csdn.net/qq_37659294/article/details/104802868
講了那么多,我們最終的目的無非就是想寫一個聲卡驅動,然后給上層的APP使用而已,在之前的文章中可以看出內核里面關于聲卡這部分是非常復雜的,但實際上我們寫驅動的時候,只需要實現和硬件相關的那幾個結構體,如cpu_dai等。然后借助內核的ASOC框架把我們的驅動注冊進去而已。下面是我們編寫的驅動:
一、machine部分:
①我們構造一個snd_soc_card結構體myalsa_card,它的dai_link指定了要使用那些cpu_dai、codec_dai...然后我們模仿內核自帶的聲卡驅動,調用platform_set_drvdata把myalsa_card保存在platform_device中(《ASOC注冊過程》machine部分的第①點有介紹)。
②構造一個名為"soc-audio"的平臺設備,然后注冊它,因為內核中有同名的平臺驅動,所以調用了相應的probe函數,即我們在《ASOC注冊過程》machine部分的第①點有介紹到的soc_probe函數。這個函數就會根據我們構造的myalsa_card結構體里的信息,完成我們聲卡的所有注冊任務。
至此,我們的machine部分的驅動就編寫完成了。
#include <linux/clk.h> #include <linux/gpio.h> #include <linux/module.h>#include <sound/soc.h>/* 參考sound\soc\samsung\s3c24xx_uda134x.c*//** 1. 分配注冊一個名為soc-audio的平臺設備* 2. 這個平臺設備有一個私有數據 snd_soc_card* snd_soc_card里有一項snd_soc_dai_link* snd_soc_dai_link被用來決定ASOC各部分的驅動*/static struct snd_soc_ops s3c2440_uda1341_ops = {//.hw_params = s3c24xx_uda134x_hw_params, };static struct snd_soc_dai_link s3c2440_uda1341_dai_link = {.name = "100ask_UDA1341",.stream_name = "100ask_UDA1341",.codec_name = "uda1341-codec",.codec_dai_name = "uda1341-iis",.cpu_dai_name = "s3c2440-iis",.ops = &s3c2440_uda1341_ops,.platform_name = "s3c2440-dma", };static struct snd_soc_card myalsa_card = {.name = "S3C2440_UDA1341",.owner = THIS_MODULE,.dai_link = &s3c2440_uda1341_dai_link,.num_links = 1, };static void asoc_release(struct device * dev) { }static struct platform_device asoc_dev = {.name = "soc-audio",.id = -1,.dev = { .release = asoc_release, }, };static int s3c2440_uda1341_init(void) {platform_set_drvdata(&asoc_dev, &myalsa_card);platform_device_register(&asoc_dev); return 0; }static void s3c2440_uda1341_exit(void) {platform_device_unregister(&asoc_dev); }module_init(s3c2440_uda1341_init); module_exit(s3c2440_uda1341_exit);MODULE_LICENSE("GPL");二、platform部分
1.cpu_dai
①構造一個snd_soc_dai_driver結構體變量s3c2440_i2s_dai,s3c2440_i2s_dai里面的函數是我們需要自己實現的關于硬件的操作函數。
static int s3c2440_i2s_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params,struct snd_soc_dai *dai) {/* 根據params設置IIS控制器 *//* 配置GPIO用于IIS */...return 0; }static int s3c2440_i2s_trigger(struct snd_pcm_substream *substream, int cmd,struct snd_soc_dai *dai) { /* 硬件相關的操作 */ }static const struct snd_soc_dai_ops s3c2440_i2s_dai_ops = {.hw_params = s3c2440_i2s_hw_params,.trigger = s3c2440_i2s_trigger, };#define S3C24XX_I2S_RATES \(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)static struct snd_soc_dai_driver s3c2440_i2s_dai = {.playback = {.channels_min = 2,.channels_max = 2,.rates = S3C24XX_I2S_RATES,.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},.capture = {.channels_min = 2,.channels_max = 2,.rates = S3C24XX_I2S_RATES,.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},.ops = &s3c2440_i2s_dai_ops, };②分別構造一個平臺設備和平臺驅動(它們的名字必須相同,且必須和machine部分的dai_lnk指定的名字相同),然后就調用它們的probe函數,
static struct platform_device s3c2440_iis_dev = {.name = "s3c2440-iis",.id = -1,.dev = { .release = s3c2440_iis_release, }, }; struct platform_driver s3c2440_iis_drv = {.probe = s3c2440_iis_probe,.remove = s3c2440_iis_remove,.driver = {.name = "s3c2440-iis",} };③在這個probe函數里調用snd_soc_register_dai(&pdev->dev, &s3c2440_i2s_dai);將第①步構造的結構體變量放入鏈表dai_list,并把它命名為第②步提到的平臺設備的名字。machine部分就是根據dai_link指定的名字,在dai_list中把s3c2440_i2s_dai找出來。
static int s3c2440_iis_probe(struct platform_device *pdev) {return snd_soc_register_dai(&pdev->dev, &s3c2440_i2s_dai); } static int s3c2440_iis_remove(struct platform_device *pdev) {snd_soc_unregister_dai(&pdev->dev);return 0; }至此,cpu_dai部分的驅動框架編寫也已經完成,具體的硬件操作函數需要根據不同的硬件來編寫。(dma、codec_dai和這個類似,下面將不再贅述,直接貼出代碼示意)
2.dma
/* 參考 sound\soc\samsung\dma.c*/static struct snd_pcm_ops s3c2440_dma_ops = {/* 需要我們自己編寫的,關于硬件的函數 */.open = s3c2440_dma_open,.close = s3c2440_dma_close,.ioctl = snd_pcm_lib_ioctl,.hw_params = s3c2440_dma_hw_params,.prepare = s3c2440_dma_prepare,.trigger = s3c2440_dma_trigger,.pointer = s3c2440_dma_pointer, };static struct snd_soc_platform_driver s3c2440_dma_platform = {/* 需要我們自己編寫的,關于硬件的函數 */.ops = &s3c2440_dma_ops,.pcm_new = dma_new,.pcm_free = dma_free_dma_buffers, };static int s3c2440_dma_probe(struct platform_device *pdev) {return snd_soc_register_platform(&pdev->dev, &s3c2440_dma_platform); } static int s3c2440_dma_remove(struct platform_device *pdev) {return snd_soc_unregister_platform(&pdev->dev); }static void s3c2440_dma_release(struct device * dev) { }static struct platform_device s3c2440_dma_dev = {.name = "s3c2440-dma",.id = -1,.dev = { .release = s3c2440_dma_release, }, }; struct platform_driver s3c2440_dma_drv = {.probe = s3c2440_dma_probe,.remove = s3c2440_dma_remove,.driver = {.name = "s3c2440-dma", //必須和dai_link里面的platform_name相同} };static int s3c2440_dma_init(void) {platform_device_register(&s3c2440_dma_dev);platform_driver_register(&s3c2440_dma_drv);return 0; }static void s3c2440_dma_exit(void) {platform_device_unregister(&s3c2440_dma_dev);platform_driver_unregister(&s3c2440_dma_drv); }module_init(s3c2440_dma_init); module_exit(s3c2440_dma_exit);三、codec部分
/* 參考 sound\soc\codecs\uda134x.c*//* 1. 構造一個snd_soc_dai_driver* 2. 構造一個snd_soc_codec_driver* 3. 注冊它們*/static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {/* 硬件相關的函數 */.probe = uda1341_soc_probe,.reg_cache_size = sizeof(uda1341_reg),.reg_word_size = sizeof(u8),.reg_cache_default = uda1341_reg,.reg_cache_step = 1,.read = uda1341_read_reg_cache,.write = uda1341_write_reg, /* 寫寄存器 */ };static const struct snd_soc_dai_ops uda1341_dai_ops = {/* 硬件相關的操作 */.hw_params = uda1341_hw_params, };static struct snd_soc_dai_driver uda1341_dai = {.name = "uda1341-iis", //必須和dai_link里面的codec_dai_name相同/* playback capabilities */.playback = {.stream_name = "Playback",.channels_min = 1,.channels_max = 2,.rates = UDA134X_RATES,.formats = UDA134X_FORMATS,},/* capture capabilities */.capture = {.stream_name = "Capture",.channels_min = 1,.channels_max = 2,.rates = UDA134X_RATES,.formats = UDA134X_FORMATS,},/* pcm operations */.ops = &uda1341_dai_ops, };/* 通過注冊平臺設備、平臺驅動來實現對snd_soc_register_codec的調用**/static void uda1341_dev_release(struct device * dev) { }static int uda1341_probe(struct platform_device *pdev) {return snd_soc_register_codec(&pdev->dev,&soc_codec_dev_uda1341, &uda1341_dai, 1); }static int uda1341_remove(struct platform_device *pdev) {return snd_soc_unregister_codec(&pdev->dev); }static struct platform_device uda1341_dev = {.name = "uda1341-codec", //必須和dai_link里面的codec_name相同.id = -1,.dev = { .release = uda1341_dev_release, }, }; struct platform_driver uda1341_drv = {.probe = uda1341_probe,.remove = uda1341_remove,.driver = {.name = "uda1341-codec",} };static int uda1341_init(void) {platform_device_register(&uda1341_dev);platform_driver_register(&uda1341_drv);return 0; }static void uda1341_exit(void) {platform_device_unregister(&uda1341_dev);platform_driver_unregister(&uda1341_drv); }module_init(uda1341_init); module_exit(uda1341_exit);?
總結
以上是生活随笔為你收集整理的编写声卡驱动(框架)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LOL进不去,别的区可以进
- 下一篇: 向天真的女生投降剧情介绍