第十四部分 snd_pcm
1.pcm结构体
struct snd_pcm {
struct snd_card *card; //声卡
struct list_head list;
int device; //设备号
unsigned int info_flags;
unsigned short dev_class;
unsigned short dev_subclass;
char id[64]; //id字串
char name[80]; //名字
struct snd_pcm_str streams[2]; //pcm流数组 0-回放 1-捕捉
struct mutex open_mutex;
wait_queue_head_t open_wait; //PCM打开等待队列
void *private_data;
void (*private_free) (struct snd_pcm *pcm); //(soc_new_pcm函数)platform->driver->pcm_free
struct device *dev; //设备文件
};
struct snd_pcm_str {
int stream; //流类型
struct snd_pcm *pcm; //捆绑的pcm结构体
/* -- substreams -- */
unsigned int substream_count; //子流个数
unsigned int substream_opened; //子流打开标志
struct snd_pcm_substream *substream; //pcm子流
#ifdef CONFIG_SND_VERBOSE_PROCFS
struct snd_info_entry *proc_root;
struct snd_info_entry *proc_info_entry;
#endif
};
struct snd_pcm_substream {
struct snd_pcm *pcm; //捆绑pcm结构体
struct snd_pcm_str *pstr; //捆绑的pcm流
void *private_data; /* copied from pcm->private_data */
int number;
char name[32]; //子流名
int stream; //流类型
struct pm_qos_request_list latency_pm_qos_req; /* pm_qos request */
size_t buffer_bytes_max; /* limit ring buffer size */
struct snd_dma_buffer dma_buffer;
unsigned int dma_buf_id;
size_t dma_max;
/* -- hardware operations -- */
struct snd_pcm_ops *ops; //pcm操作函数集
/* -- runtime information -- */
struct snd_pcm_runtime *runtime; //pcm runtime
/* -- timer section -- */
struct snd_timer *timer; //声卡定时器
unsigned timer_running: 1; /* time is running */
/* -- next substream -- */
struct snd_pcm_substream *next; //下一个pcm子流
/* -- 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; //指向 snd_pcm_file结构体
int ref_count;
atomic_t mmap_count;
unsigned int f_flags;
void (*pcm_release)(struct snd_pcm_substream *);
struct pid *pid;
#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;
#endif
/* misc flags */
unsigned int hw_opened: 1;
};
int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count,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); //分配snd_pcm内存
if (pcm == NULL) {
snd_printk(KERN_ERR "Cannot allocate PCM\n");
return -ENOMEM;
}
pcm->card = card; //捆绑声卡
pcm->device = device; //设备号
if (id) //id识别字串
strlcpy(pcm->id, id, sizeof(pcm->id)); //填充pcm结构体id字串
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { //回放 SNDRV_PCM_STREAM_PLAYBACK=0
snd_pcm_free(pcm);
return err;
}
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) { //捕捉 SNDRV_PCM_STREAM_CAPTURE=1
snd_pcm_free(pcm);
return err;
}
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait); //初始化PCM打开等待队列
if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { //创建声卡设备,声卡设备的device_data指向snd_pcm结构体对象
snd_pcm_free(pcm);
return err;
}
if (rpcm)
*rpcm = pcm;
return 0;
}
EXPORT_SYMBOL(snd_pcm_new);
5.创建pcm子流
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
int idx, err;
struct snd_pcm_str *pstr = &pcm->streams[stream]; //获取pcm流
struct snd_pcm_substream *substream, *prev;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
mutex_init(&pstr->oss.setup_mutex);
#endif
pstr->stream = stream; //获取流类型 0:回放 1:捕捉
pstr->pcm = pcm; //捆绑pcm设备
pstr->substream_count = substream_count; //子流个数
if (substream_count > 0) {
err = snd_pcm_stream_proc_init(pstr); //proc接口
if (err < 0) {
snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
return err;
}
}
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL); //分配子流内存
if (substream == NULL) {
snd_printk(KERN_ERR "Cannot allocate PCM substream\n");
return -ENOMEM;
}
substream->pcm = pcm; //捆绑pcm设备
substream->pstr = pstr; //捆绑pcm子流
substream->number = idx; //索引号
substream->stream = stream; //子流类型 0:回放 1:捕捉
sprintf(substream->name, "subdevice #%i", idx); //子流名
substream->buffer_bytes_max = UINT_MAX;
if (prev == NULL) //处理第一个pcm子流时走的分支
pstr->substream = substream; //pcm流捆绑子流
else //前一个子流的next指针指向当前子流,也就是串成一串(0->next--1->next-->2...)
prev->next = substream;
err = snd_pcm_substream_proc_init(substream); //proc接口
if (err < 0) {
snd_printk(KERN_ERR "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);
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; //prev指向当前substream
}
return 0;
}
EXPORT_SYMBOL(snd_pcm_new_stream);
6 注册方法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; //声卡设备的私有数据中获取snd_pcm对象
mutex_lock(®ister_mutex);
err = snd_pcm_add(pcm); //添加snd_pcm对象到全局snd_pcm_devices链表
if (err) {
mutex_unlock(®ister_mutex);
return err;
}
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL) //判断pcm子流是否为空
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK: //回放
sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);//pcmC+声卡号+D+设备号+p
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; //设置设备类型
break;
case SNDRV_PCM_STREAM_CAPTURE: //捕捉
sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);//pcmC+声卡号+D+设备号+c
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; //设置设备类型
break;
}
dev = pcm->dev;
if (!dev)
dev = snd_card_get_device_link(pcm->card);
err = snd_register_device_for_dev(devtype, pcm->card,pcm->device,&snd_pcm_f_ops[cidx],pcm, str, dev);
//注册声卡设备 "/dev/snd/pcmC0D0p" "/dev/snd/pcmC0D0c"...这里特别注意私有数据是snd_pcm对象
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) //遍历PCM子流链表
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;
}
6.1 添加snd_pcm对象到全局snd_pcm_devices链表
static int snd_pcm_add(struct snd_pcm *newpcm)
{
struct snd_pcm *pcm;
list_for_each_entry(pcm, &snd_pcm_devices, list) { //遍历全局snd_pcm_devices链表
if (pcm->card == newpcm->card && pcm->device == newpcm->device) //匹配声卡和设备号
return -EBUSY;
if (pcm->card->number > newpcm->card->number ||(pcm->card == newpcm->card && pcm->device > newpcm->device)) {
list_add(&newpcm->list, pcm->list.prev); //链接新的pcm到旧的pcm(根据设备号来排序)
return 0;
}
}
list_add_tail(&newpcm->list, &snd_pcm_devices); //添加pcm到全局snd_pcm_devices
return 0;
}
7.pcm相关的字符设备
snd_register_device_for_dev函数前面讲过注册字符设备文件,捆绑对应的文件操作函数集,也就是snd_pcm_f_ops数组项
PCM声卡设备操作函数集
const struct file_operations snd_pcm_f_ops[2] = {
{ //回放 对应"/dev/snd/pcmC0D0p"设备文件的操作接口
.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, //获取没映射的地址
},
{ //捕捉 对应"/dev/snd/pcmC0D0c"设备文件的操作接口
.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, //获取没映射的地址
}
};
当我们在操作/dev/snd/pcmCxDxX时候会调用到上面的函数集的方法,
X为'c'时代表capture调用snd_pcm_f_ops[1]的方法,X为'p'时代表playback调用snd_pcm_f_ops[0]的方法
我在内核代码上添加了打印语句:
当我用arecord时候调用open方法接着会调用ioctl方法设置参数属性,接着会调用mmap方法映射内存,接着不断继续用ioctl方法录制声音
主要是SNDRV_PCM_IOCTL_WRITEI_FRAMES命令录制,和SNDRV_PCM_IOCTL_SYNC_PTR命令同步.
当我用arecord时候调用open方法接着会调用ioctl方法设置参数属性,接着会调用mmap方法映射内存,接着不断继续用ioctl方法播放声音
主要是SNDRV_PCM_IOCTL_READI_FRAMES命令回放,和SNDRV_PCM_IOCTL_SYNC_PTR命令同步.
在pcm字符设备的操作函数集中会经常用到下面的结构体
8.snd_pcm_file
struct snd_pcm_file {
struct snd_pcm_substream *substream;
int no_compat_mmap;
};
该结构体很简单基本就是封装了pcm子流和一个映射方式标志
9.pcm runtime
struct snd_pcm_runtime {
/* -- Status -- */
struct snd_pcm_substream *trigger_master; //pcm子流
struct timespec trigger_tstamp; /* trigger timestamp */
int overrange;
snd_pcm_uframes_t avail_max;
snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */
snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */
unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */
snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */
/* -- HW params -- */
snd_pcm_access_t access; /* access mode */
snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */
snd_pcm_subformat_t subformat; /* subformat */
unsigned int rate; /* rate in Hz */
unsigned int channels; /* channels */
snd_pcm_uframes_t period_size; /* period size */
unsigned int periods; /* periods */
snd_pcm_uframes_t buffer_size; /* buffer size */
snd_pcm_uframes_t min_align; /* Min alignment for the format */
size_t byte_align;
unsigned int frame_bits;
unsigned int sample_bits;
unsigned int info;
unsigned int rate_num;
unsigned int rate_den;
/* -- SW params -- */
int tstamp_mode; /* mmap timestamp is updated */
unsigned int period_step;
snd_pcm_uframes_t start_threshold;
snd_pcm_uframes_t stop_threshold;
snd_pcm_uframes_t silence_threshold; /* Silence filling happens when noise is nearest than this */
snd_pcm_uframes_t silence_size; /* Silence filling size */
snd_pcm_uframes_t boundary; /* pointers wrap point */
snd_pcm_uframes_t silence_start; /* starting pointer to silence area */
snd_pcm_uframes_t silence_filled; /* size filled with silence */
union snd_pcm_sync_id sync; /* hardware synchronization ID */
/* -- mmap -- */
struct snd_pcm_mmap_status *status;
struct snd_pcm_mmap_control *control;
/* -- locking / scheduling -- */
snd_pcm_uframes_t twake; /* do transfer (!poll) wakeup if non-zero */
wait_queue_head_t sleep; /* poll sleep */
wait_queue_head_t tsleep; /* transfer sleep */
struct fasync_struct *fasync;
/* -- private section -- */
void *private_data;
void (*private_free)(struct snd_pcm_runtime *runtime);
/* -- hardware description -- */
struct snd_pcm_hardware hw; //pcm硬件信息
struct snd_pcm_hw_constraints hw_constraints; //pcm硬件约束
/* -- interrupt callbacks -- */
void (*transfer_ack_begin)(struct snd_pcm_substream *substream);
void (*transfer_ack_end)(struct snd_pcm_substream *substream);
/* -- timer -- */
unsigned int timer_resolution; /* timer resolution */
int tstamp_type; /* timestamp type */
/* -- DMA -- */
unsigned char *dma_area; /* DMA area */
dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */
size_t dma_bytes; /* size of DMA area */
struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */
};
10.pcm字符设备打开方法
回放
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;
pcm = snd_lookup_minor_data(iminor(inode),SNDRV_DEVICE_TYPE_PCM_PLAYBACK); //获取snd_pcm对象
return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); //打开回放通道
}
捕捉
static int snd_pcm_capture_open(struct inode *inode, struct file *file)
{
struct snd_pcm *pcm;
int err = nonseekable_open(inode, file); //设置打开方式
if (err < 0)
return err;
pcm = snd_lookup_minor_data(iminor(inode),SNDRV_DEVICE_TYPE_PCM_CAPTURE); //获取snd_pcm对象
return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); //打开捕捉通道
}
10.1.次设备号获取snd_pcm对象
void *snd_lookup_minor_data(unsigned int minor, int type)
{
struct snd_minor *mreg;
void *private_data;
if (minor >= ARRAY_SIZE(snd_minors))
return NULL;
mutex_lock(&sound_mutex);
mreg = snd_minors[minor]; //根据次设备号,从全局snd_minor数组获取对应的snd_minor对象
if (mreg && mreg->type == type)
private_data = mreg->private_data; //前面特别注意私有数据为snd_pcm对象
else
private_data = NULL;
mutex_unlock(&sound_mutex);
return private_data; //返回snd_pcm对象
}
EXPORT_SYMBOL(snd_lookup_minor_data);
10.2.snd_pcm_open
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
int err;
struct snd_pcm_file *pcm_file;
wait_queue_t wait;
if (pcm == NULL) {
err = -ENODEV;
goto __error1;
}
err = snd_card_file_add(pcm->card, file);
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, &pcm_file); //4.1 snd_pcm_open_file
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 (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;
}
10.2.1 snd_pcm_open_file
static int snd_pcm_open_file(struct file *file,struct snd_pcm *pcm,int stream,struct snd_pcm_file **rpcm_file)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
struct snd_pcm_str *str;
int err;
if (rpcm_file)
*rpcm_file = NULL;
err = snd_pcm_open_substream(pcm, stream, file, &substream); //4.1.1 snd_pcm_open_substream
if (err < 0)
return err;
pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); //分配snd_pcm_file对象内存
if (pcm_file == NULL) {
snd_pcm_release_substream(substream);
return -ENOMEM;
}
pcm_file->substream = substream; //snd_pcm_file对象捆绑pcm子流
if (substream->ref_count == 1) {
str = substream->pstr; //获取pcm流
substream->file = pcm_file; //pcm子流捆绑snd_pcm_file对象
substream->pcm_release = pcm_release_private;
}
file->private_data = pcm_file; //snd_pcm_file对象存放在文件对象的私有数据里
if (rpcm_file)
*rpcm_file = pcm_file;
return 0;
}
10.2.1.1 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); //获取pcm子流
if (err < 0)
return err;
if (substream->ref_count > 1) {
*rsubstream = substream;
return 0;
}
err = snd_pcm_hw_constraints_init(substream); //初始化pcm硬件约束
if (err < 0) {
snd_printd("snd_pcm_hw_constraints_init failed\n");
goto error;
}
if ((err = substream->ops->open(substream)) < 0) //调用pcm子流的open方法
goto error;
substream->hw_opened = 1;
err = snd_pcm_hw_constraints_complete(substream); //设置pcm硬件约束
if (err < 0) {
snd_printd("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);
这里调用了pcm子流的open方法,在上一篇文章ASoc中有这么几句代码
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
11.pcm子流操作集
static struct snd_pcm_ops soc_pcm_ops = {
.open = soc_pcm_open,
.close = soc_codec_close,
.hw_params = soc_pcm_hw_params,
.hw_free = soc_pcm_hw_free,
.prepare = soc_pcm_prepare,
.trigger = soc_pcm_trigger,
.pointer = soc_pcm_pointer,
};
除了open方法这里给调用,其他的方法在ioctl和close等字符设备方法中会使用到,souceinsight搜索ops->xxx,基本上在pcm_native.c文件中会调用
11.1 open方法soc_pcm_open
static int soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data; //获取soc pcm
struct snd_pcm_runtime *runtime = substream->runtime; //获取pcmrt
struct snd_soc_platform *platform = rtd->platform; //获取soc平台
struct snd_soc_dai *cpu_dai = rtd->cpu_dai; //获取dai设备(cpu_dai)
struct snd_soc_dai *codec_dai = rtd->codec_dai; //获取dai设备(codec_dai)
struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; //获取dai(cpu_dai)声卡驱动
struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; //获取dai(codec_dai)声卡驱动
int ret = 0;
mutex_lock(&pcm_mutex);
/* startup the audio subsystem */
if (cpu_dai->driver->ops->startup) {
ret = cpu_dai->driver->ops->startup(substream, cpu_dai); //调用dai设备(cpu_dai)声卡驱动的startup方法
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",cpu_dai->name);
goto out;
}
}
if (platform->driver->ops->open) {
ret = platform->driver->ops->open(substream); //调用soc平台驱动的open方法
if (ret < 0) {
printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
goto platform_err;
}
}
if (codec_dai->driver->ops->startup) {
ret = codec_dai->driver->ops->startup(substream, codec_dai); //调用dai设备(codec_dai)声卡驱动的startup方法
if (ret < 0) {
printk(KERN_ERR "asoc: can't open codec %s\n",codec_dai->name);
goto codec_dai_err;
}
}
if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
ret = rtd->dai_link->ops->startup(substream); //调用dai link的startup方法
if (ret < 0) {
printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
goto machine_err;
}
}
/* Check that the codec and cpu DAI's are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { //回放
runtime->hw.rate_min =max(codec_dai_drv->playback.rate_min,cpu_dai_drv->playback.rate_min); //最小采样率
runtime->hw.rate_max =min(codec_dai_drv->playback.rate_max,cpu_dai_drv->playback.rate_max); //最大采样率
runtime->hw.channels_min =max(codec_dai_drv->playback.channels_min,cpu_dai_drv->playback.channels_min); //最小通道数
runtime->hw.channels_max =min(codec_dai_drv->playback.channels_max,cpu_dai_drv->playback.channels_max); //最大通道数
runtime->hw.formats =codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats; //采样格式
runtime->hw.rates =codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates; //设置采样率
if (codec_dai_drv->playback.rates& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= cpu_dai_drv->playback.rates; //设置采样率
if (cpu_dai_drv->playback.rates& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= codec_dai_drv->playback.rates; //设置采样率
} else { //捕捉
runtime->hw.rate_min =max(codec_dai_drv->capture.rate_min,cpu_dai_drv->capture.rate_min); //最小采样率
runtime->hw.rate_max =min(codec_dai_drv->capture.rate_max,cpu_dai_drv->capture.rate_max); //最大采样率
runtime->hw.channels_min =max(codec_dai_drv->capture.channels_min,cpu_dai_drv->capture.channels_min); //最小通道数
runtime->hw.channels_max =min(codec_dai_drv->capture.channels_max,cpu_dai_drv->capture.channels_max); //最大通道数
runtime->hw.formats =codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; //采样格式
runtime->hw.rates =codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; //设置采样率
if (codec_dai_drv->capture.rates& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= cpu_dai_drv->capture.rates; //设置采样率
if (cpu_dai_drv->capture.rates& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= codec_dai_drv->capture.rates; //设置采样率
}
snd_pcm_limit_hw_rates(runtime); //设置采样率范围
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) {
printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",codec_dai->name, cpu_dai->name);
goto config_err;
}
/* Symmetry only applies if we've already got an active stream. */
if (cpu_dai->active || codec_dai->active) {
ret = soc_pcm_apply_symmetry(substream);
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);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active++; //更新dai(cpu_dai)的playback_active计数
codec_dai->playback_active++; //更新dai(codec_dai)的playback_active计数
} else {
cpu_dai->capture_active++; //更新dai(cpu_dai)的capture_active计数
codec_dai->capture_active++; //更新dai(codec_dai)的capture_active计数
}
cpu_dai->active++; //更新dai(cpu_dai)的active计数
codec_dai->active++; //更新dai(codec_dai)的active计数
rtd->codec->active++; //更新codec设备的active计数
mutex_unlock(&pcm_mutex);
return 0;
config_err:
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
machine_err:
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
codec_dai_err:
if (platform->driver->ops->close)
platform->driver->ops->close(substream);
platform_err:
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
mutex_unlock(&pcm_mutex);
return ret;
}
12.pcm字符设备映射方法
。。。
13.pcm字符设备命令控制方法
13.1 捕捉控制命令
static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
struct snd_pcm_file *pcm_file;
pcm_file = file->private_data; //获取snd_pcm_file对象
if (((cmd >> 8) & 0xff) != 'A')
return -ENOTTY;
return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,(void __user *)arg);
}
13.1.1 snd_pcm_capture_ioctl1
static int snd_pcm_capture_ioctl1(struct file *file,struct snd_pcm_substream *substream,unsigned int cmd, void __user *arg)
{
if (snd_BUG_ON(!substream))
return -ENXIO;
if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE))
return -EINVAL;
switch (cmd) {
case SNDRV_PCM_IOCTL_READI_FRAMES:
{
struct snd_xferi xferi;
struct snd_xferi __user *_xferi = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (put_user(0, &_xferi->result))
return -EFAULT;
if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
return -EFAULT;
result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
__put_user(result, &_xferi->result);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_READN_FRAMES:
{
struct snd_xfern xfern;
struct snd_xfern __user *_xfern = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
void *bufs;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (runtime->channels > 128)
return -EINVAL;
if (put_user(0, &_xfern->result))
return -EFAULT;
if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
return -EFAULT;
bufs = memdup_user(xfern.bufs,sizeof(void *) * runtime->channels);
if (IS_ERR(bufs))
return PTR_ERR(bufs);
result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
kfree(bufs);
__put_user(result, &_xfern->result);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_REWIND:
{
snd_pcm_uframes_t frames;
snd_pcm_uframes_t __user *_frames = arg;
snd_pcm_sframes_t result;
if (get_user(frames, _frames))
return -EFAULT;
if (put_user(0, _frames))
return -EFAULT;
result = snd_pcm_capture_rewind(substream, frames);
__put_user(result, _frames);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_FORWARD:
{
snd_pcm_uframes_t frames;
snd_pcm_uframes_t __user *_frames = arg;
snd_pcm_sframes_t result;
if (get_user(frames, _frames))
return -EFAULT;
if (put_user(0, _frames))
return -EFAULT;
result = snd_pcm_capture_forward(substream, frames);
__put_user(result, _frames);
return result < 0 ? result : 0;
}
}
return snd_pcm_common_ioctl1(file, substream, cmd, arg); // 通用命令
}
13.2 回放控制命令
static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
struct snd_pcm_file *pcm_file;
pcm_file = file->private_data; //获取snd_pcm_file对?
if (((cmd >> 8) & 0xff) != 'A')
return -ENOTTY;
return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,(void __user *)arg);
}
13.2.1 snd_pcm_playback_ioctl1
static int snd_pcm_playback_ioctl1(struct file *file,struct snd_pcm_substream *substream,unsigned int cmd, void __user *arg)
{
if (snd_BUG_ON(!substream))
return -ENXIO;
if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
return -EINVAL;
switch (cmd) {
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
{
struct snd_xferi xferi;
struct snd_xferi __user *_xferi = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (put_user(0, &_xferi->result))
return -EFAULT;
if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
return -EFAULT;
result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
__put_user(result, &_xferi->result);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
{
struct snd_xfern xfern;
struct snd_xfern __user *_xfern = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
void __user **bufs;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (runtime->channels > 128)
return -EINVAL;
if (put_user(0, &_xfern->result))
return -EFAULT;
if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
return -EFAULT;
bufs = memdup_user(xfern.bufs,sizeof(void *) * runtime->channels);
if (IS_ERR(bufs))
return PTR_ERR(bufs);
result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
kfree(bufs);
__put_user(result, &_xfern->result);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_REWIND:
{
snd_pcm_uframes_t frames;
snd_pcm_uframes_t __user *_frames = arg;
snd_pcm_sframes_t result;
if (get_user(frames, _frames))
return -EFAULT;
if (put_user(0, _frames))
return -EFAULT;
result = snd_pcm_playback_rewind(substream, frames);
__put_user(result, _frames);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_FORWARD:
{
snd_pcm_uframes_t frames;
snd_pcm_uframes_t __user *_frames = arg;
snd_pcm_sframes_t result;
if (get_user(frames, _frames))
return -EFAULT;
if (put_user(0, _frames))
return -EFAULT;
result = snd_pcm_playback_forward(substream, frames);
__put_user(result, _frames);
return result < 0 ? result : 0;
}
}
return snd_pcm_common_ioctl1(file, substream, cmd, arg); //通用命令
}
13.3 通用控制命令
static int snd_pcm_common_ioctl1(struct file *file,struct snd_pcm_substream *substream,unsigned int cmd, void __user *arg)
{
switch (cmd) {
case SNDRV_PCM_IOCTL_PVERSION:
return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
case SNDRV_PCM_IOCTL_INFO:
return snd_pcm_info_user(substream, arg);
case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */
return 0;
case SNDRV_PCM_IOCTL_TTSTAMP:
return snd_pcm_tstamp(substream, arg);
case SNDRV_PCM_IOCTL_HW_REFINE:
return snd_pcm_hw_refine_user(substream, arg);
case SNDRV_PCM_IOCTL_HW_PARAMS:
return snd_pcm_hw_params_user(substream, arg);
case SNDRV_PCM_IOCTL_HW_FREE:
return snd_pcm_hw_free(substream);
case SNDRV_PCM_IOCTL_SW_PARAMS:
return snd_pcm_sw_params_user(substream, arg);
case SNDRV_PCM_IOCTL_STATUS:
return snd_pcm_status_user(substream, arg);
case SNDRV_PCM_IOCTL_CHANNEL_INFO:
return snd_pcm_channel_info_user(substream, arg);
case SNDRV_PCM_IOCTL_PREPARE:
return snd_pcm_prepare(substream, file);
case SNDRV_PCM_IOCTL_RESET:
return snd_pcm_reset(substream);
case SNDRV_PCM_IOCTL_START:
return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
case SNDRV_PCM_IOCTL_LINK:
return snd_pcm_link(substream, (int)(unsigned long) arg);
case SNDRV_PCM_IOCTL_UNLINK:
return snd_pcm_unlink(substream);
case SNDRV_PCM_IOCTL_RESUME:
return snd_pcm_resume(substream);
case SNDRV_PCM_IOCTL_XRUN:
return snd_pcm_xrun(substream);
case SNDRV_PCM_IOCTL_HWSYNC:
return snd_pcm_hwsync(substream);
case SNDRV_PCM_IOCTL_DELAY:
return snd_pcm_delay(substream, arg);
case SNDRV_PCM_IOCTL_SYNC_PTR:
return snd_pcm_sync_ptr(substream, arg);
#ifdef CONFIG_SND_SUPPORT_OLD_API
case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
return snd_pcm_hw_refine_old_user(substream, arg);
case SNDRV_PCM_IOCTL_HW_PARAMS_OLD:
return snd_pcm_hw_params_old_user(substream, arg);
#endif
case SNDRV_PCM_IOCTL_DRAIN:
return snd_pcm_drain(substream, file);
case SNDRV_PCM_IOCTL_DROP:
return snd_pcm_drop(substream);
case SNDRV_PCM_IOCTL_PAUSE:
{
int res;
snd_pcm_stream_lock_irq(substream);
res = snd_pcm_pause(substream, (int)(unsigned long)arg);
snd_pcm_stream_unlock_irq(substream);
return res;
}
}
snd_printd("unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
}
13.4 命令总结
//capture专用命令
SNDRV_PCM_IOCTL_WRITEI_FRAMES
SNDRV_PCM_IOCTL_WRITEN_FRAMES
SNDRV_PCM_IOCTL_REWIND
SNDRV_PCM_IOCTL_FORWARD
//playback专用命令
SNDRV_PCM_IOCTL_READI_FRAMES
SNDRV_PCM_IOCTL_READN_FRAMES
SNDRV_PCM_IOCTL_REWIND
SNDRV_PCM_IOCTL_FORWARD
//通用命令
SNDRV_PCM_IOCTL_PVERSION
SNDRV_PCM_IOCTL_INFO
SNDRV_PCM_IOCTL_TSTAMP
SNDRV_PCM_IOCTL_TTSTAMP
SNDRV_PCM_IOCTL_HW_REFINE
SNDRV_PCM_IOCTL_HW_PARAMS
SNDRV_PCM_IOCTL_HW_FREE
SNDRV_PCM_IOCTL_SW_PARAMS
SNDRV_PCM_IOCTL_STATUS
SNDRV_PCM_IOCTL_CHANNEL_INFO
SNDRV_PCM_IOCTL_PREPARE
SNDRV_PCM_IOCTL_RESET
SNDRV_PCM_IOCTL_START
SNDRV_PCM_IOCTL_LINK
SNDRV_PCM_IOCTL_UNLINK
SNDRV_PCM_IOCTL_RESUME
SNDRV_PCM_IOCTL_XRUN
SNDRV_PCM_IOCTL_HWSYNC
SNDRV_PCM_IOCTL_DELAY
SNDRV_PCM_IOCTL_SYNC_PTR
SNDRV_PCM_IOCTL_HW_REFINE_OLD
SNDRV_PCM_IOCTL_HW_PARAMS_OLD
SNDRV_PCM_IOCTL_DRAIN
SNDRV_PCM_IOCTL_DROP
SNDRV_PCM_IOCTL_PAUSE