-
alsa
声卡
/dev/snd/pcmC0D
0p
的
open
打
开
流程
< br>原文地址:
/?uid=20564848&do=blog&cuid=211
6725
aplay.c
==>
main
==> snd_pcm_open(&handle,
pcm_name, stream, open_mode); //
打开一路
p>
pcm,
刷新
config
配置
如果是
同时
type
等于
SND_C
ONFIG_TYPE_COMPOUND
那么这里对应
static const char *const build_in_pcms[] =
{
NULL
};
_snd_pcm_
empty_open
和
snd_pcm_open_name
d_slave
==> snd_pcm_open_conf(pcmp,
name, root, conf, stream, mode);
==>
open_func = snd_dlobj_cache_lookup(open_name);
将获得
lib
库中
_s
nd_pcm_empty_open
函数
所以
open_func
p>
将等于
_snd_pcm_empty_open
_snd_pcm_empty_open
_snd_pcm_asym_open
_snd_pcm_plug_open
_snd_pcm_softvol_open
_snd_pcm_dmix_open
_snd_pcm_hw_open
==> snd_pcm_hw_open(pcmp, name, card,
device, subdevice, stream,
mode | (nonblock ? SND_PCM_NONBLOCK :
0),
0, sync_ptr_ioctl);
==> snd_ctl_hw_open
filename
等于
==>
snd_open_device(filename, fmode);
ctl->ops = &snd_ctl_hw_ops;
ctl->private_data = hw;
ctl->poll_fd = fd;
*handle = ctl;
filename
等于
==> fd =
snd_open_device(filename, fmode);
==>
return snd_pcm_hw_open_fd(pcmp, name, fd, 0,
sync_ptr_ioctl);
==> snd_pcm_new(&pcm,
SND_PCM_TYPE_HW, name, , mode);
pcm->ops = &snd_pcm_hw_ops;
pcm->fast_ops = &snd_pcm_hw_fast_ops;
static int
snd_pcm_hw_mmap_control(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private_data;
void *ptr;
int err;
if
(hw->sync_ptr == NULL) { //
如果还没有
mmap,
那么执行
mmap
映射内核空间驱动使用的声音缓冲区
ptr = mmap(NULL,
page_align(sizeof(struct sndrv_pcm_mmap_control)),
PROT_READ|PROT_WRITE,
MAP_FILE|MAP_SHARED,
hw->fd,
SNDRV_PCM_MMAP_OFFSET_CONTROL);
if (ptr == MAP_FAILED ||
ptr == NULL) {
err = -errno;
SYSMSG(
return err;
}
hw->mmap_control = ptr;
//
声卡驱动头部填充了一个结构体
sndrv_pcm_m
map_control,
类似
qvfb
显
示原理
.
// struct
sndrv_pcm_mmap_control {
//
sndrv_pcm_uframes_t appl_ptr;
/* RW: appl ptr (0...boundary-1) */
//
sndrv_pcm_uframes_t
avail_min;
/* RW: min available
frames for wakeup */
// };
} else {
hw->mmap_control->avail_min = 1;
}
snd_pcm_set_appl_ptr(pcm,
&hw->mmap_control->appl_ptr, hw->fd,
SNDRV_PCM_MMAP_OFFSET_CONTROL);
return 0;
}
snd_pcm_mmap
switch
(i->type) {
case SND_PCM_AREA_MMAP: //
表
示为数据区分配驱动内存
,
在
snd_
pcm_hw_channel_info
中设置
了
type
ptr = mmap(NULL, size,
PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->,
i->);
/*
mmap
==> snd_pcm_mmap_data
==>
snd_pcm_default_mmap
// mmap the DMA
buffer on RAM
static int
snd_pcm_default_mmap(struct snd_pcm_substream
*substream,
struct
vm_area_struct *area)
{
area->vm_ops = &snd_pcm_vm_ops_data; //
vma
操作函数
,
当应用程序向该
p>
area
读写不存在的内存数据
时
,
area->vm_private_data = substream;
//
将执行
snd_pcm_vm_
ops_data
中的
fault
//
函数
snd_pcm_mmap
_data_fault
进一步以页为单位申请内存空间
,
p>
所以如果用户程序需要
64k,
那么将
p>
执行
16
次
,
p>
每次申请
4k
空间
[ttp].
area->vm_flags |=
VM_RESERVED;
atomic_inc(&substream->mmap_count);
return 0;
}
*/
if (ptr == MAP_FAILED) {
SYSERR(
return -errno;
}
i->addr = ptr;
==> snd_pcm_mmap_control
static int snd_pcm_mmap_control(struct
snd_pcm_substream *substream, struct file *file,
struct vm_area_struct
*area)
{
struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return
-EINVAL;
runtime =
substream->runtime;
size
= area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct
snd_pcm_mmap_control)))
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_control;
//
当对
( area->vm_start,area->
vm_end)
之间空间操作
,
发
p>
生
area->vm_private_data = substream;
//
缺页时
,
内核将调用该
vm_ops
方法来处理
fault
异常
,
area->vm_flags |= VM_RESERVED;
//
进而执行
snd_pcm_mm
ap_control_fault
申请
1
个
page
空
间
< br>
return 0;
}
==> writei_func = snd_pcm_writei;
==> playback(argv[optind++]);
==> playback_go(fd, dtawave,
pbrec_count, FORMAT_WAVE, name);
==>
pcm_write(audiobuf, l);
==>
writei_func(handle, data,
count);
就是调用上面的
snd_pcm_writei
==> snd_pcm_writei
==>
_snd_pcm_writei
==>
pcm->fast_ops->writei(pcm->fast_op_arg, buffer,
size);
==> snd_pcm_plugin_writei
==> snd_pcm_write_areas(pcm, areas, 0,
size,
snd_pcm_plugin_write_areas);
==> avail = snd_pcm_avail_update(pcm);
//
获取可用缓冲区位置偏移索引值
==> func()
就是
snd_p
cm_plugin_write_areas
函数发送
102
4
帧音频数据
,
一帧对应一次完整采样
,
比如
stereo
< br>立体声
,24bits
量化<
/p>
,
那么这里一帧对应
3*2
字节数据
,
即一次完整采样所需空间
[ttp].
==> plugin->write(pcm,
areas, offset, frames,
slave_areas, slave_offset,
&slave_frames);
即调用
snd_pcm_l
inear_write_areas
函数将
areas
中的
frames
频数据拷贝到
slave_areas
内存区
==>
pcm->fast_ops->mmap_commit(pcm->fast_op_arg,
offset, frames);
==>
snd_pcm_dmix_mmap_commit
==>
snd_pcm_dmix_sync_area
/*
*
synchronize shm ring buffer with
hardware
*/
static void
snd_pcm_dmix_sync_area(snd_pcm_t *pcm)
==> /* add sample areas here */
src_areas =
snd_pcm_mmap_areas(pcm);
dst_areas =
snd_pcm_mmap_areas(dmix->spcm); //
添加
==>
mix_areas(dmix, src_areas, dst_areas, appl_ptr,
slave_appl_ptr, transfer);
if (dmix->interleaved) { //
可以将缓冲中的音频数据填充到硬件中
[ttp]
/*
*
process all areas in one loop
* it optimizes the
memory accesses for this case
*/
do_mix_areas(size *
channels,
(unsigned char *)dst_areas[0].addr +
sample_size * dst_ofs * channels,
(unsigned char *)src_areas[0].addr +
sample_size * src_ofs * channels,
dmix->_buffer + dst_ofs * channels,
sample_size,
sample_size,
sizeof(signed int));
return;
}
==>
do_mix_areas(size * channels,
(unsigned char *)dst_areas[0].addr +
sample_size * dst_ofs * channels,
(unsigned char *)src_areas[0].addr +
sample_size * src_ofs * channels,
dmix->_buffer + dst_ofs * channels,
sample_size,
sample_size,
sizeof(signed int));
p>
这里的
do_mix_areas
在
i386
中
,
使用下
面完全用汇编实现的拷贝函数
MIX_AREAS_32
完成数
据从
src
到
dst
< br>的快速拷贝
,
每拷贝一次
,<
/p>
声卡就会发出一点声音
[ttp]
/*
*
for plain i386, 32-bit
version (24-bit resolution)
*/
static void MIX_AREAS_32(unsigned int
size,
volatile signed int *dst, signed int
*src,
volatile signed int *sum, size_t
dst_step,
size_t src_step, size_t
sum_step)
_snd_pcm_asym_open
_snd_pcm_dmix_open
snd_pcm_plugin_avail_update
==> snd_pcm_avail_update(slave);
==>
pcm->fast_ops->avail_update(pcm->fast_op_arg);
==> snd_pcm_dmix_avail_update
==> snd_pcm_mmap_playback_avail(pcm);
alsa_sound_init
#define CONFIG_SND_MAJOR
116
/* standard
configuration */
static int major =
CONFIG_SND_MAJOR;
module_init(alsa_sound_init)
alsa_sound_init
==>
register_chrdev(major,
//
主设
备号为
116
的所有设备都为
alsa
设备
,
节点
方
法集为
snd_fops
static const
struct file_operations snd_fops =
// alsa
的设备名为
pcmC0
D1c
或
pcmC0D1p
等
[ttp].
{
.owner =
THIS_MODULE,
.open =
snd_open
};
snd_open
==>
__snd_open(inode, file);
==> __snd_open
-
-
-
-
-
-
-
-
-
上一篇:TMS320F28335中文资料
下一篇:TMS320F28335及其最小系统设计