PCM的create和open过程及核心层数据结构[kernel 4.19]

PCM的create和open过程及核心层数据结构[kernel 4.19]

4.19 核心层数据结构

核心层为用户空间提供逻辑设备接口, 同时为驱动提供接口来驱动硬件设备, 主要位于sound/core目录下.

核心层数据结构自上向下为

snd_card      //表示一个声卡实例, 包含多个声卡设备
└──snd_device    //表示一个声卡设备部件
   └── snd_pcm       //表示一个PCM设备, 声卡设备的一种, 用于播放和录音
   │   └── snd_pcm_str   //表示PCM流, 分为playback和capture
   │	   └── snd_pcm_substream //PCM子流, 用于音频的播放或录制
   │	       └── snd_pcm_ops   //PCM流操作集
   └── snd_control   //表示Control设备, 声卡设备的一种, 用于控制声卡

此处暂时先略过snd_control部分的分析。

snd_card

struct snd_card {
	int number;			/* number of soundcard (声卡设备的索引) */

	char id[16];			/* id string of this card(标识符) */
	char driver[16];		/* 驱动名 */
	char shortname[32];		/* 声卡的短名 */
	char longname[80];		/* 声卡名 */
	char irq_descr[32];		/* Interrupt description */
	char mixername[80];		/* mixer name */
	char components[128];		/* card components delimited with space */
	struct module *module;		/* top-level module */
	
	void *private_data;		/* 声卡私有数据 */
	void (*private_free) (struct snd_card *card); /* 私有数据释放回调 */
	struct list_head devices;	/* 该声卡下所有设备 */
	
	struct device ctl_dev;		/* control设备*/
	unsigned int last_numid;	/* last used numeric ID */
	struct rw_semaphore controls_rwsem;	/* controls list lock */
	rwlock_t ctl_files_rwlock;	/* ctl_files list lock */
	int controls_count;		/* count of all controls */
	int user_ctl_count;		/* count of all user controls */
	struct list_head controls;	/* 该声卡下所有control设备 */
	struct list_head ctl_files;	/* active control files */
	
	struct snd_info_entry *proc_root;	/* root for soundcard specific files */
	struct snd_info_entry *proc_id;	/* the card id */
	struct proc_dir_entry *proc_root_link;	/* number link to real id */
	
	struct list_head files_list;	/* 与此声卡关联的所有文件 */
	struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown state */
	spinlock_t files_lock;		/* lock the files for this card */
	int shutdown;			/* this card is going down */
	struct completion *release_completion;
	struct device *dev;		/* 声卡相关的device */
	struct device card_dev;		/* cardX object for sysfs(用于sysfs, 代表该声卡) */
	const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
	bool registered;		/* card_dev is registered?(是否注册标记) */
	wait_queue_head_t remove_sleep;

#ifdef CONFIG_PM
	unsigned int power_state;	/* power state */
	wait_queue_head_t power_sleep;
#endif

#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
	struct snd_mixer_oss *mixer_oss;
	int mixer_oss_change_count;
#endif
};

snd_device

struct snd_device {
	struct list_head list;		/* 所有注册的device链表 */
	struct snd_card *card;		/*设备所属声卡 */
	enum snd_device_state state;	/* 设备状态 */
	enum snd_device_type type;	/* 设备类型 */
	void *device_data;		/* device structure,指向具体的声卡设备,如snd_pcm或snd_control */
	struct snd_device_ops *ops;	/* 设备操作函数集 */
};

snd_pcm

在调用_snd_pcm_new时就会创建一个snd_pcm类型的PCM 实例.

struct snd_pcm {
	struct snd_card *card;//该PCM设备所属声卡
	struct list_head list;//所有注册的PCM设备链表,一个Card可能有多个PCM 实例
	int device; /* device number,即PCM设备的索引 */
	unsigned int info_flags;
	unsigned short dev_class;
	unsigned short dev_subclass;
	char id[64]; //PCM设备标识
	char name[80]; //PCM设备名
	struct snd_pcm_str streams[2];//PCM的playback和capture stream
	struct mutex open_mutex;
	wait_queue_head_t open_wait;
	void *private_data;//PCM设备私有数据
	void (*private_free) (struct snd_pcm *pcm);//用来释放private_data
	bool internal; /* pcm is for internal use only */
	bool nonatomic; /* whole PCM operations are in non-atomic context */
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
	struct snd_pcm_oss oss;
#endif
};

snd_pcm_str

一个PCM 实例分别有一个playback, capture stream,由snd_pcm_new_stream创建。

playback 和capture stream是一个snd_pcm_str的结构体。

struct snd_pcm_str {
	int stream;				/* stream (direction),playback stream 或 capture stream */
	struct snd_pcm *pcm;//当前的PCM 实例
	/* -- substreams -- */
	unsigned int substream_count;//substream的数目。
	unsigned int substream_opened;//已经open的substream数目。每次open +1,close -1.
	struct snd_pcm_substream *substream;//playback stream 或 capture stream的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;
	struct snd_info_entry *proc_info_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
	unsigned int xrun_debug;	/* 0 = disabled, 1 = verbose, 2 = stacktrace */
	struct snd_info_entry *proc_xrun_debug_entry;
#endif
#endif
	struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
	struct device dev;//stream的device结构
};


snd_pcm_substream

substream的结构体snd_pcm_substream :

struct snd_pcm_substream {
	struct snd_pcm *pcm;//PCM 实例
	struct snd_pcm_str *pstr;//playback stream or substream
    //private_data通常是和PCM 实例的private data相同。
	void *private_data;		/* copied from pcm->private_data */
	int number;//当前substream的index
	char name[32];			/* substream name */
	int stream;			/* stream (direction),playback or capture */
	struct pm_qos_request latency_pm_qos_req; /* pm_qos request */
	size_t buffer_bytes_max;	/* limit ring buffer size,最大的buffer size */
	struct snd_dma_buffer dma_buffer;
	size_t dma_max;
	/* -- hardware operations -- */
	const struct snd_pcm_ops *ops;  //substream的操作函数,在创建完PCM 实例后,调用snd_pcm_set_ops设置PCM实例playback stream的每一个substream的操作函数。
	/* -- runtime information -- */
	struct snd_pcm_runtime *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 -- */
	void *file;
	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;
	struct snd_info_entry *proc_info_entry;
	struct snd_info_entry *proc_hw_params_entry;
	struct snd_info_entry *proc_sw_params_entry;
	struct snd_info_entry *proc_status_entry;
	struct snd_info_entry *proc_prealloc_entry;
	struct snd_info_entry *proc_prealloc_max_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
	struct snd_info_entry *proc_xrun_injection_entry;
#endif
#endif /* CONFIG_SND_VERBOSE_PROCFS */
	/* misc flags */
	unsigned int hw_opened: 1;
};

4.19 pcm创建过程

首先,在soc-core.c中

soc_probe [soc-core.c]

/* probes a new socdev */
static int soc_probe(struct platform_device *pdev)
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);

	/*
	 * no card, so machine driver should be registering card
	 * we should not be here in that case so ret error
	 */
	if (!card)
		return -EINVAL;
	
	dev_warn(&pdev->dev,
		 "ASoC: machine %s should use snd_soc_register_card()\n",
		 card->name);
	
	/* Bodge while we unpick instantiation */
	card->dev = &pdev->dev;
	
	return snd_soc_register_card(card);
}

snd_soc_register_card

Register a card with the ASoC core

/**
 * snd_soc_register_card - Register a card with the ASoC core
 *
 * @card: Card to register
 */
   int snd_soc_register_card(struct snd_soc_card *card)
   {
   int i, ret;
   struct snd_soc_pcm_runtime *rtd;

   if (!card->name || !card->dev)
   	return -EINVAL;

   for (i = 0; i < card->num_links; i++) {
   	struct snd_soc_dai_link *link = &card->dai_link[i];

   	ret = soc_init_dai_link(card, link);
   	if (ret) {
   		dev_err(card->dev, "ASoC: failed to init link %s\n",
   			link->name);
   		return ret;
   	}

   }

   dev_set_drvdata(card->dev, card);

   snd_soc_initialize_card_lists(card);

   INIT_LIST_HEAD(&card->dai_link_list);
   card->num_dai_links = 0;

   INIT_LIST_HEAD(&card->rtd_list);
   card->num_rtd = 0;

   INIT_LIST_HEAD(&card->dapm_dirty);
   INIT_LIST_HEAD(&card->dobj_list);
   card->instantiated = 0;
   mutex_init(&card->mutex);
   mutex_init(&card->dapm_mutex);

   ret = snd_soc_instantiate_card(card);
   if (ret != 0)
   	return ret;

   /* deactivate pins to sleep state */
   list_for_each_entry(rtd, &card->rtd_list, list)  {
   	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
   	int j;

   	for (j = 0; j < rtd->num_codecs; j++) {
   		struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
   		if (!codec_dai->active)
   			pinctrl_pm_select_sleep_state(codec_dai->dev);
   	}
   	
   	if (!cpu_dai->active)
   		pinctrl_pm_select_sleep_state(cpu_dai->dev);

   }

   return ret;
   }
   EXPORT_SYMBOL_GPL(snd_soc_register_card);

snd_soc_instantiate_card

太长,只列部分

static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
	struct snd_soc_pcm_runtime *rtd;
	struct snd_soc_dai_link *dai_link;
	int ret, i, order;

	
	/* add predefined DAI links to the list */
	for (i = 0; i < card->num_links; i++)
		snd_soc_add_dai_link(card, card->dai_link+i);
	
	/* card bind complete so register a sound card */
	ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
			card->owner, 0, &card->snd_card);
	if (ret < 0) {
		dev_err(card->dev,
			"ASoC: can't create sound card for card %s: %d\n",
			card->name, ret);
		goto base_error;
	}
	……

	snd_soc_dapm_new_widgets(card);
	
	ret = snd_card_register(card->snd_card);
	if (ret < 0) {
		dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
				ret);
		goto probe_aux_dev_err;
	}
	……
}

snd_card_register [init.c]

/**
 * snd_card_register - register the soundcard
 * @card: soundcard structure
 *
 * This function registers all the devices assigned to the soundcard.
 * Until calling this, the ALSA control interface is blocked from the
 * external accesses.  Thus, you should call this function at the end
 * of the initialization of the card.
 *
 * Return: Zero otherwise a negative error code if the registration failed.
 */
   int snd_card_register(struct snd_card *card)
   {
   int err;

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

   if (!card->registered) {
   	err = device_add(&card->card_dev);
   	if (err < 0)
   		return err;
   	card->registered = true;
   }

   if ((err = snd_device_register_all(card)) < 0)
   	return err;
   mutex_lock(&snd_card_mutex);
   if (snd_cards[card->number]) {
   	/* 已经注册过 */
   	mutex_unlock(&snd_card_mutex);
   	return snd_info_card_register(card); /* register pending info */
   }
   if (*card->id) {
   	/* 根据给定的字符串创建唯一的ID名称 */
   	char tmpid[sizeof(card->id)];
   	memcpy(tmpid, card->id, sizeof(card->id));
   	snd_card_set_id_no_lock(card, tmpid, tmpid);
   } else {
   	/* 从shortname或longname创建ID */
   	const char *src;
   	src = *card->shortname ? card->shortname : card->longname;
   	snd_card_set_id_no_lock(card, src,
   				retrieve_id_from_card_name(src));
   }
   snd_cards[card->number] = card;
   mutex_unlock(&snd_card_mutex);
   init_info_for_card(card);
   #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
   if (snd_mixer_oss_notify_callback)
   	snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
   #endif
   return 0;
   }
   EXPORT_SYMBOL(snd_card_register);

snd_device_register_all

/*
 * register all the devices on the card.
 * called from init.c
   */
   int snd_device_register_all(struct snd_card *card)
   {
   struct snd_device *dev;
   int err;

   if (snd_BUG_ON(!card))
   	return -ENXIO;
   list_for_each_entry(dev, &card->devices, list) {
   	err = __snd_device_register(dev);
   	if (err < 0)
   		return err;
   }
   return 0;
   }

__snd_device_register

static int __snd_device_register(struct snd_device *dev)
{
	if (dev->state == SNDRV_DEV_BUILD) {
		if (dev->ops->dev_register) {
			int err = dev->ops->dev_register(dev);
			if (err < 0)
				return err;
		}
		dev->state = SNDRV_DEV_REGISTERED;
	}
	return 0;
}

调用了pcm.c中的.dev_register = snd_pcm_dev_register

_snd_pcm_new [pcm.c]

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;
	static struct snd_device_ops ops = {//device的操作函数
		.dev_free = snd_pcm_dev_free,
		.dev_register =	snd_pcm_dev_register,//此处指定注册函数,在__snd_device_register调用
		.dev_disconnect = snd_pcm_dev_disconnect,
	};
	static struct snd_device_ops internal_ops = {
		.dev_free = snd_pcm_dev_free,
	};

	if (snd_BUG_ON(!card))
		return -ENXIO;
	if (rpcm)
		*rpcm = NULL;
	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);//为PCM 实例创建空间
	if (!pcm)
		return -ENOMEM;
	pcm->card = card;//将声卡保存在PCM 实例中
	pcm->device = device;
	pcm->internal = internal;
	mutex_init(&pcm->open_mutex);
	init_waitqueue_head(&pcm->open_wait);
	INIT_LIST_HEAD(&pcm->list);
	if (id)
		strlcpy(pcm->id, id, sizeof(pcm->id));
	//为PCM 实例创建playback stream
	err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
				 playback_count);
	if (err < 0)
		goto free_pcm;
	//为PCM 实例创建capture stream
	err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);
	if (err < 0)
		goto free_pcm;
	//以PCM 实例创建snd_device并挂载到Card上。
	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;
}
snd_pcm_new_stream
/**
 * snd_pcm_new_stream - 创建一个新的 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;
   //当前的playback stream or capture stream
   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;
	dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
		     stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); //设置pcm设备名称
	
	if (!pcm->internal) {
		err = snd_pcm_stream_proc_init(pstr);//将playback or capture stream挂载到/proc目录下
		if (err < 0) {
			pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");
			return err;
		}
	}
	prev = NULL;
    //一个playback or capture stream下可有多个substream,创建substream
	for (idx = 0, prev = NULL; idx < substream_count; idx++) {
		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)//将substream链接到playback or capture stream的substream成员中。
			pstr->substream = substream;
		else
			prev->next = substream;
	
		if (!pcm->internal) {
			err = snd_pcm_substream_proc_init(substream); //将substream挂到/proc下。
			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;
		spin_lock_init(&substream->self_group.lock);
		mutex_init(&substream->self_group.mutex);
		INIT_LIST_HEAD(&substream->self_group.substreams);
		list_add_tail(&substream->link_list, &substream->self_group.substreams);
		atomic_set(&substream->mmap_count, 0);
		prev = substream;
	}
	return 0;

}				
EXPORT_SYMBOL(snd_pcm_new_stream);
snd_pcm_dev_register
static int snd_pcm_dev_register(struct snd_device *device)
{
	int cidx, err;
	struct snd_pcm_substream *substream;
	struct snd_pcm *pcm;

	if (snd_BUG_ON(!device || !device->device_data))
		return -ENXIO;
    //snd_device的device_data即PCM实例,是在snd_device_new时将pcm赋值给device_data
	pcm = device->device_data;
	
	mutex_lock(&register_mutex);
	err = snd_pcm_add(pcm);
	if (err)
		goto unlock;
	for (cidx = 0; cidx < 2; cidx++) {
		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 */
        //注册pcm实例的device,device的操作函数为snd_pcm_f_ops.
		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)
			snd_pcm_timer_init(substream);//初始化每个substream 的timer
	}

	pcm_call_notify(pcm, n_register);

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

Register the ALSA device file for the card

/**
 * 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;
   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);
   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;
   }
   EXPORT_SYMBOL(snd_register_device);

4.19 pcm open过程

大致流程如下:

snd_pcm_open("default", SND_PCM_STREAM_PLAYBACK)  // alsa-lib接口
├── open("/dev/snd/controlC0")         // 打开控制设备; 主设备116, 次设备0
└── open("/dev/snd/pcmC0D0p")          // 打开PCM设备; 主设备116, 次设备16
    └──snd_open()                       // 根据主设备号找到该入口
       └──snd_minors[minor]              // 根据次设备号找到对应操作集
          └──snd_ctl_f_ops::open()        // control设备打开方法
          │  └──snd_ctl_open()
          └──snd_pcm_f_ops::open()        // PCM设备打开方法
             └──snd_pcm_playback_open()
                └──snd_lookup_minor_data()  // 根据次设备号查找对应PCM设备(snd_pcm)
                └──snd_pcm_open()           // 打开PCM播放子流

pcm_native.c中

snd_pcm_f_ops

const struct file_operations snd_pcm_f_ops[2] = {
    {
        .owner =        THIS_MODULE,
        .write =        snd_pcm_write,
        .write_iter =        snd_pcm_writev,
        .open =            snd_pcm_playback_open, //  打开/dev/snd/pcmC0D0p时调用到此函数
        .release =        snd_pcm_release,
        .llseek =        no_llseek,
        .poll =            snd_pcm_poll,
        .unlocked_ioctl =    snd_pcm_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,
        .read_iter =        snd_pcm_readv,
        .open =            snd_pcm_capture_open,
        .release =        snd_pcm_release,
        .llseek =        no_llseek,
        .poll =            snd_pcm_poll,
        .unlocked_ioctl =    snd_pcm_ioctl,
        .compat_ioctl =     snd_pcm_ioctl_compat,
        .mmap =            snd_pcm_mmap,
        .fasync =        snd_pcm_fasync,
        .get_unmapped_area =    snd_pcm_get_unmapped_area,
    }
};

snd_pcm_playback_open

static int snd_pcm_playback_open(struct inode *inode, struct file *file)
{
	struct snd_pcm *pcm;
	int err = nonseekable_open(inode, file);
	if (err < 0)
		return err;
    //获取注册设备的user data
	pcm = snd_lookup_minor_data(iminor(inode), 
				    SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
	err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
	if (pcm)
		snd_card_unref(pcm->card);
	return err;
}

snd_pcm_open

static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
	int err;
	wait_queue_entry_t wait;

	if (pcm == NULL) {
		err = -ENODEV;
		goto __error1;
	}
	err = snd_card_file_add(pcm->card, file); //add the file to the file list of the card
	if (err < 0)
		goto __error1;
	if (!try_module_get(pcm->card->module)) {
		err = -EFAULT;
		goto __error2;
	}
	init_waitqueue_entry(&wait, current);
	add_wait_queue(&pcm->open_wait, &wait);
	mutex_lock(&pcm->open_mutex);
	while (1) {
		err = snd_pcm_open_file(file, pcm, stream);
		if (err >= 0)
			break;
		if (err == -EAGAIN) {
			if (file->f_flags & O_NONBLOCK) {
				err = -EBUSY;
				break;
			}
		} else
			break;
		set_current_state(TASK_INTERRUPTIBLE);
		mutex_unlock(&pcm->open_mutex);
		schedule();
		mutex_lock(&pcm->open_mutex);
		if (pcm->card->shutdown) {
			err = -ENODEV;
			break;
		}
		if (signal_pending(current)) {
			err = -ERESTARTSYS;
			break;
		}
	}
	remove_wait_queue(&pcm->open_wait, &wait);
	mutex_unlock(&pcm->open_mutex);
	if (err < 0)
		goto __error;
	return err;
	
	  __error:
		module_put(pcm->card->module);
	  __error2:
	  	snd_card_file_remove(pcm->card, file);
	  __error1:
	  	return err;
}

snd_pcm_open_file

static int snd_pcm_open_file(struct file *file,
			     struct snd_pcm *pcm,
			     int stream)
{
	struct snd_pcm_file *pcm_file;
	struct snd_pcm_substream *substream;
	int err;

	err = snd_pcm_open_substream(pcm, stream, file, &substream);
	if (err < 0)
		return err;
	
	pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
	if (pcm_file == NULL) {
		snd_pcm_release_substream(substream);
		return -ENOMEM;
	}
	pcm_file->substream = substream;
	if (substream->ref_count == 1) {
		substream->file = pcm_file;
		substream->pcm_release = pcm_release_private;
	}
	file->private_data = pcm_file;
	
	return 0;
}

snd_pcm_open_substream

int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
			   struct file *file,
			   struct snd_pcm_substream **rsubstream)
{
	struct snd_pcm_substream *substream;
	int err;

	err = snd_pcm_attach_substream(pcm, stream, file, &substream);
	if (err < 0)
		return err;
	if (substream->ref_count > 1) {
		*rsubstream = substream;
		return 0;
	}
	
	err = snd_pcm_hw_constraints_init(substream); //硬件参数约束初始化
	if (err < 0) {
		pcm_dbg(pcm, "snd_pcm_hw_constraints_init failed\n");
		goto error;
	}
	
	if ((err = substream->ops->open(substream)) < 0)
		goto error;
	
	substream->hw_opened = 1;
	
	err = snd_pcm_hw_constraints_complete(substream); //
	if (err < 0) {
		pcm_dbg(pcm, "snd_pcm_hw_constraints_complete failed\n");
		goto error;
	}
	
	*rsubstream = substream;
	return 0;

 error:
	snd_pcm_release_substream(substream);
	return err;
}
EXPORT_SYMBOL(snd_pcm_open_substream);

经验证,hw.info是在substream->ops->open中设置的

以rk3308为例,hw:0,0的.open指向soc_pcm_open,而hw:7,0的.open指向的是loopback_open。

if ((err = substream->ops->open(substream)) < 0)
		goto error;
soc_pcm_open [soc-pcm.c]
/*
 * Called by ALSA when a PCM substream is opened, the runtime->hw record is
 * then initialized and any private data can be allocated. This also calls
 * startup for the cpu DAI, component, machine and codec DAI.
 * 当PCM子流打开时,由ALSA调用,然后初始化runtime->hw的记录,并可以分配任何专用数据。
 * 这也要求启动cpu DAI,component,machine和codec DAI。
 */
   static int soc_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_soc_component *component;
   struct snd_soc_rtdcom_list *rtdcom;
   struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
   struct snd_soc_dai *codec_dai;
   const char *codec_dai_name = "multicodec";
   int i, ret = 0;

   pinctrl_pm_select_default_state(cpu_dai->dev);
   for (i = 0; i < rtd->num_codecs; i++)
   	pinctrl_pm_select_default_state(rtd->codec_dais[i]->dev);

   for_each_rtdcom(rtd, rtdcom) {
   	component = rtdcom->component;

   	pm_runtime_get_sync(component->dev);
   }

   mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);

   /* 启动音频子系统 */
   if (cpu_dai->driver->ops->startup) {
   	ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
   	if (ret < 0) {
   		dev_err(cpu_dai->dev, "ASoC: can't open interface"
   			" %s: %d\n", cpu_dai->name, ret);
   		goto out;
   	}
   }

   for_each_rtdcom(rtd, rtdcom) {
   	component = rtdcom->component;

   	if (!component->driver->ops ||
   	    !component->driver->ops->open)
   		continue;

   	ret = component->driver->ops->open(substream);  //在此处调用了dmaengine_pcm_open,4.19中删去了platform部分,pcm dmaengine从platform改为component。
   	if (ret < 0) {
   		dev_err(component->dev,
   			"ASoC: can't open component %s: %d\n",
   			component->name, ret);
   		goto component_err;
   	}

   }
   component = NULL;

   for (i = 0; i < rtd->num_codecs; i++) {
   	codec_dai = rtd->codec_dais[i];
   	if (codec_dai->driver->ops->startup) {
   		ret = codec_dai->driver->ops->startup(substream,
   						      codec_dai);
   		if (ret < 0) {
   			dev_err(codec_dai->dev,
   				"ASoC: can't open codec %s: %d\n",
   				codec_dai->name, ret);
   			goto codec_dai_err;
   		}
   	}

   	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
   		codec_dai->tx_mask = 0;
   	else
   		codec_dai->rx_mask = 0;

   }

   if (rtd->dai_link->ops->startup) {
   	ret = rtd->dai_link->ops->startup(substream);
   	if (ret < 0) {
   		pr_err("ASoC: %s startup failed: %d\n",
   		       rtd->dai_link->name, ret);
   		goto machine_err;
   	}
   }

   /* Dynamic PCM DAI links compat checks use dynamic capabilities */
   if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
   	goto dynamic;

   /* Check that the codec and cpu DAIs are compatible */
   soc_pcm_init_runtime_hw(substream);

   if (rtd->num_codecs == 1)
   	codec_dai_name = rtd->codec_dai->name;

   if (soc_pcm_has_symmetry(substream))
   	runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;

   ret = -EINVAL;
   if (!runtime->hw.rates) {
   	printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
   		codec_dai_name, cpu_dai->name);
   	goto config_err;
   }
   if (!runtime->hw.formats) {
   	printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
   		codec_dai_name, cpu_dai->name);
   	goto config_err;
   }
   if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
       runtime->hw.channels_min > runtime->hw.channels_max) {
   	printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
   			codec_dai_name, cpu_dai->name);
   	goto config_err;
   }

   soc_pcm_apply_msb(substream);

   /* Symmetry only applies if we've already got an active stream. */
   if (cpu_dai->active) {
   	ret = soc_pcm_apply_symmetry(substream, cpu_dai);
   	if (ret != 0)
   		goto config_err;
   }

   for (i = 0; i < rtd->num_codecs; i++) {
   	if (rtd->codec_dais[i]->active) {
   		ret = soc_pcm_apply_symmetry(substream,
   					     rtd->codec_dais[i]);
   		if (ret != 0)
   			goto config_err;
   	}
   }

   pr_debug("ASoC: %s <-> %s info:\n",
   		codec_dai_name, cpu_dai->name);
   pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
   pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
   	 runtime->hw.channels_max);
   pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min,
   	 runtime->hw.rate_max);

dynamic:

	snd_soc_runtime_activate(rtd, substream->stream);
	
	mutex_unlock(&rtd->pcm_mutex);
	return 0;

config_err:
	if (rtd->dai_link->ops->shutdown)
		rtd->dai_link->ops->shutdown(substream);

machine_err:
	i = rtd->num_codecs;

codec_dai_err:
	while (--i >= 0) {
		codec_dai = rtd->codec_dais[i];
		if (codec_dai->driver->ops->shutdown)
			codec_dai->driver->ops->shutdown(substream, codec_dai);
	}

component_err:
	soc_pcm_components_close(substream, component);

	if (cpu_dai->driver->ops->shutdown)
		cpu_dai->driver->ops->shutdown(substream, cpu_dai);

out:
	mutex_unlock(&rtd->pcm_mutex);

	for_each_rtdcom(rtd, rtdcom) {
		component = rtdcom->component;
	
		pm_runtime_mark_last_busy(component->dev);
		pm_runtime_put_autosuspend(component->dev);
	}
	
	for (i = 0; i < rtd->num_codecs; i++) {
		if (!rtd->codec_dais[i]->active)
			pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
	}
	if (!cpu_dai->active)
		pinctrl_pm_select_sleep_state(cpu_dai->dev);
	
	return ret;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值