当前位置:首页 > 音频设备驱动
3 int (*open)(struct snd_pcm_substream *substream);//打开 4 int (*close)(struct snd_pcm_substream *substream);//关闭 5 int (*ioctl)(struct snd_pcm_substream * substream, 6 unsigned int cmd, void *arg);//io 控制
7 int (*hw_params)(struct snd_pcm_substream *substream, 8 struct snd_pcm_hw_params *params);//硬件参数
9 int (*hw_free)(struct snd_pcm_substream *substream); //资源释放 10 int (*prepare)(struct snd_pcm_substream *substream);//准备 11 //在PCM 被开始、停止或暂停时调用
12 int (*trigger)(struct snd_pcm_substream *substream, int cmd);
13 snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);// 当前缓冲区的硬件位置 14 //缓冲区拷贝
15 int (*copy)(struct snd_pcm_substream *substream, int channel, 16 snd_pcm_uframes_t pos,
17 void __user *buf, snd_pcm_uframes_t count);
18 int (*silence)(struct snd_pcm_substream *substream, int channel, 19 snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
20 struct page *(*page)(struct snd_pcm_substream *substream, 21 unsigned long offset);
22 int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma); 23 int (*ack)(struct snd_pcm_substream *substream); 24 };
snd_pcm_ops 中的所有操作都需事先通过snd_pcm_substream_chip()获得xxxchip 指针,例如: int xxx() {
struct xxxchip *chip = snd_pcm_substream_chip(substream); ... }
当1 个PCM 子流被打开时,snd_pcm_ops 中的open()函数将被调用,在这个函数中,至少需要初始化
runtime->hw 字段,代码清单17.9 给出了open()函数的范例。 代码清单17.9 snd_pcm_ops 结构体中open()函数
1 static int snd_xxx_open(struct snd_pcm_substream *substream) 2 {
3 //从子流获得xxxchip 指针
4 struct xxxchip *chip = snd_pcm_substream_chip(substream); 5 //获得PCM 运行时信息指针
6 struct snd_pcm_runtime *runtime = substream->runtime; 7 ...
8 //初始化runtime->hw
9 runtime->hw = snd_xxxchip_playback_hw; 10 return 0;
11 }
上述代码中的snd_xxxchip_playback_hw 是预先定义的硬件描述。在open()函数中,可以分配1 段私有数
据。如果硬件配置需要更多的限制,也需设置硬件限制。
当PCM 子流被关闭时,close()函数将被调用。如果open()函数中分配了私有数据,则在close()函数中应
该释放substream 的私有数据,代码清单17.10 给出了close()函数的范例。 代码清单17.10 snd_pcm_ops 结构体中close()函数
1 static int snd_xxx_close(struct snd_pcm_substream *substream) 2 {
3 //释放子流私有数据
4 kfree(substream->runtime->private_data); 5 //... 6 }
驱动中通常可以给snd_pcm_ops 的ioctl()成员函数传递通用的snd_pcm_lib_ioctl()函数。 snd_pcm_ops 的hw_params()成员函数将在应用程序设置硬件参数(PCM 子流的周期大小、缓冲区大小和格
式等)的时候被调用,它的形式如下:
static int snd_xxx_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *hw_params);
在这个函数中,将完成大量硬件设置,甚至包括缓冲区分配,这时可调用如下辅助函数: snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); 仅当DMA 缓冲区已被预先分配的情况下,上述调用才可成立。
与hw_params()对应的函数是hw_free(),它释放由hw_params()分配的资源,例如,通过如下调用释放
snd_pcm_lib_malloc_pages()缓冲区: snd_pcm_lib_free_pages(substream);
当PCM 被“准备”时,prepare()函数将被调用,在其中可以设置采样率、格式等。prepare()函数与
hw_params()函数的不同在于对prepare()的调用发生在snd_pcm_prepare()每次被调用的时候。prepare() 的形式如下:
static int snd_xxx_prepare(struct snd_pcm_substream *substream);
trigger()成员函数在PCM 被开始、停止或暂停时调用,函数的形式如下: static int snd_xxx_trigger(struct snd_pcm_substream *substream, int cmd);
cmd 参数定义了具体的行为, 在trigger() 成员函数中至少要处理SNDRV_PCM_TRIGGER_START 和
SNDRV_PCM_TRIGGER_STOP 命令,如果PCM 支持暂停,还应处理SNDRV_PCM_TRIGGER_PAUSE_PUSH 和
SNDRV_PCM_TRIGGER_PAUSE_RELEASE 命令。如果设备支持挂起/恢复,当能量管理状态发生变化时将处理
SNDRV_PCM_TRIGGER_SUSPEND 和SNDRV_PCM_TRIGGER_RESUME 这2 个命令。注意trigger()函数是原子的,
中途不能睡眠。代码清单17.11 给出了1 个trigger()函数的范例。 代码清单17.11 snd_pcm_ops 结构体中trigger()函数
1 static int snd_xxx_trigger(struct snd_pcm_substream *substream, int cmd) 2 {
3 switch (cmd) 4 {
5 case SNDRV_PCM_TRIGGER_START: 6 // 开启PCM 引擎 7 break;
8 case SNDRV_PCM_TRIGGER_STOP: 9 // 停止PCM 引擎 10 break;
11 ...//其它命令 12 default:
13 return - EINVAL; 14 } 15 }
pointer()函数用于PCM 中间层查询目前缓冲区的硬件位置,该函数以帧的形式返回0~buffer_size – 1
的位置(ALSA 0.5.x 中为字节形式),此函数也是原子的。 copy()和silence()函数一般可以省略,但是,当硬件缓冲区不处于常规内存中时需要。例如,一些设备
有自己的不能被映射的硬件缓冲区,这种情况下,我们不得不将数据从内存缓冲区拷贝到硬件缓冲区。例
外,当内存缓冲区在物理和虚拟地址上都不连续时,这2 个函数也必须被实现。 3、分配缓冲区
分配缓冲区的最简单方法是调用如下函数:
int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, int type, void *data, size_t size, size_t max);
type 参数是缓冲区的类型,包含SNDRV_DMA_TYPE_UNKNOWN(未知)、SNDRV_DMA_TYPE_CONTINUOUS(连续
的非DMA 内存)、SNDRV_DMA_TYPE_DEV (连续的通用设备),SNDRV_DMA_TYPE_DEV_SG(通用设备SG-buffer) 和SNDRV_DMA_TYPE_SBUS(连续的SBUS)。如下代码将分配64KB 的缓冲区: snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),64*1024, 64*1024); 4、设置标志
在构造PCM 实例、设置操作集并分配缓冲区之后,如果有需要,应设置PCM 的信息标志,例如,如果PCM
设备只支持半双工,则这样定义标志:
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; 5、PCM 实例析构
PCM 实例的“析构函数”并非是必须的,因为PCM 实例会被PCM 中间层代码自动释放,如果驱动中分配了
一些特别的内存空间,则必须定义“析构函数”,代码清单17.x 给出了PCM“析构函数”与对应的“构造
函数”,“析构函数”会释放“构造函数”中创建的xxx_private_pcm_data。 代码清单17.12 PCM 设备“析构函数”
1 static void xxxchip_pcm_free(struct snd_pcm *pcm) 2 {
3 /* 从pcm 实例得到chip */
4 struct xxxchip *chip = snd_pcm_chip(pcm); 5 /* 释放自定义用途的内存 */
6 kfree(chip->xxx_private_pcm_data); 7 ... 8 } 9
10 static int __devinit snd_xxxchip_new_pcm(struct xxxchip *chip) 11 {
12 struct snd_pcm *pcm; 13 ...
14 /* 分配自定义用途的内存 */
15 chip->xxx_private_pcm_data = kmalloc(...); 16 pcm->private_data = chip; 17 /* 设置“析构函数” */
18 pcm->private_free = xxxchip_pcm_free; 19 ... 20 }
上述代码第4 行的snd_pcm_chip()从PCM 实例指针获得xxxchip 指针,实际上它就是返回第16 行给PCM
实例赋予的xxxchip 指针。 6、PCM 信息运行时指针
当PCM 子流被打开后,PCM 运行时实例(定义为结构体snd_pcm_runtime,如代码清单17.13)将被分配给
这个子流,这个指针通过substream->runtime 获得。运行时指针包含各种各样的信息:hw_params 及
sw_params 配置的拷贝、缓冲区指针、mmap 记录、自旋锁等,几乎要控制PCM 的所有信息均能从中取得。
代码清单17.13 snd_pcm_runtime 结构体 1 struct snd_pcm_runtime 2 {
3 /* 状态 */
4 struct snd_pcm_substream *trigger_master;
5 snd_timestamp_t trigger_tstamp; /* 触发时间戳 */ 6 int overrange;
7 snd_pcm_uframes_t avail_max;
8 snd_pcm_uframes_t hw_ptr_base; /* 缓冲区复位时的位置 */ 9 snd_pcm_uframes_t hw_ptr_interrupt; /* 中断时的位置*/
共分享92篇相关文档