Процесс регистрации в АСОК

1. Что такое АСОК

Драйвером звуковой карты во встроенной системе является ASOC (ALSA System on Chip), представляющий собой слой, инкапсулированный в драйвере ALSA, разделенный на 3 части, Machine, Platform и Codec. Отношения между этими тремя частями показаны на следующем рисунке: Machine относится к нашей плате разработки, Platform - к Soc, а Codec - к кодеку (например, uda1341).

Во-вторых, процесс регистрации звуковой карты встроенной системы

Здесь мы возьмем плату разработки mini2440 в качестве примера 

安装新驱动

insmod alsa/driver/myalsa/platform/s3c2440_iis.ko 
insmod alsa/driver/myalsa/platform/s3c2440_dma.ko 
insmod alsa/driver/myalsa/codec/uda1341.ko 
insmod alsa/driver/myalsa/machine/s3c2440_uda1341.ko 

Во-первых, мы устанавливаем драйвер звуковой карты, который мы записали в ядро ​​(машинная часть файла ko должна быть помещена в конце, у платформы и кодека нет особых требований, ниже объясним почему)

Часть 1.platform

1.1 cpu_dai

 При установке s3c2440_iis.ko драйвер платформы s3c24xx_iis_driver будет зарегистрирован. Поскольку в ядре есть платформенное устройство с тем же именем, что и «s3c24xx-iis», вызывается соответствующая функция зонда s3c24xx_iis_dev_probe. Эта функция в конечном итоге вызовет аппаратную операцию s3c24xx_i2s_dai (cp) ( Введите связанный список dai_list и назовите его «s3c24xx-iis» (драйвер машины позже создает экземпляр звуковой карты на основе этого имени)

1. platform:
1.1 s3c24xx-i2s.c : 把s3c24xx_i2s_dai放入链表dai_list, .name = "s3c24xx-iis",
s3c24xx_iis_dev_probe
  snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);
    list_add(&dai->list, &dai_list);
平台驱动
static struct platform_driver s3c24xx_iis_driver = {
	.probe  = s3c24xx_iis_dev_probe,
	.remove = s3c24xx_iis_dev_remove,
	.driver = {
		.name = "s3c24xx-iis",
		.owner = THIS_MODULE,
	},
};

平台设备
struct platform_device s3c_device_iis = {
	.name		  = "s3c24xx-iis",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_iis_resource),
	.resource	  = s3c_iis_resource,
	.dev              = {
		.dma_mask = &s3c_device_iis_dmamask,
		.coherent_dma_mask = 0xffffffffUL
	}
};
static struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
	.trigger	= s3c24xx_i2s_trigger,
	.hw_params	= s3c24xx_i2s_hw_params,
	.set_fmt	= s3c24xx_i2s_set_fmt,
	.set_clkdiv	= s3c24xx_i2s_set_clkdiv,
	.set_sysclk	= s3c24xx_i2s_set_sysclk,
};

//cpu_dai相关的硬件操作
static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
	.probe = s3c24xx_i2s_probe,
	.suspend = s3c24xx_i2s_suspend,
	.resume = s3c24xx_i2s_resume,
	.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 = &s3c24xx_i2s_dai_ops,
};

1.2 platform_dma

При установке s3c2440_dma.ko поместите Samsung_asoc_platform в связанный список platform_list и назовите его «samsung-audio» (позже драйвер Machineko создает звуковую карту на основе этого имени)

1.2 sound/soc/samsung/dma.c : 把samsung_asoc_platform放入了链表platform_list, .name = "samsung-audio",
samsung_asoc_platform_probe
  snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
    list_add(&platform->list, &platform_list);
static struct snd_pcm_ops dma_ops = {
	.open		= dma_open,
	.close		= dma_close,
	.ioctl		= snd_pcm_lib_ioctl,
	.hw_params	= dma_hw_params,
	.hw_free	= dma_hw_free,
	.prepare	= dma_prepare,
	.trigger	= dma_trigger,
	.pointer	= dma_pointer,
	.mmap		= dma_mmap,
};
//cpu_dam相关的硬件操作
static struct snd_soc_platform_driver samsung_asoc_platform = {
	.ops		= &dma_ops,
	.pcm_new	= dma_new,
	.pcm_free	= dma_free_dma_buffers,
};

2. Часть регистрации кодеков

При установке uda1341.ko поместите soc_codec_dev_uda134x и uda134x_dai в связанные списки dai_list и codec_list (используется для создания экземпляра звуковой карты с помощью драйвера Machineko)

2. codec: uda134x.c
uda134x_codec_probe
  snd_soc_register_codec(&pdev->dev,&soc_codec_dev_uda134x, &uda134x_dai, 1);
      struct snd_soc_codec *codec;
      codec->driver = codec_drv; = &soc_codec_dev_uda134x
      
      snd_soc_register_dais(dev, dai_drv, num_dai); // uda134x_dai
        list_add(&dai->list, &dai_list); : 把uda134x_dai放入了链表dai_list
      list_add(&codec->list, &codec_list); 把soc_codec_dev_uda134x放入了链表codec_list
//设置codec芯片的硬件操作
static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
	.probe =        uda134x_soc_probe,
	.remove =       uda134x_soc_remove,
	.suspend =      uda134x_soc_suspend,
	.resume =       uda134x_soc_resume,
	.reg_cache_size = sizeof(uda134x_reg),
	.reg_word_size = sizeof(u8),
	.reg_cache_default = uda134x_reg,
	.reg_cache_step = 1,
	.read = uda134x_read_reg_cache,
	.write = uda134x_write,
	.set_bias_level = uda134x_set_bias_level,
};

 

static struct snd_soc_dai_ops uda134x_dai_ops = {
	.startup	= uda134x_startup,
	.shutdown	= uda134x_shutdown,
	.hw_params	= uda134x_hw_params,
	.digital_mute	= uda134x_mute,
	.set_sysclk	= uda134x_set_dai_sysclk,
	.set_fmt	= uda134x_set_dai_fmt,
};

//codec_dai相关的硬件操作
static struct snd_soc_dai_driver uda134x_dai = {
	.name = "uda134x-hifi",
	/* 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 = &uda134x_dai_ops,
};

3. Машинная часть

Машинная часть имеет много контента, и общий процесс вызова выглядит следующим образом:

s3c24xx_uda134x_probe
  s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
  platform_set_drvdata(s3c24xx_uda134x_snd_device, &snd_soc_s3c24xx_uda134x);     
  platform_device_add(s3c24xx_uda134x_snd_device);
  
  .....
  soc_probe
    snd_soc_register_card(card);  // card = &snd_soc_s3c24xx_uda134x

      card->rtd = devm_kzalloc(card->dev,...
      card->rtd[i].dai_link = &card->dai_link[i];  // &s3c24xx_uda134x_dai_link
      
      list_add(&card->list, &card_list);
      
      snd_soc_instantiate_cards();  // 实例化声卡
          snd_soc_instantiate_card(card);
            3.1   /* bind DAIs */
                  for (i = 0; i < card->num_links; i++)
                    soc_bind_dai_link(card, i);
                        3.1.1 /* find CPU DAI */
                              rtd->cpu_dai = cpu_dai; = //&s3c24xx_i2s_dai
                        3.1.2 /* find_codec */
                              rtd->codec = codec;  = // codec, codec->driver=&soc_codec_dev_uda134x
                        3.1.3 /* find CODEC DAI */      
                              rtd->codec_dai = codec_dai; // = &uda134x_dai
                        3.1.4 /* find_platform */
                              rtd->platform = platform; // = &samsung_asoc_platform
            3.2 /* initialize the register cache for each available codec */
                ret = snd_soc_init_codec_cache(codec, compress_type);
                
            3.3 snd_card_create
			err = snd_ctl_create(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,
				};
				snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
					dev->ops = ops;
            
            3.4 /* early DAI link probe */
                soc_probe_dai_link    
                		/* probe the cpu_dai */
                		/* probe the CODEC */
                		/* probe the platform */
                		/* probe the CODEC DAI */
                		/* create the pcm */
                		ret = soc_new_pcm(rtd, num);
                			struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
							soc_pcm_ops->open	= soc_pcm_open;
							soc_pcm_ops->close	= soc_pcm_close;
							soc_pcm_ops->hw_params	= soc_pcm_hw_params;
							soc_pcm_ops->hw_free	= soc_pcm_hw_free;
							soc_pcm_ops->prepare	= soc_pcm_prepare;
							soc_pcm_ops->trigger	= soc_pcm_trigger;
							soc_pcm_ops->pointer	= soc_pcm_pointer;
                					
                			snd_pcm_new
								static struct snd_device_ops ops = {
									.dev_free = snd_pcm_dev_free,
									.dev_register =	snd_pcm_dev_register,
									.dev_disconnect = snd_pcm_dev_disconnect,
								};
								err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)
									dev->ops = ops;

								pcm->private_data = rtd;
            3.5 snd_card_register
			snd_device_register_all
				err = dev->ops->dev_register(dev)调用前面snd_pcm_new的snd_pcm_dev_register
                              		snd_pcm_dev_register
                           			err = snd_register_device_for_dev(devtype, pcm->card,
						  	pcm->device,
						  	&snd_pcm_f_ops[cidx],
						  	pcm, str, dev);

При установке s3c2440_uda1341.ko зарегистрируйте драйвер платформы s3c24xx_uda134x_driver. Так как в ядре есть платформенное устройство mini2440_audio с тем же именем, функция s3c24xx_uda134x_probe называется

//平台驱动
static struct platform_driver s3c24xx_uda134x_driver = {
	.probe  = s3c24xx_uda134x_probe,
	.remove = s3c24xx_uda134x_remove,
	.driver = {
		.name = "s3c24xx_uda134x",
		.owner = THIS_MODULE,
	},
};

//平台设备
static struct platform_device mini2440_audio = {
	.name		= "s3c24xx_uda134x",
	.id		= 0,
	.dev		= {
		.platform_data	= &mini2440_audio_pins,
	},
};

Функция ①s3c24xx_uda134x_probe вызов snd_soc_card platform_set_drvdata структура snd_soc_s3c24xx_uda134x (содержащий dai_link: Платформы отвечают за подключение
и кодек, будет использованы примеры того времени) хранятся в PDEV (platform_device) -> dev-> р-> driver_data в
и platform_device_add (s3c24xx_uda134x_snd_device ), Поскольку в ядре есть драйвер платформы soc_driver с тем же именем, поэтому вызовите функцию зонда драйвера (функция soc_probe)

    s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
    platform_set_drvdata(s3c24xx_uda134x_snd_device, &snd_soc_s3c24xx_uda134x); 
        dev_set_drvdata(&pdev->dev, data);
            dev->p->driver_data = data;    
    platform_device_add(s3c24xx_uda134x_snd_device);
//指定了platform和codec
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
	.name = "UDA134X",
	.stream_name = "UDA134X",
	.codec_name = "uda134x-codec",
	.codec_dai_name = "uda134x-hifi",
	.cpu_dai_name = "s3c24xx-iis",
	.ops = &s3c24xx_uda134x_ops,
	.platform_name	= "samsung-audio",
};

static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
	.name = "S3C24XX_UDA134X",
	.dai_link = &s3c24xx_uda134x_dai_link,
	.num_links = 1,        //只有1个dai_link
};
/* ASoC platform driver */
static struct platform_driver soc_driver = {
	.driver		= {
		.name		= "soc-audio",
		.owner		= THIS_MODULE,
		.pm		= &snd_soc_pm_ops,
	},
	.probe		= soc_probe,
	.remove		= soc_remove,
};

Soc Функция soc_probe вызывает функцию platform_get_drvdata для извлечения структуры snd_soc_card snd_soc_s3c24xx_uda134x (содержащей dai_link: используется для соединения Platform и Codec, которая будет использоваться во время создания экземпляра) шага ① , а затем вызывает функцию snd_soc_register_card для регистрации.

soc_probe
    struct snd_soc_card *card = platform_get_drvdata(pdev);
        return dev->p->driver_data;
    snd_soc_register_card(card);  // card = &snd_soc_s3c24xx_uda134x

③snd_soc_register_card функция Remove структура dai_link snd_soc_card (snd_soc_s3c24xx_uda134x структура только один dai_link, где для выполнения только один раз), затем называют snd_soc_instantiate_cards конкретизирует звук , по сути, определяется структурой snd_soc_s3c24xx_uda134x внутри dai_link имени, в dai_list, PLATFORM_LIST, Связанный список codec_list находит четыре структуры, которые наши части платформы и регистрации кодеков помещают в очередь . (Функция soc_bind_dai_link сканирует три связанных списка один за другим и сопоставляет их в соответствии с именами в card-> dai_link []. После сопоставления соответствующие экземпляры кодека, dai и платформы назначаются (snd_soc_card) card-> rtd [] (snd_soc_pcm_runtime). После этого процесса snd_soc_pcm_runtime: (card-> rtd) сохранил информацию о драйвере кодека, DAI и платформы, используемую на этом компьютере)

snd_soc_register_card
    card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
			    (card->num_links + card->num_aux_devs),
			    GFP_KERNEL);
    for (i = 0; i < card->num_links; i++)
		card->rtd[i].dai_link = &card->dai_link[i];
    list_add(&card->list, &card_list);
    snd_soc_instantiate_cards();
        snd_soc_instantiate_card(card);
            soc_bind_dai_link(card, i);
                /* find CPU DAI */
                rtd->cpu_dai = cpu_dai; = //&s3c24xx_i2s_dai
                /* find_codec */
                rtd->codec = codec;  = // codec, codec->driver=&soc_codec_dev_uda134x
                /* find CODEC DAI */      
                rtd->codec_dai = codec_dai; // = &uda134x_dai
                /* find_platform */
                rtd->platform = platform; // = &samsung_asoc_platform

Вызовите API традиционного драйвера звуковой карты ALSA, функцию snd_card_create, затем эта функция вызывает snd_ctl_create, функция snd_ctl_create вызывает snd_device_new, чтобы заполнить опцию структуры snd_device_ops для члена ops элемента snd_device (dev-> ops = ops ; _de_card, ss____card, s_s____card , затем и scard), а также ) Карта-> Список устройств. Член dev_register структуры snd_device_ops будет использоваться на шаге ⑧ snd_card_register позже. В настоящее время мы просто вешаем его в связанный список , как показано ниже:

            3.3 snd_card_create
			err = snd_ctl_create(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,
				};
				snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
					dev->ops = ops;

Soc Функция soc_probe_dai_link сначала вызывает функцию исследования, такую ​​как cpu_dai, найденную на предыдущем шаге (например, структура s3c24xx_i2s_dai s3c24xx_iis_dev_probe), то есть аппаратную инициализацию . Затем вызовите soc_new_pcm.

soc_probe_dai_link    
   /* probe the cpu_dai */
    ret = cpu_dai->driver->probe(cpu_dai);
   /* probe the CODEC */
    ret = soc_probe_codec(card, codec);
   /* probe the platform */
    ret = platform->driver->probe(platform);
   /* probe the CODEC DAI */
    ret = codec_dai->driver->probe(codec_dai);
   /* create the pcm */
    ret = soc_new_pcm(rtd, num);

⑥soc_new_pcm первой функции для функции карты-> rtd-> OPS структуры в функции присваивания указателя ( процедура APP вызывается программа будет снова вызывать эти функции с помощью функции аппаратной связанных (например, передняя платформы s3c24xx_i2s_dai) ), то Адрес card-> rtd-> ops назначается для подпотока-> ops . Наконец, вызовите snd_pcm_new.

ret = soc_new_pcm(rtd, num);
    struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;

    soc_pcm_ops->open	= soc_pcm_open;
    soc_pcm_ops->close	= soc_pcm_close;
    soc_pcm_ops->hw_params	= soc_pcm_hw_params;
    soc_pcm_ops->hw_free	= soc_pcm_hw_free;
    soc_pcm_ops->prepare	= soc_pcm_prepare;
    soc_pcm_ops->trigger	= soc_pcm_trigger;
    soc_pcm_ops->pointer	= soc_pcm_pointer;
                					
    snd_pcm_new

    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);
        substream->ops = ops;
或
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);
        substream->ops = ops;

Ndsnd_pcm_new - это традиционный API драйвера звуковой карты ALSA, этот шаг аналогичен шагу ④, отличие состоит в том, что шаг ④ соответствует snd_control, а здесь snd_pcm . После выполнения snd_pcm_new он возвращается к функции soc_new_pcm. Здесь (snd_pcm) pcm-> private_data = rtd; когда наше приложение вызывает программу, мы извлекаем rtd из pcm-> private_data (rtd имеет cpu_dai и codec_dai для нашей доски разработки ...)

snd_pcm_new
	static struct snd_device_ops ops = {
		.dev_free = snd_pcm_dev_free,
		.dev_register =	snd_pcm_dev_register,
		.dev_disconnect = snd_pcm_dev_disconnect,
	};
	err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)
			dev->ops = ops;

	pcm->private_data = rtd;

⑧snd_card_register, эта функция будет вызывать snd_ctl_dev_register и snd_pcm_dev_register в шагах ④ и ⑦ (зарегистрировать все логические устройства, висящие под звуковой картой через snd_device_register_all (), snd_device_register_all () на самом деле через список устройств snd_card, и обойти все s Вызовите ops-> dev_register () из snd_device для регистрации каждого устройства), эти две функции также являются API-интерфейсами традиционной звуковой карты ALSA, в конечном итоге они будут вызывать функцию snd_register_device_for_dev, snd_ctl_f_ops, snd_pcm_f_ops по мере появления параметров snd_register_device_for_dev И записывается в поле f_ops в snd_minors [minor], и создается узел устройства (device_create (class_create находится в sound_core.c)). Когда пользовательская программа должна открыть устройства управления и pcm, драйвер может получить различные функции обратного вызова в структурах snd_ctl_f_ops и snd_pcm_f_ops через глобальный массив snd_minors [] и этот номер устройства (представленный в другой статье)

3.5 snd_card_register
		snd_device_register_all
			err = dev->ops->dev_register(dev)调用前面snd_ctl_create的snd_ctl_dev_register和snd_pcm_new的snd_pcm_dev_register
				snd_ctl_dev_register
					snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, name)
                                            snd_register_device_for_dev
	                                            preg->type = type;
	                                            preg->card = card ? card->number : -1;
	                                            preg->device = dev;
	                                            preg->f_ops = f_ops;
	                                            preg->private_data = private_data;

	                                            snd_minors[minor] = preg;
	                                            preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name);

				和snd_pcm_dev_register
				 	err = snd_register_device_for_dev(devtype, pcm->card,
						  	pcm->device,
						  	&snd_pcm_f_ops[cidx],
						  	pcm, str, dev);
static const struct file_operations snd_ctl_f_ops =
{
	.owner =	THIS_MODULE,
	.read =		snd_ctl_read,
	.open =		snd_ctl_open,
	.release =	snd_ctl_release,
	.llseek =	no_llseek,
	.poll =		snd_ctl_poll,
	.unlocked_ioctl =	snd_ctl_ioctl,
	.compat_ioctl =	snd_ctl_ioctl_compat,
	.fasync =	snd_ctl_fasync,
};


const struct file_operations snd_pcm_f_ops[2] = {
	{
		.owner =		THIS_MODULE,
		.write =		snd_pcm_write,
		.aio_write =		snd_pcm_aio_write,
		.open =			snd_pcm_playback_open,
		.release =		snd_pcm_release,
		.llseek =		no_llseek,
		.poll =			snd_pcm_playback_poll,
		.unlocked_ioctl =	snd_pcm_playback_ioctl,
		.compat_ioctl = 	snd_pcm_ioctl_compat,
		.mmap =			snd_pcm_mmap,
		.fasync =		snd_pcm_fasync,
		.get_unmapped_area =	snd_pcm_get_unmapped_area,
	},
	{
		.owner =		THIS_MODULE,
		.read =			snd_pcm_read,
		.aio_read =		snd_pcm_aio_read,
		.open =			snd_pcm_capture_open,
		.release =		snd_pcm_release,
		.llseek =		no_llseek,
		.poll =			snd_pcm_capture_poll,
		.unlocked_ioctl =	snd_pcm_capture_ioctl,
		.compat_ioctl = 	snd_pcm_ioctl_compat,
		.mmap =			snd_pcm_mmap,
		.fasync =		snd_pcm_fasync,
		.get_unmapped_area =	snd_pcm_get_unmapped_area,
	}
};

 На этом этапе задача машинной части была выполнена.

 

4. Регистрация устройства персонажа

В файле sound / core / sound.c есть функция alsa_sound_init (). Основной параметр в register_chrdev такой же, как основной при создании устройства pcm - device_create. В результате, когда приложение открывает файл устройства / dev / snd / Когда pcmCxDxp, он входит в функцию обратного вызова open snd_fops. Функция open индексируется по младшему номеру устройства, а структура snd_minor, которая была заполнена при первоначальной регистрации устройства conrol и pcm, берется из глобального массива snd_minors. f_ops, замените file-> f_op на f_ops устройства pcm, затем напрямую вызовите f_ops-> open () устройства pcm ...

#define CONFIG_SND_MAJOR	116	/* standard configuration */
static int major = CONFIG_SND_MAJOR;

static const struct file_operations snd_fops =
{
	.owner =	THIS_MODULE,
	.open =		snd_open,
	.llseek =	noop_llseek,
};

alsa_sound_init
    register_chrdev(major, "alsa", &snd_fops)

Мы подробно рассмотрим процесс вызова в следующей статье.

 

 

Опубликовано 42 оригинальных статей · Мне нравится 10 · Посетителей 10 000+

рекомендация

отblog.csdn.net/qq_37659294/article/details/104748747
рекомендация