snd_pcm_open Linux,杂记asla-lib库函数snd_pcm_open打开流程

沃驰
2023-12-01

杂记asla-lib库函数snd_pcm_open打开流程

浅析ac97声卡intel8x0的DMA内存substream->dma_buffer什么时候被赋值

浅析ac97声卡intel8x0的runtime->dma_area是怎么获取的

浅析ac97声卡intel8x0的pci总线DMA物理地址填充和音频数据发送流程

aplay.c

==> main

==> snd_pcm_open(&handle, pcm_name, stream, open_mode); // 打开一路pcm,刷新config配置

如果是"default",同时type等于SND_CONFIG_TYPE_COMPOUND那么这里对应"empty"

static const char *const build_in_pcms[] = {

"adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat",

"linear", "meter", "mulaw", "multi", "null", "empty", "plug", "rate", "route", "share",

"shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul",

NULL

};

_snd_pcm_empty_open和snd_pcm_open_named_slave

==> snd_pcm_open_conf(pcmp, name, root, conf, stream, mode);

==> open_func = snd_dlobj_cache_lookup(open_name);将获得lib库中_snd_pcm_empty_open函数

所以open_func将等于_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等于"/dev/snd/controlC0"

==> snd_open_device(filename, fmode);

ctl->ops = &snd_ctl_hw_ops;

ctl->private_data = hw;

ctl->poll_fd = fd;

*handle = ctl;

filename等于"/dev/snd/pcmC0D0p"

==> 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, info.stream, 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("control mmap failed");

return err;

}

hw->mmap_control = ptr; // 声卡驱动头部填充了一个结构体sndrv_pcm_mmap_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->u.mmap.fd, i->u.mmap.offset);

/*

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操作函数,当应用程序向该area读写不存在的内存数据时,

area->vm_private_data = substream;   // 将执行snd_pcm_vm_ops_data中的fault

// 函数snd_pcm_mmap_data_fault进一步以页为单位申请内存空间,所以如果用户程序需要64k,那么将执行16次,每次申请4k空间[luther.gliethttp].

area->vm_flags |= VM_RESERVED;

atomic_inc(&substream->mmap_count);

return 0;

}

*/

if (ptr == MAP_FAILED) {

SYSERR("mmap failed");

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)之间空间操作,发生

area->vm_private_data = substream;      // 缺页时,内核将调用该vm_ops方法来处理fault异常,

area->vm_flags |= VM_RESERVED;          // 进而执行snd_pcm_mmap_control_fault申请1个page空间

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_pcm_plugin_write_areas函数发送1024帧音频数据,一帧对应一次完整采样,比如stereo立体声

,24bits量化,那么这里一帧对应3*2字节数据,即一次完整采样所需空间[luther.gliethttp].

==> plugin->write(pcm, areas, offset, frames,

slave_areas, slave_offset, &slave_frames);

即调用snd_pcm_linear_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) { // 可以将缓冲中的音频数据填充到硬件中[luther.gliethttp]

/*

* 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->u.dmix.sum_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->u.dmix.sum_buffer + dst_ofs * channels,

sample_size,

sample_size,

sizeof(signed int));

这里的do_mix_areas在i386中,使用下面完全用汇编实现的拷贝函数MIX_AREAS_32完成数据从src到dst的快速拷贝,

每拷贝一次,声卡就会发出一点声音[luther.gliethttp]

/*

*  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, "alsa", &snd_fops)               // 主设备号为116的所有设备都为alsa设备,节点方法集为snd_fops

static const struct file_operations snd_fops =              // alsa的设备名为pcmC0D1c或pcmC0D1p等[luther.gliethttp].

{

.owner =    THIS_MODULE,

.open =        snd_open

};

snd_open

==> __snd_open(inode, file);

==> __snd_open

unsigned int minor = iminor(inode);

mptr = snd_minors[minor];

file->f_op = fops_get(mptr->f_ops);

file->f_op->open(inode, file);

const struct file_operations snd_pcm_f_ops[2] = {

{                                                       // alsa使用到的SNDRV_PCM_STREAM_PLAYBACK放音方法集[luther.gliethttp]

.owner =        THIS_MODULE,

.write =        snd_pcm_write,

.aio_write =        snd_pcm_aio_write,

.open =            snd_pcm_playback_open,

.release =        snd_pcm_release,

.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 =    dummy_get_unmapped_area,

},

{                                                       // alsa使用到的SNDRV_PCM_STREAM_CAPTURE录音方法集[luther.gliethttp]

.owner =        THIS_MODULE,

.read =            snd_pcm_read,

.aio_read =        snd_pcm_aio_read,

.open =            snd_pcm_capture_open,

.release =        snd_pcm_release,

.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 =    dummy_get_unmapped_area,

}

};

=========================================================================

snd_intel8x0_probe

==> snd_intel8x0_create

==> request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_SHARED,

card->shortname, chip)

snd_intel8x0_interrupt

snd_intel8x0_update

snd_open

==> snd_pcm_playback_open

==> snd_pcm_open

==> snd_pcm_open_file

==> snd_pcm_open_substream

==> substream->ops->open(substream)即snd_intel8x0_playback_ops.open

==> snd_intel8x0_playback_open

==> snd_intel8x0_pcm_open

static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev)

{

struct intel8x0 *chip = snd_pcm_substream_chip(substream);

struct snd_pcm_runtime *runtime = substream->runtime;

int err;

ichdev->substream = substream;

runtime->hw = snd_intel8x0_stream; // 声卡配置硬件信息[luther.gliethttp]

runtime->hw.rates = ichdev->pcm->rates;

snd_pcm_limit_hw_rates(runtime);

if (chip->device_type == DEVICE_SIS) {

runtime->hw.buffer_bytes_max = 64*1024;

runtime->hw.period_bytes_max = 64*1024;

}

if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)

return err;

runtime->private_data = ichdev;

return 0;

}

ioctl(SNDRV_PCM_IOCTL_HW_PARAMS)

==> snd_pcm_f_ops.unlocked_ioctl即:snd_pcm_playback_ioctl

==> snd_pcm_playback_ioctl

==> snd_pcm_playback_ioctl1

==> snd_pcm_common_ioctl1

case SNDRV_PCM_IOCTL_HW_PARAMS:

return snd_pcm_hw_params_user(substream, arg);

==> snd_pcm_hw_params_user

==> snd_pcm_hw_params

==> substream->ops->hw_params即snd_intel8x0_playback_ops.hw_params

==> snd_intel8x0_hw_params

==> snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),

params_channels(hw_params),

ichdev->pcm->r[dbl].slots);

ioctl(SNDRV_PCM_IOCTL_PREPARE)

==> snd_pcm_playback_ioctl

==> snd_pcm_playback_ioctl1

==> snd_pcm_common_ioctl1

==> snd_pcm_prepare // prepare the PCM substream to be triggerable

==> snd_pcm_action_nonatomic(&snd_pcm_action_prepare,

substream, f_flags);

==> snd_pcm_action_single(ops, substream, state);

ops->pre_action(substream, state);

ops->do_action(substream, state);

ops->post_action(substream, state);

上面ops就是之前提到的snd_pcm_action_prepare

==> snd_pcm_do_prepare调用snd_pcm_do_reset(substream, 0);复位

substream->ops->prepare(substream);即snd_intel8x0_playback_ops.prepare

==> snd_intel8x0_pcm_prepare

static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)

{

struct intel8x0 *chip = snd_pcm_substream_chip(substream);

struct snd_pcm_runtime *runtime = substream->runtime;

struct ichdev *ichdev = get_ichdev(substream);

《浅析ac97声卡intel8x0的runtime->dma_area是怎么获取的》

ichdev->physbuf = runtime->dma_addr;    // dma缓冲区地址

ichdev->size = snd_pcm_lib_buffer_bytes(substream); // 将帧缓冲大小转为字节空间大小[luther.gliethttp]

ichdev->fragsize = snd_pcm_lib_period_bytes(substream);

if (ichdev->ichd == ICHD_PCMOUT) {

snd_intel8x0_setup_pcm_out(chip, runtime); // 为play模式设置ac97寄存器[luther.gliethttp]

if (chip->device_type == DEVICE_INTEL_ICH4)

ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;

}

snd_intel8x0_setup_periods(chip, ichdev); // 设置PCI总线ac97的bank地址空间[luther.gliethttp]

return 0;

}

==> snd_intel8x0_setup_pcm_out

static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,

struct snd_pcm_runtime *runtime)

{

unsigned int cnt;

int dbl = runtime->rate > 48000;

// 一共有如下几种设备:enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };

spin_lock_irq(&chip->reg_lock);

switch (chip->device_type) {

case DEVICE_ALI:

cnt = igetdword(chip, ICHREG(ALI_SCR));

cnt &= ~ICH_ALI_SC_PCM_246_MASK;

if (runtime->channels == 4 || dbl)

cnt |= ICH_ALI_SC_PCM_4;

else if (runtime->channels == 6)

cnt |= ICH_ALI_SC_PCM_6;

iputdword(chip, ICHREG(ALI_SCR), cnt);

break;

case DEVICE_SIS:

cnt = igetdword(chip, ICHREG(GLOB_CNT));

cnt &= ~ICH_SIS_PCM_246_MASK;

if (runtime->channels == 4 || dbl)

cnt |= ICH_SIS_PCM_4;

else if (runtime->channels == 6)

cnt |= ICH_SIS_PCM_6;

iputdword(chip, ICHREG(GLOB_CNT), cnt);

break;

default:

cnt = igetdword(chip, ICHREG(GLOB_CNT));

cnt &= ~(ICH_PCM_246_MASK | ICH_PCM_20BIT);

if (runtime->channels == 4 || dbl)

cnt |= ICH_PCM_4;

else if (runtime->channels == 6)

cnt |= ICH_PCM_6;

else if (runtime->channels == 8)

cnt |= ICH_PCM_8;

if (chip->device_type == DEVICE_NFORCE) {

/* reset to 2ch once to keep the 6 channel data in alignment,

* to start from Front Left always

*/

if (cnt & ICH_PCM_246_MASK) {

iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_PCM_246_MASK);

spin_unlock_irq(&chip->reg_lock);

msleep(50); /* grrr... */

spin_lock_irq(&chip->reg_lock);

}

} else if (chip->device_type == DEVICE_INTEL_ICH4) {

if (runtime->sample_bits > 16)

cnt |= ICH_PCM_20BIT;

}

iputdword(chip, ICHREG(GLOB_CNT), cnt);

break;

}

spin_unlock_irq(&chip->reg_lock);

}

ioctl(SNDRV_PCM_IOCTL_START)

==> snd_pcm_playback_ioctl

==> snd_pcm_playback_ioctl1

==> snd_pcm_common_ioctl1

==> snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);

==> snd_pcm_action_single // state等于SNDRV_PCM_STATE_RUNNING

static struct action_ops snd_pcm_action_start = {

.pre_action = snd_pcm_pre_start,

.do_action = snd_pcm_do_start,

.undo_action = snd_pcm_undo_start,

.post_action = snd_pcm_post_start

};

ops->pre_action(substream, state);

ops->do_action(substream, state);

ops->post_action(substream, state);

上面ops就是之前提到的snd_pcm_action_start

==> snd_pcm_do_start

==> substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);即snd_intel8x0_playback_ops.trigger

==> snd_intel8x0_pcm_trigger启动ac97数据传输

以上都只是执行一次[luther.gliethttp]

只要发送音频数据,就会执行该ioctl更新pointer

ioctl(SNDRV_PCM_IOCTL_HWSYNC)

==> snd_pcm_playback_ioctl

==> snd_pcm_playback_ioctl1

==> snd_pcm_common_ioctl1

==> snd_pcm_hwsync

case SNDRV_PCM_STATE_RUNNING:

if ((err = snd_pcm_update_hw_ptr(substream)) < 0)

break;

==> snd_pcm_update_hw_ptr

==> snd_pcm_update_hw_ptr_post

==> snd_pcm_update_hw_ptr_pos

==> substream->ops->pointer(substream);即snd_intel8x0_playback_ops.pointer

==> snd_intel8x0_pcm_pointer // 更新dma缓冲区数据最后可用数据索引值[luther.gliethttp]

 类似资料: