Controlador Linux ALSA tres: análisis del código fuente del proceso de creación de PCM (basado en Linux 5.18)

1. Conceptos básicos y relaciones lógicas

        Como se muestra arriba, a través del estudio de la tarjeta de sonido en la sección anterior, ya sabemos que PCM es un subdispositivo de la tarjeta de sonido, o representa una instancia de PCM.

        Cada tarjeta de sonido puede contener hasta 4 instancias de pcm y cada instancia de pcm corresponde a un archivo de dispositivo pcm. Esta limitación en el número de instancias de pcm se debe al tamaño de bits que ocupan los números de dispositivos de Linux.Si usamos números de dispositivos de 64 bits en el futuro, podremos crear más instancias de pcm. Sin embargo, la mayoría de las veces, en dispositivos integrados, una instancia de pcm es suficiente.

        Una instancia de pcm consta de un flujo de reproducción y un flujo de captura, y estos dos flujos se componen de uno o más subflujos. La siguiente figura se puede utilizar para representar su relación lógica directa:

        Cuando ya existe una subtransmisión y se ha abierto, se bloqueará cuando se vuelva a abrir.        

        En las aplicaciones reales, por lo general no es tan complicado como la figura anterior. En la mayoría de los casos, una tarjeta de sonido tiene una instancia de PCM, y hay reproducción y captura bajo el PCM, y cada reproducción y captura tienen una subtransmisión.

        La capa PCM tiene varias estructuras importantes, y clasificamos su relación directa a través del siguiente diagrama UML.

         Dirección de la imagen: http://hi.csdn.net/attachment/201104/2/0_1301728746sAUd.gif

        1. snd_pcm: un snd_device colgado debajo de snd_card.

        2. Campos en snd_pcm: streams[2] : los dos elementos de esta matriz apuntan a dos estructuras snd_pcm_str, que representan el flujo de reproducción y el flujo de captura, respectivamente.

        3. El campo de subflujo en snd_pcm_str: apunta a la estructura snd_pcm_substream.

        4. snd_pcm_substream es el núcleo de la capa intermedia de pcm. La mayoría de las tareas se procesan en el substream, especialmente su campo ops (snd_pcm_ops). Muchas aplicaciones de espacio de usuario solicitan el controlador a través de alsa-lib. Manejo de funciones en estructuras. Su campo de tiempo de ejecución apunta a la estructura snd_pcm_runtime, y snd_pcm_runtime registra algunos parámetros y entornos operativos de software y hardware importantes de esta subtransmisión.

2. Proceso de creación de PCM

        Para conocer todo el proceso de creación de PCM, consulte el siguiente diagrama de secuencia para comprenderlo:

         La capa intermedia de alsa-driver ha proporcionado una API para crear un nuevo PCM:

2.1 Crear una instancia de PCM

int snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, struct snd_pcm **rpcm)

        tarjeta: Indica la tarjeta de sonido a la que pertenece.

        ID: ID (nombre) de la instancia PCM.

        dispositivo: indica qué PCM se crea actualmente en la tarjeta de sonido y el primer dispositivo PCM comienza a contar desde 0.

        playback_count: indica cuántas subtransmisiones habrá en la transmisión de reproducción PCM.

        capture_count : indica cuántas subtransmisiones habrá en la transmisión de grabación PCM.

        rpcm: la instancia de PCM devuelta.

        La función principal de esta función es crear un dispositivo lógico PCM, crear una transmisión secundaria de reproducción y una instancia de transmisión secundaria de grabación, e inicializar las funciones de operación PCM de la transmisión secundaria de reproducción y la transmisión secundaria de grabación (cuando los datos se mueven , estas funciones deben llamarse para controlar el funcionamiento del dispositivo codec, codec_dai, cpu_dai y dma).

2.2 Establecer la función de operación del dispositivo PCM

void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
		     const struct snd_pcm_ops *ops)

        pcm: la instancia de PCM creada por el snd_pcm_new anterior .

        dirección: se refiere a SNDRV_PCM_STREAM_PLAYBACK o SNDRV_PCM_STREAM_CAPTURE, que está configurado para reproducir o grabar.

        snd_pcm_ops: Las funciones en la estructura suelen ser las funciones que impulsamos a implementar.

2.3 Definir la función de operación de PCM

        Tomando el controlador AC97 (linux/sound/arm/pxa2xx-ac97.c) como ejemplo, el PCM está configurado de la siguiente manera en el controlador:

static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = {
	.open		= pxa2xx_ac97_pcm_open,
	.close		= pxa2xx_ac97_pcm_close,
	.hw_params	= pxa2xx_pcm_hw_params,
	.prepare	= pxa2xx_ac97_pcm_prepare,
	.trigger	= pxa2xx_pcm_trigger,
	.pointer	= pxa2xx_pcm_pointer,
};

snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pxa2xx_ac97_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pxa2xx_ac97_pcm_ops);

2.4 Definir parámetros de hardware

static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
	.info			= SNDRV_PCM_INFO_MMAP |
				  SNDRV_PCM_INFO_MMAP_VALID |
				  SNDRV_PCM_INFO_INTERLEAVED |
				  SNDRV_PCM_INFO_PAUSE |
				  SNDRV_PCM_INFO_RESUME,
	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
				  SNDRV_PCM_FMTBIT_S24_LE |
				  SNDRV_PCM_FMTBIT_S32_LE,
	.period_bytes_min	= 32,
	.period_bytes_max	= 8192 - 32,
	.periods_min		= 1,
	.periods_max		= 256,
	.buffer_bytes_max	= 128 * 1024,
	.fifo_size		= 32,
};

int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_dmaengine_dai_dma_data *dma_params;
	int ret;

	runtime->hw = pxa2xx_pcm_hardware;

	dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
	if (!dma_params)
		return 0;

	/*
	 * For mysterious reasons (and despite what the manual says)
	 * playback samples are lost if the DMA count is not a multiple
	 * of the DMA burst size.  Let's add a rule to enforce that.
	 */
	ret = snd_pcm_hw_constraint_step(runtime, 0,
		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
	if (ret)
		return ret;

	ret = snd_pcm_hw_constraint_step(runtime, 0,
		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
	if (ret)
		return ret;

	ret = snd_pcm_hw_constraint_integer(runtime,
					    SNDRV_PCM_HW_PARAM_PERIODS);
	if (ret < 0)
		return ret;

	return snd_dmaengine_pcm_open(
		substream, dma_request_slave_channel(asoc_rtd_to_cpu(rtd, 0)->dev,
						     dma_params->chan_name));
}

3. Análisis de código fuente relacionado con PCM

3.1, snd_pcm_nuevo

/**
 * snd_pcm_new - create a new PCM instance
 * @card: the card instance
 * @id: the id string
 * @device: the device index (zero based)
 * @playback_count: the number of substreams for playback
 * @capture_count: the number of substreams for capture
 * @rpcm: the pointer to store the new pcm instance
 *
 * Creates a new PCM instance.
 *
 * The pcm operators have to be set afterwards to the new instance
 * via snd_pcm_set_ops().
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, struct snd_pcm **rpcm)
{
    /* 直接调用函数_snd_pcm_new,参数internal传入false */
	return _snd_pcm_new(card, id, device, playback_count, capture_count,
			false, rpcm);
}

static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, bool internal,
		struct snd_pcm **rpcm)
{
	struct snd_pcm *pcm;
	int err;
    /* 1. 逻辑设备的操作函数结构体, 主要用于注册子设备 */
	static const struct snd_device_ops ops = {
		.dev_free = snd_pcm_dev_free,
		.dev_register =	snd_pcm_dev_register,
		.dev_disconnect = snd_pcm_dev_disconnect,
	};
	static const struct snd_device_ops internal_ops = {
		.dev_free = snd_pcm_dev_free,
	};

	if (snd_BUG_ON(!card))
		return -ENXIO;
	if (rpcm)
		*rpcm = NULL;
    /* 2. 为snd_pcm结构体分配空间,根据传入参数赋值 */
	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
	if (!pcm)
		return -ENOMEM;
	pcm->card = card;
	pcm->device = device;
	pcm->internal = internal;
	mutex_init(&pcm->open_mutex);
	init_waitqueue_head(&pcm->open_wait);
	INIT_LIST_HEAD(&pcm->list);
	if (id)
		strscpy(pcm->id, id, sizeof(pcm->id));

    /* 3. 根据传入的playback和capture的个数创建PCM流 snd_pcm_str */
	err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
				 playback_count);
	if (err < 0)
		goto free_pcm;

	err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);
	if (err < 0)
		goto free_pcm;

    /* 4. 创建一个PCM逻辑设备,创建逻辑设备,并添加到逻辑设备链表 */
	err = snd_device_new(card, SNDRV_DEV_PCM, pcm,
			     internal ? &internal_ops : &ops);
	if (err < 0)
		goto free_pcm;

	if (rpcm)
		*rpcm = pcm;
	return 0;

free_pcm:
	snd_pcm_free(pcm);
	return err;
}

3.2, snd_pcm

struct snd_pcm {
	struct snd_card *card;
	struct list_head list;
	int device; /* device number */
	unsigned int info_flags;
	unsigned short dev_class;
	unsigned short dev_subclass;
	char id[64];
	char name[80];
	struct snd_pcm_str streams[2];
	struct mutex open_mutex;
	wait_queue_head_t open_wait;
	void *private_data;
	void (*private_free) (struct snd_pcm *pcm);
	bool internal; /* pcm is for internal use only */
	bool nonatomic; /* whole PCM operations are in non-atomic context */
	bool no_device_suspend; /* don't invoke device PM suspend */
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
	struct snd_pcm_oss oss;
#endif
};

         Las variables importantes aquí son dos flujos y datos_privados. Hay dos flujos porque uno apunta al dispositivo de reproducción y el otro apunta al dispositivo de grabación. private_data se puede ver en muchas estructuras, y es algo similar a la herencia en los objetos de superficie.Si snd_pcm se entiende como una clase base, private_data apunta a su clase heredada, que es el implementador real.

        list, hay una variable global snd_pcm_devices en pcm.c, que vincula todos los objetos snd_pcm, el propósito es proporcionar algunas interfaces externas para enumerar todos los dispositivos, que no parece usarse mucho.

        Además, variables como info_flags y dev_class parecen estar reservadas para algunos dispositivos especiales y tratan algunas operaciones especiales.

struct snd_pcm_str {
	int stream;				/* stream (direction) */
	struct snd_pcm *pcm;
	/* -- substreams -- */
	unsigned int substream_count;
	unsigned int substream_opened;
	struct snd_pcm_substream *substream;
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
	/* -- OSS things -- */
	struct snd_pcm_oss_stream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
	struct snd_info_entry *proc_root;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
	unsigned int xrun_debug;	/* 0 = disabled, 1 = verbose, 2 = stacktrace */
#endif
#endif
	struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
	struct device dev;
};

         La función principal de snd_pcm_str es apuntar a snd_pcm_substream, y snd_pcm_substream puede tener múltiples, razón por la cual existe snd_pcm_str, de lo contrario, snd_pcm puede apuntar directamente a snd_pcm_substream.

        El dev aquí se usa al agregar pcm al sistema de archivos. La información contenida se verá en el snd_pcm_new_stream que se presenta a continuación.

3.3, snd_pcm_new_stream

/**
 * snd_pcm_new_stream - create a new PCM stream
 * @pcm: the pcm instance
 * @stream: the stream direction, SNDRV_PCM_STREAM_XXX
 * @substream_count: the number of substreams
 *
 * Creates a new stream for the pcm.
 * The corresponding stream on the pcm must have been empty before
 * calling this, i.e. zero must be given to the argument of
 * snd_pcm_new().
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
	int idx, err;
    /* 3.1 根据传入的参数,为PCM流(snd_pcm_str)赋值:方向,所属的PCM,PCM子流的个数 */
	struct snd_pcm_str *pstr = &pcm->streams[stream];
	struct snd_pcm_substream *substream, *prev;

#if IS_ENABLED(CONFIG_SND_PCM_OSS)
	mutex_init(&pstr->oss.setup_mutex);
#endif
	pstr->stream = stream;
	pstr->pcm = pcm;
	pstr->substream_count = substream_count;
	if (!substream_count)
		return 0;

	snd_device_initialize(&pstr->dev, pcm->card);
	pstr->dev.groups = pcm_dev_attr_groups;
	pstr->dev.type = &pcm_dev_type;
	dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
		     stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');

    /* proc */
	if (!pcm->internal) {
		err = snd_pcm_stream_proc_init(pstr);
		if (err < 0) {
			pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");
			return err;
		}
	}
	prev = NULL;
	for (idx = 0, prev = NULL; idx < substream_count; idx++) {
        /* 为子流分配空间,赋值(pcm,pcm流,ID, 方向.....) */
		substream = kzalloc(sizeof(*substream), GFP_KERNEL);
		if (!substream)
			return -ENOMEM;
		substream->pcm = pcm;
		substream->pstr = pstr;
		substream->number = idx;
		substream->stream = stream;
		sprintf(substream->name, "subdevice #%i", idx);
		substream->buffer_bytes_max = UINT_MAX;
        /* 添加子流到子流的链表 */
		if (prev == NULL)    /* 第一个子流 */
			pstr->substream = substream;
		else
			prev->next = substream;    /* 非第一个子流,添加到前一个子流后部 */
        /* proc */
		if (!pcm->internal) {
			err = snd_pcm_substream_proc_init(substream);
			if (err < 0) {
				pcm_err(pcm,
					"Error in snd_pcm_stream_proc_init\n");
				if (prev == NULL)
					pstr->substream = NULL;
				else
					prev->next = NULL;
				kfree(substream);
				return err;
			}
		}
        /* 结构体初始化 */
		substream->group = &substream->self_group;
		snd_pcm_group_init(&substream->self_group);
		list_add_tail(&substream->link_list, &substream->self_group.substreams);
		atomic_set(&substream->mmap_count, 0);
		prev = substream;
	}
	return 0;
}

         El flujo int en el parámetro de función es un tipo de enumeración: 

enum {
	SNDRV_PCM_STREAM_PLAYBACK = 0,
	SNDRV_PCM_STREAM_CAPTURE,
	SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE,
};

        Comience con snd_device_initialize(&pstr->dev, pcm->card); dev eventualmente se pasará a la función device_add para construir el sistema de archivos.

void snd_device_initialize(struct device *dev, struct snd_card *card)
{
	device_initialize(dev);
	if (card)
		dev->parent = &card->card_dev;
	dev->class = sound_class;
	dev->release = default_release;
}

        En esta función, puede ver que dev->class está configurado en sound_class, por lo que los archivos que mencionamos antes se colocan en el directorio snd.

3.4, snd_pcm_substream

struct snd_pcm_substream {
	struct snd_pcm *pcm;
	struct snd_pcm_str *pstr;
	void *private_data;		/* copied from pcm->private_data */
	int number;
	char name[32];			/* substream name */
	int stream;			/* stream (direction) */
	struct pm_qos_request latency_pm_qos_req; /* pm_qos request */
	size_t buffer_bytes_max;	/* limit ring buffer size */
	struct snd_dma_buffer dma_buffer;
	size_t dma_max;
	/* -- hardware operations -- */
	const struct snd_pcm_ops *ops;
	/* -- runtime information -- */
	struct snd_pcm_runtime *runtime;
        /* -- timer section -- */
	struct snd_timer *timer;		/* timer */
	unsigned timer_running: 1;	/* time is running */
	long wait_time;	/* time in ms for R/W to wait for avail */
	/* -- next substream -- */
	struct snd_pcm_substream *next;
	/* -- linked substreams -- */
	struct list_head link_list;	/* linked list member */
	struct snd_pcm_group self_group;	/* fake group for non linked substream (with substream lock inside) */
	struct snd_pcm_group *group;		/* pointer to current group */
	/* -- assigned files -- */
	int ref_count;
	atomic_t mmap_count;
	unsigned int f_flags;
	void (*pcm_release)(struct snd_pcm_substream *);
	struct pid *pid;
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
	/* -- OSS things -- */
	struct snd_pcm_oss_substream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
	struct snd_info_entry *proc_root;
#endif /* CONFIG_SND_VERBOSE_PROCFS */
	/* misc flags */
	unsigned int hw_opened: 1;
	unsigned int managed_buffer_alloc:1;
};

        snd_pcm_substream tiene mucho contenido, solo los importantes deben introducirse aquí.

        private_data: copiado de private_data en snd_pcm, apuntando a la estructura del implementador.

        const struct snd_pcm_ops *ops: esta parte es el contenido del marco, la operación específica requiere la participación del implementador y el conjunto de punteros de función dejados para el implementador. Esto es coherente con la estrategia de diseño para las operaciones con archivos.

        struct snd_pcm_runtime *runtime: Controla al leer y escribir datos. Cuando llegue el momento de analizar el código de lectura y escritura, será el foco.

        struct snd_pcm_substream *siguiente: vincula varios objetos snd_pcm_substream, que es el vínculo al que apunta snd_pcm_str .

        grupo: en el espacio del usuario, se pueden vincular varias subtransmisiones a través de SNDRV_PCM_IOCTL_LINK. Luego puede realizar operaciones unificadas en estos objetos. No he encontrado un escenario de aplicación específico.

3.5, snd_pcm_set_ops

/**
 * snd_pcm_set_ops - set the PCM operators
 * @pcm: the pcm instance
 * @direction: stream direction, SNDRV_PCM_STREAM_XXX
 * @ops: the operator table
 *
 * Sets the given PCM operators to the pcm instance.
 */
void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
		     const struct snd_pcm_ops *ops)
{
	struct snd_pcm_str *stream = &pcm->streams[direction];
	struct snd_pcm_substream *substream;
	
	for (substream = stream->substream; substream != NULL; substream = substream->next)
		substream->ops = ops;
}
EXPORT_SYMBOL(snd_pcm_set_ops);

        Esta función se proporciona al lado que llama. Para conocer el contenido de la configuración, consulte el diagrama simplificado de la estructura del archivo pcm. 

3.6, snd_pcm_dev_registrar

        Antes de continuar con el análisis de la función snd_pcm_dev_register, es necesario introducir una estructura. estructura snd_menor.

struct snd_minor {
	int type;			/* SNDRV_DEVICE_TYPE_XXX */
	int card;			/* card number */
	int device;			/* device number */
	const struct file_operations *f_ops;	/* file operations */
	void *private_data;		/* private data for f_ops->open */
	struct device *dev;		/* device for sysfs */
	struct snd_card *card_ptr;	/* assigned card instance */
};

        tipo: tipo de dispositivo, como pcm, control, temporizador y otros dispositivos.

        card_number: la tarjeta a la que pertenece.

        dispositivo: El número de dispositivo bajo el tipo de dispositivo actual.

        f_ops: una colección de operaciones de archivos para dispositivos específicos.

        private_data: datos privados de la función abierta.

        card_ptr: la tarjeta a la que pertenece.

        Esta estructura se utiliza para guardar la información de contexto del dispositivo actual, y todos los dispositivos lógicos de esta tarjeta tienen esta estructura.

static int snd_pcm_dev_register(struct snd_device *device)
{
    /* 1、添加pcm结构体到全局链表snd_pcm_devices */
	int cidx, err;
	struct snd_pcm_substream *substream;
	struct snd_pcm *pcm;

	if (snd_BUG_ON(!device || !device->device_data))
		return -ENXIO;
    /* snd_devcie保存的是snd_pcm对象 */
	pcm = device->device_data;

	mutex_lock(&register_mutex);
    /* snd_pcm对象将被保存到全局变量snd_pcm_devices中,用于枚举设备等操作 */
	err = snd_pcm_add(pcm);
	if (err)
		goto unlock;
	for (cidx = 0; cidx < 2; cidx++) {
        /* 2、确定PCM设备节点名字 */
		int devtype = -1;
		if (pcm->streams[cidx].substream == NULL)
			continue;
		switch (cidx) {
		case SNDRV_PCM_STREAM_PLAYBACK:
			devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
			break;
		case SNDRV_PCM_STREAM_CAPTURE:
			devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
			break;
		}
		/* register pcm */
        /* 将设备添加到文件系统,将snd_pcm_f_ops传入,将被设置给snd_minor对象 */
		err = snd_register_device(devtype, pcm->card, pcm->device,
					  &snd_pcm_f_ops[cidx], pcm,
					  &pcm->streams[cidx].dev);
		if (err < 0) {
			list_del_init(&pcm->list);
			goto unlock;
		}

		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
            /* 设定CONFIG_SND_PCM_TIMER宏的时候,会去设置substream的时间 */
			snd_pcm_timer_init(substream);
	}

	pcm_call_notify(pcm, n_register);

 unlock:
	mutex_unlock(&register_mutex);
	return err;
}

/**
 * snd_register_device - Register the ALSA device file for the card
 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 * @card: the card instance
 * @dev: the device index
 * @f_ops: the file operations
 * @private_data: user pointer for f_ops->open()
 * @device: the device to register
 *
 * Registers an ALSA device file for the given card.
 * The operators have to be set in reg parameter.
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_register_device(int type, struct snd_card *card, int dev,
			const struct file_operations *f_ops,
			void *private_data, struct device *device)
{
	int minor;
	int err = 0;
	struct snd_minor *preg;

	if (snd_BUG_ON(!device))
		return -EINVAL;

	preg = kmalloc(sizeof *preg, GFP_KERNEL);
	if (preg == NULL)
		return -ENOMEM;
    /* 创建一个snd_minor,并添加到全局结构体 snd_minors */
	preg->type = type;
	preg->card = card ? card->number : -1;
	preg->device = dev;
	preg->f_ops = f_ops;
	preg->private_data = private_data;
	preg->card_ptr = card;
	mutex_lock(&sound_mutex);
    /* 4、注册一个设备节点 */
	minor = snd_find_free_minor(type, card, dev);
	if (minor < 0) {
		err = minor;
		goto error;
	}

	preg->dev = device;
	device->devt = MKDEV(major, minor);
	err = device_add(device);
	if (err < 0)
		goto error;

	snd_minors[minor] = preg;
 error:
	mutex_unlock(&sound_mutex);
	if (err < 0)
		kfree(preg);
	return err;
}

        Cuando se registra la tarjeta de sonido, se registran todos los dispositivos lógicos. El trabajo principal es crear un nodo de dispositivo PCM
El proceso específico:

                1. Agregue la estructura pcm a la lista enlazada global snd_pcm_devices.

                2. Determine el nombre de nodo del dispositivo PCM.

                3. Cree un snd_minor y agréguelo a la estructura global snd_minors.

                4. Registre un nodo de dispositivo

        Puede ver que el dispositivo de reproducción y el dispositivo de grabación se agregan al sistema de archivos, que se configuran de acuerdo con el contenido señalado por snd_pcm_str. En el código se ve que snd_pcm también se define como un dispositivo SNDRV_DEV_PCM, pero este tipo de dispositivo no se guarda en el sistema de archivos.

        snd_pcm_timer_init funcionará cuando se defina la macro CONFIG_SND_PCM_TIMER.

        El siguiente diagrama puede ayudarlo a comprender mejor la relación directa sin sentido entre cada estructura.

Supongo que te gusta

Origin blog.csdn.net/code_lyb/article/details/126144107
Recomendado
Clasificación