在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(®ister_mutex); err = snd_pcm_add(pcm); if (err) { mutex_unlock(®ister_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(®ister_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(®ister_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, } };