Linux音频驱动-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);struct device *dev; /* actual hw device this belongs to */bool internal; /* pcm is for internal use only */bool nonatomic; /* whole PCM operations are in non-atomic context */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)struct snd_pcm_oss oss;
#endif
};
.card: 此pcm设备所属的card。
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;
};
.stream: 当前stream的方向,capture or playback。
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 *//* -- 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;/* misc flags */unsigned int hw_opened: 1;
};
.pcm: 所属的pcm。
pcm设备的创建
/*** 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)
{return _snd_pcm_new(card, id, device, playback_count, capture_count,false, rpcm);
}
此函数会传入六个参数,其中该函数的注释写的很清楚,不做过多解释。函数最终会返回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;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) {dev_err(card->dev, "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;
}
1. 分配一个snd_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];struct snd_pcm_substream *substream, *prev;#if IS_ENABLED(CONFIG_SND_PCM_OSS)mutex_init(&pstr->oss.setup_mutex);
#endifpstr->stream = stream;pstr->pcm = pcm;pstr->substream_count = substream_count;if (substream_count > 0 && !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++) {substream = kzalloc(sizeof(*substream), GFP_KERNEL);if (substream == NULL) {pcm_err(pcm, "Cannot allocate PCM substream\n");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;elseprev->next = substream;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;elseprev->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;
}
1. 根据传递进来的参数,设置pcm的stream, pcm, substream_count的值。
root@test:/proc/asound/card0/pcm0c$ cat info
card: 0
device: 0
subdevice: 0
stream: CAPTURE
id: ALC662 rev1 Analog
name: ALC662 rev1 Analog
subname: subdevice #0
class: 0
subclass: 0
subdevices_count: 1
subdevices_avail: 1
3. 会根据substrem_count的个数,进行for循环操作。
PCM硬件操作函数集设置
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;
}
该函数会根据当前stream的方向/类型,设置该硬件对应的snd_pcm_ops操作集合。
整个流程梳理
PCM设备节点创建
static struct snd_device_ops ops = {.dev_free = snd_pcm_dev_free,.dev_register = snd_pcm_dev_register,.dev_disconnect = snd_pcm_dev_disconnect,};
此时会调用到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;}dev = snd_get_device(devtype, pcm->card, pcm->device);if (dev) {err = sysfs_create_groups(&dev->kobj,pcm_dev_attr_groups);if (err < 0)dev_warn(dev,"pcm %d:%d: cannot create sysfs groups\n",pcm->card->number, pcm->device);put_device(dev);}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;
}
1. 合法性判断,对pcm设备来说,snd_device->device_data存放的是当前的pcm指针。
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 */
};
.type: 设备类型,比如是pcm, control, timer等设备。
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data,const char *name, struct device *device)
{int minor;struct snd_minor *preg;if (snd_BUG_ON(!name))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);
#ifdef CONFIG_SND_DYNAMIC_MINORSminor = snd_find_free_minor(type);
#elseminor = snd_kernel_minor(type, card, dev);if (minor >= 0 && snd_minors[minor])minor = -EBUSY;
#endifif (minor < 0) {mutex_unlock(&sound_mutex);kfree(preg);return minor;}snd_minors[minor] = preg;preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name);if (IS_ERR(preg->dev)) {snd_minors[minor] = NULL;mutex_unlock(&sound_mutex);minor = PTR_ERR(preg->dev);kfree(preg);return minor;}mutex_unlock(&sound_mutex);return 0;
}
1. 首先上来就分配一个snd_minor结构体。
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,.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,},{.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,}
};
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
5. 调用device_create函数创建该pcm的设备节点。
static char *sound_devnode(struct device *dev, umode_t *mode)
{if (MAJOR(dev->devt) == SOUND_MAJOR)return NULL;return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
}static int __init init_soundcore(void)
{int rc;rc = init_oss_soundcore();if (rc)return rc;sound_class = class_create(THIS_MODULE, "sound");if (IS_ERR(sound_class)) {cleanup_oss_soundcore();return PTR_ERR(sound_class);}sound_class->devnode = sound_devnode;return 0;
}
当调用device_create的时候,最终会调用到device_add->devtmpfs_create_node->device_get_devnode中
/* the class may provide a specific name */if (dev->class && dev->class->devnode)*tmp = dev->class->devnode(dev, mode);
最终出现的设备节点会出现在/dev/snd下。
应用到驱动的过程
static const struct file_operations snd_fops =
{.owner = THIS_MODULE,.open = snd_open,.llseek = noop_llseek,
};
if (file->f_op->open)err = file->f_op->open(inode, file);
3. 此处的open回调就是snd_pcm_f_ops中的open。
#define replace_fops(f, fops) \do { \struct file *__file = (f); \fops_put(__file->f_op); \BUG_ON(!(__file->f_op = (fops))); \} while(0)
5. 比如当前调用的是playback中的open,会调用snd_pcm_playback_open函数,此函数会设置pcm的runtime信息,最终会调用硬件相关的open函数中。
if ((err = substream->ops->open(substream)) < 0)
至此,整个pcm设备创建,调用,以及应用到驱动整个流程分析完毕。:)
Linux音频驱动-PCM设备相关推荐
- linux音频子系统 - pcm设备
1.pcm设备 脉冲编码调制(Pulse Code Modulation,PCM),就是把一个时间连续,取值连续的模拟信号变换成时间离散,取值离散的数字信号后在信道中传输,这是基本原理. 根据此原理, ...
- Linux 音频驱动(五) ALSA音频驱动之PCM逻辑设备
目录 1. 前言 2. PCM逻辑设备 2.1. 创建 PCM逻辑设备: 2.2. PCM逻辑设备文件操作函数集:snd_pcm_f_ops[] 2.3. Open PCM逻辑设备 2.4. Writ ...
- Linux 音频驱动
Linux 音频驱动 硬件介绍 WM8960与IMX6ULL之间有两个通信接口:I2C和I2S 其中I2C用于配置WM8960 I2S用于音频数据传输 修改设备树文件 编写I2C子节点设备树 code ...
- Linux 音频驱动(四) ASoC音频驱动之Machine驱动
目录 1. 基本介绍 2. 源码分析 2.1. Machine数据结构 struct snd_soc_dai_link 3. 声卡 3.1. 数据结构struct snd_soc_card 3.2. ...
- STM32MP157驱动开发——Linux 音频驱动
STM32MP157驱动开发--Linux 音频驱动 一.简介 1.CS42L51 简介 2.I2S总线 3.STM32MP1 SAI 总线接口 二.驱动开发 1.音频驱动 1)修改设备树 i2c 接 ...
- 转载:Linux音频驱动-OSS和ALSA声音系统简介及其比较
Linux音频驱动-OSS和ALSA声音系统简介及其比较 概述 昨天想在Ubuntu上用一下HTK工具包来绘制语音信号的频谱图和提取MFCC的结果,但由于前段时间把Ubuntu升级到13.04,系统的 ...
- Linux 音频驱动(二) ASoC音频驱动之Platform驱动
目录 1. 简介 2. 源码分析 2.1. CPU DAI 2.1.1. 数据结构struct snd_soc_dai_driver 2.1.2. 注册CPU DAI:snd_soc_register ...
- Linux音频驱动之二:Control接口的调用
本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记 一. control接口说明 Control接口主要让用户空间的应用程序(alsa-lib)可以访问和控制音频 ...
- linux 音频架构绕过,linux音频驱动架构
1.linux音频驱动架构分为3部分组成:硬件无关层(核心层ALSA).板级音频数字接口层驱动(McASP.McBSP等).外部codes驱动 sound/soc/davinci/ti81xx-etv ...
- linux音频设备节点,Linux音频驱动之三:PCM设备的创建
3. 新建一个pcm alsa-driver的中间层已经为我们提供了新建pcm的api: int snd_pcm_new(struct snd_card *card, const char *id, ...
最新文章
- docker pull的镜像放在哪里_Docker 安装ELK及Docker常见命令
- 『ACM-算法-ST算法』信息竞赛进阶指南--区间最值问题的ST算法
- 在VI中删除行尾的换行符
- Python2.x 和 Python3.x,如何选择?
- 买iPhone将可能只有盒子了?网友:库克账号多少,我直接打钱吧?
- Get value from agent failed: cannot connect to [[192.168.121.128]:10050]:[4]Interrupted systemctl ca
- 2018 CCPC 桂林站(upc复现赛)总结
- 入坑codewars第五天-Dubstep、Regex validate PIN code
- bolt界面引擎学习笔记一
- TeeChart Pro VCL,提供高性能图表
- 永中集成Office与上访科技部有感
- Android 设备接入扫码枪,Android 设备接入扫码枪
- AI笔记: 数学基础之反函数和6个基本初等函数
- 关于ORA-01034和ORA-27101的一种解决方法
- ubuntu 下tftp服务器配置笔记
- c语言学籍管理系统实训作业,学籍管理系统C语言实训报告
- 深入浅出了解撞库攻击!
- Qt界面美化-飞扬青云自绘控件插件的使用-避免采坑
- 基于白骨顶鸡优化算法的函数寻优算法
- 数据库 之 备份工具Mysqldump具体使用介绍