android audio/linux alsa音频-应用与驱动的接口

在LINUX系统中,每个设备文件都是文件。音频设备也是一样,它的设备文件被放在/dev/snd目录下,我们来看下这些设备文件:

ls /dev/snd -l
crw-rw----+ 1 root audio 116,  2 5月  19 21:24 controlC0
crw-rw----+ 1 root audio 116,  4 6月   6 19:31 pcmC0D0c
crw-rw----+ 1 root audio 116,  3 6月  11 11:53 pcmC0D0p
crw-rw----+ 1 root audio 116,  1 5月  19 21:24 seq
crw-rw----+ 1 root audio 116, 33 5月  19 21:24 timer
(1)controlC0: 音频控制设备文件,例如通道选择,混音,麦克风的控制等

(2)pcmC0D0c: 声卡0设备0的录音设备,c表示capter;

(3)pcmC0D0p: 声卡0设备0的播音设备,p表示play;

(4)seq: 音序器设备;

(5)timer:定时器设备。

这5个设备为音频设备最常见的设备,它还可以是其它设备类型,这些类型被定义在include/sound/core.h:

    #define SNDRV_DEV_TOPLEVEL  ((__force snd_device_type_t) 0)  
    #define SNDRV_DEV_CONTROL   ((__force snd_device_type_t) 1)  
    #define SNDRV_DEV_LOWLEVEL_PRE  ((__force snd_device_type_t) 2)  
    #define SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000)  
    #define SNDRV_DEV_PCM       ((__force snd_device_type_t) 0x1001)  
    #define SNDRV_DEV_RAWMIDI   ((__force snd_device_type_t) 0x1002)  
    #define SNDRV_DEV_TIMER     ((__force snd_device_type_t) 0x1003)  
    #define SNDRV_DEV_SEQUENCER ((__force snd_device_type_t) 0x1004)  
    #define SNDRV_DEV_HWDEP     ((__force snd_device_type_t) 0x1005)  
    #define SNDRV_DEV_INFO      ((__force snd_device_type_t) 0x1006)  
    #define SNDRV_DEV_BUS       ((__force snd_device_type_t) 0x1007)  
    #define SNDRV_DEV_CODEC     ((__force snd_device_type_t) 0x1008)  
    #define SNDRV_DEV_JACK          ((__force snd_device_type_t) 0x1009)  
    #define SNDRV_DEV_LOWLEVEL  ((__force snd_device_type_t) 0x2000)  

驱动代码分析:

在调用snd_soc_register_card()的过程中会创建/dev/snd/下的设备文件。

代码score-core.c:

函数snd_soc_instantiate_card():

	/* early DAI link probe */
	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
			order++) {
		for (i = 0; i < card->num_links; i++) {
			ret = soc_probe_dai_link(card, i, order);  //处理每个dai_link
			if (ret < 0) {
				pr_err("asoc: failed to instantiate card %s: %d\n",
			       card->name, ret);
				goto probe_dai_err;
			}
		}
	}
跟踪soc_probe_dai_link()的代码,见

static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
{
	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
	struct snd_soc_codec *codec = rtd->codec;
	struct snd_soc_platform *platform = rtd->platform;
	struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
	int ret;

	dev_dbg(card->dev, "probe %s dai link %d late %d\n",
			card->name, num, order);

	/* config components */
	codec_dai->codec = codec;
	cpu_dai->platform = platform;
	codec_dai->card = card;
	cpu_dai->card = card;

	/* set default power off timeout */
	rtd->pmdown_time = pmdown_time;

	/* probe the cpu_dai */
	if (!cpu_dai->probed &&
			cpu_dai->driver->probe_order == order) {
		if (!try_module_get(cpu_dai->dev->driver->owner))
			return -ENODEV;

		if (cpu_dai->driver->probe) {
			ret = cpu_dai->driver->probe(cpu_dai);
			if (ret < 0) {
				pr_err("asoc: failed to probe CPU DAI %s: %d\n",
							cpu_dai->name, ret);
				module_put(cpu_dai->dev->driver->owner);
				return ret;
			}
		}
		cpu_dai->probed = 1;
		/* mark cpu_dai as probed and add to card dai list */
		list_add(&cpu_dai->card_list, &card->dai_dev_list);
	}

	/* probe the CODEC */
	if (!codec->probed &&
			codec->driver->probe_order == order) {
		ret = soc_probe_codec(card, codec);
		if (ret < 0)
			return ret;
	}

	/* probe the platform */
	if (!platform->probed &&
			platform->driver->probe_order == order) {
		ret = soc_probe_platform(card, platform);
		if (ret < 0)
			return ret;
	}

	/* probe the CODEC DAI */
	if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
		if (codec_dai->driver->probe) {
			ret = codec_dai->driver->probe(codec_dai);
			if (ret < 0) {
				pr_err("asoc: failed to probe CODEC DAI %s: %d\n",
							codec_dai->name, ret);
				return ret;
			}
		}

		/* mark codec_dai as probed and add to card dai list */
		codec_dai->probed = 1;
		list_add(&codec_dai->card_list, &card->dai_dev_list);
	}

	/* complete DAI probe during last probe */
	if (order != SND_SOC_COMP_ORDER_LAST)
		return 0;

	ret = soc_post_component_init(card, codec, num, 0);
	if (ret)
		return ret;

	ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
	if (ret < 0)
		pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret);

	/* create the pcm */
	ret = soc_new_pcm(rtd, num);
	if (ret < 0) {
		pr_err("asoc: can't create pcm %s :%d\n",
				dai_link->stream_name, ret);
		return ret;
	}

	/* add platform data for AC97 devices */
	if (rtd->codec_dai->driver->ac97_control)
		snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata);

	return 0;
}
Soc-pcm.c:

/* create a new pcm */
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
	struct snd_soc_codec *codec = rtd->codec;
	struct snd_soc_platform *platform = rtd->platform;
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
	struct snd_pcm *pcm;
	char new_name[64];
	int ret = 0, playback = 0, capture = 0;

	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;

	/* check client and interface hw capabilities */
	snprintf(new_name, sizeof(new_name), "%s %s-%d",
			rtd->dai_link->stream_name, codec_dai->name, num);

	if (codec_dai->driver->playback.channels_min)
		playback = 1;
	if (codec_dai->driver->capture.channels_min)
		capture = 1;

	dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
	ret = snd_pcm_new(rtd->card->snd_card, new_name,
			num, playback, capture, &pcm);
	if (ret < 0) {
		printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
		return ret;
	}

	/* DAPM dai link stream work */
	INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);

	rtd->pcm = pcm;
	pcm->private_data = rtd;
	if (platform->driver->ops) {
		soc_pcm_ops->mmap = platform->driver->ops->mmap;
		soc_pcm_ops->pointer = platform->driver->ops->pointer;
		soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
		soc_pcm_ops->copy = platform->driver->ops->copy;
		soc_pcm_ops->silence = platform->driver->ops->silence;
		soc_pcm_ops->ack = platform->driver->ops->ack;
		soc_pcm_ops->page = platform->driver->ops->page;
	}

	if (playback)
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);

	if (capture)
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);

	if (platform->driver->pcm_new) {
		ret = platform->driver->pcm_new(rtd);
		if (ret < 0) {
			pr_err("asoc: platform pcm constructor failed\n");
			return ret;
		}
	}

	pcm->private_free = platform->driver->pcm_free;
	printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
		cpu_dai->name);
	return ret;
}
Sound/core/pcm.c:

int snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, struct snd_pcm **rpcm)
{
	return _snd_pcm_new(card, id, device, playback_count, capture_count,
			false, rpcm);
}
_snd_pcm_new:

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 = {
		.dev_free = snd_pcm_dev_free,
		.dev_register =	snd_pcm_dev_register,
		.dev_disconnect = snd_pcm_dev_disconnect,
	};

	if (snd_BUG_ON(!card))
		return -ENXIO;
	if (rpcm)
		*rpcm = NULL;
	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
	if (pcm == NULL) {
		snd_printk(KERN_ERR "Cannot allocate PCM\n");
		return -ENOMEM;
	}
	pcm->card = card;
	pcm->device = device;
	pcm->internal = internal;
	if (id)
		strlcpy(pcm->id, id, sizeof(pcm->id));
	if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_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;
	}
	mutex_init(&pcm->open_mutex);
	init_waitqueue_head(&pcm->open_wait);
	if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
		snd_pcm_free(pcm);
		return err;
	}
	if (rpcm)
		*rpcm = pcm;
	return 0;
}
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_notify *notify;
	char str[16];
	struct snd_pcm *pcm;
	struct device *dev;

	if (snd_BUG_ON(!device || !device->device_data))
		return -ENXIO;
	pcm = device->device_data;
	mutex_lock(&register_mutex);
	err = snd_pcm_add(pcm);
	if (err) {
		mutex_unlock(&register_mutex);
		return err;
	}
	for (cidx = 0; cidx < 2; cidx++) {
		int devtype = -1;
		if (pcm->streams[cidx].substream == NULL || pcm->internal)
			continue;
		switch (cidx) {
		case SNDRV_PCM_STREAM_PLAYBACK:
			sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
			devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
			break;
		case SNDRV_PCM_STREAM_CAPTURE:
			sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
			devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
			break;
		}
		/* device pointer to use, pcm->dev takes precedence if
		 * it is assigned, otherwise fall back to card's device
		 * if possible */
		dev = pcm->dev;
		if (!dev)
			dev = snd_card_get_device_link(pcm->card);
		/* register pcm */
		err = snd_register_device_for_dev(devtype, pcm->card,
						  pcm->device,
						  &snd_pcm_f_ops[cidx],
						  pcm, str, dev);
		if (err < 0) {
			list_del(&pcm->list);
			mutex_unlock(&register_mutex);
			return err;
		}
		snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
					  &pcm_attrs);
		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
			snd_pcm_timer_init(substream);
	}

	list_for_each_entry(notify, &snd_pcm_notify_list, list)
		notify->n_register(pcm);

	mutex_unlock(&register_mutex);
	return 0;
}
上面粗体代码部分即是在/dev/snd下的play和capter设备的名称。

/dev/snd/pcmCxDnp/c的操作函数即对应pcm_native.c中的snd_pcm_f_ops[2], [0]是play操作,[1]是capter操作:

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,              //对应应用层的open
		.release =		snd_pcm_release,
		.llseek =		no_llseek,
		.poll =			snd_pcm_playback_poll,
		.unlocked_ioctl =	snd_pcm_playback_ioctl,               //对应应用层的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,
	}
};

猜你喜欢

转载自blog.csdn.net/xgbing/article/details/73022067