kernel 4.19音频框架超详细分析(ALSA数据流程、控制流程、驱动层)

您所在的位置:网站首页 alsa编程书籍 kernel 4.19音频框架超详细分析(ALSA数据流程、控制流程、驱动层)

kernel 4.19音频框架超详细分析(ALSA数据流程、控制流程、驱动层)

2024-07-16 06:37| 来源: 网络整理| 查看: 265

4.19音频框架

文章目录 4.19音频框架内核音频大致框架数据流程应用层[aplay.c]调用snd_pcm_open [alsa-lib pcm.c].writei = snd_pcm_hw_writei main函数判断是否交错以playback为例以playback_raw为例调用playback_go具体应用层的数据处理,暂且略过。后续有空再分析调用pcm_write调用writei_func ALSA Library API调用snd_pcm_writei [alsa-lib pcm.c]调用_snd_pcm_writei [alsa-lib pcm_local.h]pcm->fast_ops->writei = snd_pcm_hw_writeisnd_pcm_hw_writei [alsa-lib pcm_hw.c]ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi) ALSA-CORE注册pcm_dev时,指定.unlocked_ioctlsnd_pcm_dev_register [pcm.c]snd_register_device [pcm.c]snd_pcm_f_ops [pcm_native.c].unlocked_ioctl=snd_pcm_ioctl, [pcm_native.c]snd_pcm_common_ioctl [pcm_native.c] 根据alsa-lib传入的cmd,调用snd_pcm_xferi_frames_ioctl [pcm_native.c]snd_pcm_lib_write [pcm_native.c]snd_pcm_lib_write [pcm.h]__snd_pcm_lib_xfer [pcm\_lib.c] 【重点】数据写入DMA buffer假定write为interleaved_copy假定transfer为default_write_copy。get_dma_ptr 调用snd_pcm_start [pcm_native.c]snd_pcm_action [pcm_native.c]调用snd_pcm_action_single [pcm_native.c]snd_pcm_do_start [pcm_native.c]substream->ops->trigger 驱动层rockchip_pdm_trigger调用 rockchip_pdm_rxctrlregmap_update_bits 控制流程应用层 [amixer.c]cset调用snd_ctl_open和snd_ctl_elem_write ALSA Library APIsnd_ctl_elem_write调用ctl->ops->element_write,snd_ctl_open_snd_ctl_hw_opensnd_ctl_hw_openctl->ops = &snd_ctl_hw_ops .element_write = snd_ctl_hw_elem_writeioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) ALSA-CORE [control.c]**snd_ctl_create创建control core****snd_ctl_dev_register注册control设备**调用snd_register_device并且传入了snd_ctl_f_ops.unlocked_ioctl = snd_ctl_ioctl,根据传入的cmd,进行操作。假定调用snd_ctl_elem_write_user**memdup_user** →snd_ctl_elem_write→ **copy_to_user**数据类型为struct snd_ctl_elem_value调用snd_ctl_elem_writeresult = kctl->put(kctl, control); 驱动层以rk3308_codec为例,创建controls首先要定义struct **snd_kcontrol_new** 。**调用include/sound/soc.h中的宏SOC_SINGLE_RANGE_TLV**这里又调用了个DECLARE_TLV_DB_SCALE宏来设置。SOC_SINGLE_RANGE_TLV宏 添加contols**snd_soc_component_driver**snd_soc_add_component_controls [soc/soc-core.c] 驱动中自定义的put函数snd_soc_put_volsw_range [soc/soc-ops.c]**snd_soc_component_update_bits负责更新寄存器的值** 实践认识

内核音频大致框架 +--------+ +--------+ +--------+ |apaly | |arecord | |amixer | +--------+ +--------+ +--------+ | ^ ^ V | V +--------------------------------+ | ALSA Library API | | (tinyalsa, alsa-lib) | +--------------------------------+ user space ^ ----------------------|--------------------- kernel space V +--------------------------------+ | ALSA CORE | | +-------+ +-------+ +------+ | | | PCM | |CONTROL| | MIDI |...| | +-------+ +-------+ +------+ | +--------------------------------+ | +--------------------------------+ | ASoC CORE | +--------------------------------+ | +--------------------------------+ | hardware driver | | +-------+ +--------+ +-----+ | | |Machine| |Platform| |Codec| | | +-------+ +--------+ +-----+ | +--------------------------------+ +------------------------------------------+ | Machine | | +--------------+ +--------------+ | | | Platform | | Codec | | | | | I2S | | | | | cpu_dai||codec_dai | | | | | | | | | +--------------+ +--------------+ | +------------------------------------------+

pcm数据流向,以playback为例

copy_from_user DMA I2S DAC ^ ^ ^ ^ +---------+ | +----------+ | +-----------+ | +-----+ | +------+ |userspace+-------->DMA Buffer+------->I2S TX FIFO+------->CODEC+------->SPK/HP| +---------+ +----------+ +-----------+ +-----+ +------+

alsa驱动框架核心层:创建声卡设备的控制接口和PCM设备

在这里插入图片描述

数据流程

​ sound/core/pcm_native.c 对下层的PCM驱动提供包装,为上层提供统一的接口。

在这里插入图片描述

应用层[aplay.c]

​ 以播放为例。

调用snd_pcm_open [alsa-lib pcm.c]

​ handle 返回PCM handle,pcm_name为设备名,stream为SND_PCM_STREAM_PLAYBACK,

​ open_mode 为打开pcm句柄时的一些附加参数 SND_PCM_NONBLOCK 非阻塞打开(默认阻塞打开),SND_PCM_ASYNC 异步模式打开。

err = snd_pcm_open(&handle, pcm_name, stream, open_mode);

snd_pcm_hw_open

int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, int card, int device, int subdevice, snd_pcm_stream_t stream, int mode, int mmap_emulation ATTRIBUTE_UNUSED, int sync_ptr_ioctl) { …… snd_ctl_close(ctl); return snd_pcm_hw_open_fd(pcmp, name, fd, sync_ptr_ioctl); _err: snd_ctl_close(ctl); return ret; }

snd_pcm_open

└── snd_pcm_open_noupdate

​ └── snd_pcm_open_conf

​ └── snd_dlobj_cache_get

​ └── _snd_pcm_hw_open [假定传入hw参数,从lib库中获取]

​ └── snd_pcm_hw_open

​ └── snd_pcm_hw_open_fd ​ └── pcm->fast_ops = &snd_pcm_hw_fast_ops;

参数如下:

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 }; .writei = snd_pcm_hw_writei

alsa-lib的pcm_hw.c中定义了snd_pcm_hw_fast_ops,其中.writei = snd_pcm_hw_writei,这个后面会用到。

static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = { …… .writei = snd_pcm_hw_writei, .writen = snd_pcm_hw_writen, .readi = snd_pcm_hw_readi, .readn = snd_pcm_hw_readn, …… }; main函数判断是否交错 if (interleaved) { if (optind > argc - 1) { if (stream == SND_PCM_STREAM_PLAYBACK) playback(NULL); else capture(NULL); } else { while (optind fast_op_arg, buffer, size); } /** PCM handle */ typedef struct _snd_pcm snd_pcm_t; struct _snd_pcm { void *open_func; char *name; snd_pcm_type_t type; snd_pcm_stream_t stream; int mode; …… const snd_pcm_ops_t *ops; const snd_pcm_fast_ops_t *fast_ops; //这里定义了 snd_pcm_t *op_arg; snd_pcm_t *fast_op_arg; void *private_data; …… }; pcm->fast_ops->writei = snd_pcm_hw_writei

前面调用snd_pcm_open时,指定了.writei = snd_pcm_hw_writei

snd_pcm_hw_writei [alsa-lib pcm_hw.c] static snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) { int err; snd_pcm_hw_t *hw = pcm->private_data; int fd = hw->fd; struct snd_xferi xferi; xferi.buf = (char*) buffer; xferi.frames = size; xferi.result = 0; /* make valgrind happy */ if (ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi) device, &snd_pcm_f_ops[cidx], pcm, //此处传入snd_pcm_f_ops &pcm->streams[cidx].dev); …… } snd_register_device [pcm.c] int snd_register_device(int type, struct snd_card *card, int dev, const struct file_operations *f_ops, void *private_data, struct device *device) { int minor; int err = 0; struct snd_minor *preg; …… 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; //此处指定f_ops preg->private_data = private_data; …… } EXPORT_SYMBOL(snd_register_device); snd_pcm_f_ops [pcm_native.c] const struct file_operations snd_pcm_f_ops[2] = { { .owner = THIS_MODULE, .write = snd_pcm_write, .write_iter = snd_pcm_writev, .open = snd_pcm_playback_open, .release = snd_pcm_release, .llseek = no_llseek, .poll = snd_pcm_poll, //64位 SNDRV_PCM_IOCTL_WRITEN_FRAMES .unlocked_ioctl = snd_pcm_ioctl, //32位 SNDRV_PCM_IOCTL_WRITEN_FRAMES32 .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, .read_iter = snd_pcm_readv, …… } }; .unlocked_ioctl=snd_pcm_ioctl, [pcm_native.c] static long snd_pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_pcm_file *pcm_file; pcm_file = file->private_data; if (((cmd >> 8) & 0xff) != 'A') return -ENOTTY; return snd_pcm_common_ioctl(file, pcm_file->substream, cmd, (void __user *)arg); } snd_pcm_common_ioctl [pcm_native.c] static int snd_pcm_common_ioctl(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { struct snd_pcm_file *pcm_file = file->private_data; int res; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; res = snd_power_wait(substream->pcm->card, SNDRV_CTL_POWER_D0); if (res pcm, "unknown ioctl = 0x%x\n", cmd); return -ENOTTY; } 根据alsa-lib传入的cmd,调用snd_pcm_xferi_frames_ioctl [pcm_native.c] static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream, struct snd_xferi __user *_xferi) { struct snd_xferi xferi; 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)) //Write a simple value into user space. return -EFAULT; if (copy_from_user(&xferi, _xferi, sizeof(xferi)))//从用户空间拷贝数据 return -EFAULT; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); //播放流调用此函数 else result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames); //不进行地址空间检查,在对同一区域进行多次访问时很有用 __put_user(result, &_xferi->result); return result runtime; snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; snd_pcm_uframes_t avail; pcm_copy_f writer; pcm_transfer_f transfer; bool nonblock; bool is_playback; int err; err = pcm_sanity_check(substream);/* sanity-check for read/write methods */ if (err stream == SNDRV_PCM_STREAM_PLAYBACK; //确认是播放流 if (interleaved) { if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1)//数据为交错,且通道数>1 return -EINVAL; //指定writer,后面会调用。interleaved_copy主要做了frames_to_bytes writer = interleaved_copy; } else { if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; writer = noninterleaved_copy; } if (!data) { if (is_playback) transfer = fill_silence;//如果数据为空且在播放,则填充静音而不是复制数据 else return -EINVAL; } else if (in_kernel) { if (substream->ops->copy_kernel) transfer = substream->ops->copy_kernel; else transfer = is_playback ? default_write_copy_kernel : default_read_copy_kernel; } else { if (substream->ops->copy_user) transfer = (pcm_transfer_f)substream->ops->copy_user; else transfer = is_playback ? default_write_copy : default_read_copy; } if (size == 0) return 0; nonblock = !!(substream->f_flags & O_NONBLOCK); snd_pcm_stream_lock_irq(substream); err = pcm_accessible_state(runtime); if (err status->state == SNDRV_PCM_STATE_PREPARED && size >= runtime->start_threshold) { //录音流,状态处于PREPARED,size大于阈值 err = snd_pcm_start(substream); if (err twake = runtime->control->avail_min ? : 1; if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream);//更新hw_ptr【分析在buffer管理部分】 //若substream为播放流,调用snd_pcm_playback_avail //获取可用(可写)的播放空间 //avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr; //hw_ptr指向硬件已经处理过的数据位置,appl_ptr为用户程序已经处理过的数据位置。appl_ptr之后到buffer末尾这段,是可以写入的,hw_ptr之前的空间也是可写的(其中的数据已被播放)。 appl_ptr比hw_ptr靠后。 avail = snd_pcm_avail(substream); while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t cont; if (!avail) {//当无可写空间时 if (!is_playback && runtime->status->state == SNDRV_PCM_STATE_DRAINING) { //处于录音流且状态为DRAINING,暂停 snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); goto _end_unlock; } if (nonblock) { err = -EAGAIN; goto _end_unlock; } runtime->twake = min_t(snd_pcm_uframes_t, size, runtime->control->avail_min ? : 1); err = wait_for_avail(substream, &avail);//等待avail_min数据可用 if (err avail ? avail : size; //frames取size和avail中较小的值 //READ_ONCE可保证在多线程下被其它函数调用变量时不会出错 appl_ptr = READ_ONCE(runtime->control->appl_ptr);//HW buffer的写指针 appl_ofs = appl_ptr % runtime->buffer_size; //写指针在当前HW buffer中的位置。 cont = runtime->buffer_size - appl_ofs; //当前HW buffer中还未被写过的空间数 if (frames > cont) frames = cont; //令frames不超出count if (snd_BUG_ON(!frames)) { runtime->twake = 0; snd_pcm_stream_unlock_irq(substream); return -EINVAL; } snd_pcm_stream_unlock_irq(substream); err = writer(substream, appl_ofs, data, offset, frames, transfer);//调用前面的interleaved_copy 主要是frames_to_bytes //将数据写入DMA缓冲区,分析在下面。 snd_pcm_stream_lock_irq(substream); if (err boundary) appl_ptr -= runtime->boundary; //更新给定的appl_ptr,在需要时调用ack callback,返回错误时,恢复为原始值。 err = pcm_lib_apply_appl_ptr(substream, appl_ptr); if (err status->state == SNDRV_PCM_STATE_PREPARED && snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { //当处于播放流,状态为PREPARED,有效数据大于启动阈值时 err = snd_pcm_start(substream); //这里开始DMA传输 if (err twake = 0; if (xfer > 0 && err >= 0) snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } EXPORT_SYMBOL(__snd_pcm_lib_xfer); 数据写入DMA buffer

​ 这里主要是

err = writer(substream, appl_ofs, data, offset, frames, transfer);//调用前面的interleaved_copy frames_to_bytes

​ __snd_pcm_lib_xfer中指定了writer为interleaved_copy或noninterleaved_copy。

假定write为interleaved_copy /* call transfer function with the converted pointers and sizes; * for interleaved mode, it's one shot for all samples */ static int interleaved_copy(struct snd_pcm_substream *substream, snd_pcm_uframes_t hwoff, void *data, snd_pcm_uframes_t off, snd_pcm_uframes_t frames, pcm_transfer_f transfer) { struct snd_pcm_runtime *runtime = substream->runtime; /* convert to bytes */ //frame_bits = snd_pcm_format_physical_width(pcm_format) * channels hwoff = frames_to_bytes(runtime, hwoff); //返回 hwoff * runtime->frame_bits / 8; off = frames_to_bytes(runtime, off); frames = frames_to_bytes(runtime, frames); return transfer(substream, 0, hwoff, data + off, frames); } 假定transfer为default_write_copy。 /* default copy_user ops for write; used for both interleaved and non- modes */ static int default_write_copy(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, void *buf, unsigned long bytes) { if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff), //get_dma_ptr:计算要写入/读取的目标DMA缓冲区位置 (void __user *)buf, bytes)) return -EFAULT; return 0; } get_dma_ptr /* calculate the target DMA-buffer position to be written/read */ static void *get_dma_ptr(struct snd_pcm_runtime *runtime, int channel, unsigned long hwoff) { return runtime->dma_area + hwoff + channel * (runtime->dma_bytes / runtime->channels); } 调用snd_pcm_start [pcm_native.c] /** * snd_pcm_start - start all linked streams * @substream: the PCM substream instance * Return: Zero if successful, or a negative error code. * The stream lock must be acquired before calling this function. */ int snd_pcm_start(struct snd_pcm_substream *substream) { return snd_pcm_action(&snd_pcm_action_start, substream, //传入snd_pcm_action_start为ops SNDRV_PCM_STATE_RUNNING); }

snd_pcm_action_start中定义了.do_action为snd_pcm_do_start,后面会调用到。

static const 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 }; snd_pcm_action [pcm_native.c] /* * Note: call with stream lock */ static int snd_pcm_action(const struct action_ops *ops, struct snd_pcm_substream *substream, int state) { int res; if (!snd_pcm_stream_linked(substream)) return snd_pcm_action_single(ops, substream, state); if (substream->pcm->nonatomic) { if (!mutex_trylock(&substream->group->mutex)) { mutex_unlock(&substream->self_group.mutex); mutex_lock(&substream->group->mutex); mutex_lock(&substream->self_group.mutex); } res = snd_pcm_action_group(ops, substream, state, 1); mutex_unlock(&substream->group->mutex); } else { if (!spin_trylock(&substream->group->lock)) { spin_unlock(&substream->self_group.lock); spin_lock(&substream->group->lock); spin_lock(&substream->self_group.lock); } res = snd_pcm_action_group(ops, substream, state, 1); spin_unlock(&substream->group->lock); } return res; }

假定substream尚未link

调用snd_pcm_action_single [pcm_native.c] /* * Note: call with stream lock */ static int snd_pcm_action_single(const struct action_ops *ops, struct snd_pcm_substream *substream, int state) { int res; res = ops->pre_action(substream, state); if (res do_action(substream, state); if (res == 0) ops->post_action(substream, state); else if (ops->undo_action) ops->undo_action(substream, state); return res; } snd_pcm_do_start [pcm_native.c] static int snd_pcm_do_start(struct snd_pcm_substream *substream, int state) { //在snd_pcm_pre_start中会runtime->trigger_master = substream; if (substream->runtime->trigger_master != substream) return 0; return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); } substream->ops->trigger 驱动层

传入到驱动中的trigger函数。

以rockchipo_pdm驱动为例

rockchip_pdm_trigger static int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct rk_pdm_dev *pdm = to_info(dai); int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) rockchip_pdm_rxctrl(pdm, 1); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) rockchip_pdm_rxctrl(pdm, 0); break; default: ret = -EINVAL; break; } return ret; } 调用 rockchip_pdm_rxctrl static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on) { if (on) { regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RD_MSK, PDM_DMA_RD_EN); regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, PDM_RX_MASK, PDM_RX_START); } else { regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RD_MSK, PDM_DMA_RD_DIS); regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, PDM_RX_MASK | PDM_RX_CLR_MASK, PDM_RX_STOP | PDM_RX_CLR_WR); } } regmap_update_bits

在regmap.h中

#define regmap_update_bits(map, reg, mask, val) \ regmap_update_bits_base(map, reg, mask, val, NULL, false, false)

regmap_update_bits

└── regmap_update_bits_base

​ └── _regmap_update_bits

​ └── __regmap_write

​ └── regcache_write

​ └── map->cache_ops->write

控制流程

​ sound/core/control.c对下层的Control提供包装,为上层提供统一的接口,snd_ctl_f_ops文件操作结构提供控制功能函数,其中主要是snd_ctl_ioctl函数。

​ Control设备和PCM设备一样,都属于声卡下的逻辑设备。用户空间的应用程序通过alsa-lib访问该Control设备,读取或控制control的控制状态,从而达到控制音频Codec进行各种Mixer等控制操作。

在这里插入图片描述

应用层 [amixer.c]

alsa-utils-1.1.5中amixer.c,配置音量。

amixer cset 为例。

cset调用snd_ctl_open和snd_ctl_elem_write static int cset(int argc, char *argv[], int roflag, int keep_handle) { int err; static snd_ctl_t *handle = NULL; snd_ctl_elem_info_t *info; snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; snd_ctl_elem_info_alloca(&info); snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); …… //此处调用snd_ctl_open,返回&handle if (handle == NULL && (err = snd_ctl_open(&handle, card, 0)) id.numid)); return ctl->ops->element_write(ctl, data); } 调用ctl->ops->element_write,

而具体函数则要看调用snd_ctl_open时指定的是哪一个。

snd_ctl_open /** * \brief Opens a CTL * \param ctlp Returned CTL handle * \param name ASCII identifier of the CTL handle * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC) * \return 0 on success otherwise a negative error code */ int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode) { snd_config_t *top; int err; assert(ctlp && name); err = snd_config_update_ref(&top); if (err ops = &snd_ctl_shm_ops

​ └── .element_write = snd_ctl_shm_elem_write

​ └── ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control)

_snd_ctl_hw_open int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode) { …… return snd_ctl_hw_open(handlep, name, card, mode); } SND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION); snd_ctl_hw_open int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode) { int fd, ver; char filename[sizeof(SNDRV_FILE_CONTROL) + 10]; int fmode; snd_ctl_t *ctl; snd_ctl_hw_t *hw; int err; …… err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name); if (err ops = &snd_ctl_hw_ops; //指定ctl->ops ctl->private_data = hw; ctl->poll_fd = fd; *handle = ctl; return 0; } ctl->ops = &snd_ctl_hw_ops static const snd_ctl_ops_t snd_ctl_hw_ops = { …… .element_read = snd_ctl_hw_elem_read, .element_write = snd_ctl_hw_elem_write, …… }; .element_write = snd_ctl_hw_elem_write static int snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control) { snd_ctl_hw_t *hw = handle->private_data; if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) ALSA-CORE [control.c] snd_ctl_create创建control core /* * create control core: * called from init.c */ int snd_ctl_create(struct snd_card *card) { static struct snd_device_ops ops = { .dev_free = snd_ctl_dev_free, .dev_register = snd_ctl_dev_register, //会在其他地方被调用,注册control设备 .dev_disconnect = snd_ctl_dev_disconnect, }; int err; if (snd_BUG_ON(!card)) return -ENXIO; if (snd_BUG_ON(card->number number >= SNDRV_CARDS)) return -ENXIO; snd_device_initialize(&card->ctl_dev, card); dev_set_name(&card->ctl_dev, "controlC%d", card->number); err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); if (err ctl_dev); return err; } snd_ctl_dev_register注册control设备 static int snd_ctl_dev_register(struct snd_device *device) { struct snd_card *card = device->device_data; return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops, card, &card->ctl_dev); //传入了snd_ctl_f_ops } 调用snd_register_device并且传入了snd_ctl_f_ops static const struct file_operations snd_ctl_f_ops = { .owner = THIS_MODULE, .read = snd_ctl_read, .open = snd_ctl_open, .release = snd_ctl_release, .llseek = no_llseek, .poll = snd_ctl_poll, .unlocked_ioctl = snd_ctl_ioctl, //指定snd_ctl_ioctl .compat_ioctl = snd_ctl_ioctl_compat, .fasync = snd_ctl_fasync, }; .unlocked_ioctl = snd_ctl_ioctl, static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_ctl_file *ctl; struct snd_card *card; struct snd_kctl_ioctl *p; void __user *argp = (void __user *)arg; int __user *ip = argp; int err; ctl = file->private_data; card = ctl->card; if (snd_BUG_ON(!card)) return -ENXIO; switch (cmd) { //根据传入cmd,调用函数 case SNDRV_CTL_IOCTL_PVERSION: return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0; case SNDRV_CTL_IOCTL_CARD_INFO: return snd_ctl_card_info(card, ctl, cmd, argp); case SNDRV_CTL_IOCTL_ELEM_LIST: return snd_ctl_elem_list(card, argp); case SNDRV_CTL_IOCTL_ELEM_INFO: return snd_ctl_elem_info_user(ctl, argp); case SNDRV_CTL_IOCTL_ELEM_READ: return snd_ctl_elem_read_user(card, argp); case SNDRV_CTL_IOCTL_ELEM_WRITE: //write return snd_ctl_elem_write_user(ctl, argp); …… } 根据传入的cmd,进行操作。 #define SNDRV_CTL_IOCTL_ELEM_WRITE _IOWR('U', 0x13, struct snd_ctl_elem_value) bit31~bit30 2位为 “区别读写” 区,作用是区分是读取命令还是写入命令。 bit29~bit15 14位为 "数据大小" 区,表示 ioctl() 中的 arg 变量传送的内存大小。 bit20~bit08 8位为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。 bit07~bit00 8位为 "区别序号" 区,是区分命令的命令顺序序号。 假定调用snd_ctl_elem_write_user static int snd_ctl_elem_write_user(struct snd_ctl_file *file, struct snd_ctl_elem_value __user *_control) { struct snd_ctl_elem_value *control; struct snd_card *card; int result; control = memdup_user(_control, sizeof(*control)); //从用户空间拷贝数据到内核中 if (IS_ERR(control)) return PTR_ERR(control); card = file->card; result = snd_power_wait(card, SNDRV_CTL_POWER_D0); if (result controls_rwsem); /* controls_rwsem 为 controls list lock */ result = snd_ctl_elem_write(card, file, control); //调用snd_ctl_elem_write //调用该函数释放信号量 up_write(&card->controls_rwsem); if (result id); //snd_ctl_find_id,遍历kcontrol链表找到匹配的kctl if (kctl == NULL) return -ENOENT; index_offset = snd_ctl_get_ioff(kctl, &control->id); vd = &kctl->vd[index_offset]; if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL || (file && vd->owner && vd->owner != file)) { return -EPERM; //判断control的acess是否是write权限 } snd_ctl_build_ioff(&control->id, kctl, index_offset); result = kctl->put(kctl, control); //调用kctl->put 函数 if (result 0) { struct snd_ctl_elem_id id = control->id; snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id); } return 0; }

snd_ctl_find_id,遍历kcontrol链表找到匹配的kctl

result = kctl->put(kctl, control); 驱动层 以rk3308_codec为例,创建controls 首先要定义struct snd_kcontrol_new 。 调用include/sound/soc.h中的宏SOC_SINGLE_RANGE_TLV static const struct snd_kcontrol_new rk3308_codec_dapm_controls[] = { …… SOC_SINGLE_RANGE_TLV("ADC ALC Group 0 Left Volume", RK3308_ADC_ANA_CON03(0), RK3308_ADC_CH1_ALC_GAIN_SFT, RK3308_ADC_CH1_ALC_GAIN_MIN, RK3308_ADC_CH1_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), …… }

​ 以ADC ALC Group 0 Left Volume为例,依次传入name,regshift,min,max,invert, tlv字段 为该control提供元数据。

这里又调用了个DECLARE_TLV_DB_SCALE宏来设置。 static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_alc_gain_tlv, -1800, 150, 2850); **DECLARE_TLV_DB_SCALE**宏定义的mixer control,它所代表的值按一个固定的dB值的步长变化。该宏的第一个参数是要定义变量的名字,第二个参数是最小值,以0.01dB为单位。第三个参数是变化的步长,也是以0.01dB为单位。如果该control处于最小值时会做出mute时,需要把第四个参数设为1。

​ 所谓tlv,就是Type-Length-Value的意思,数组的第0个元素代表数据的类型,第1个元素代表数据的长度,第三个元素和之后的元素保存该变量的数据。

SOC_SINGLE_RANGE_TLV宏 #define SOC_SINGLE_RANGE_TLV(xname, xreg, xshift, xmin, xmax, xinvert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ SNDRV_CTL_ELEM_ACCESS_READWRITE,\ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw_range, \ .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \ .private_value = (unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .rreg = xreg, .shift = xshift, \ .rshift = xshift, .min = xmin, .max = xmax, \ .platform_max = xmax, .invert = xinvert} }

​ access:字段是访问控制权限。SNDRV_CTL_ELEM_ACCESS_READ意味着只读,这时put()函数不必实现;SNDRV_CTL_ELEM_ACCESS_WRITE意味着只写,这时get()函数不必实现。若control值频繁变化,则需定义VOLATILE标志。当control处于非激活状态时,应设置INACTIVE标志。

​ info回调函数用于 获取control的详细信息。它的主要工作就是填充通过参数传入的 snd_ctl_elem_info对象

​ get回调函数用于 读取control的当前值,并返回给 用户空间的 应用程序。

​ put回调函数用于 把应用程序的控制值 设置到control中。

​ get和put函数,有的control项可以改用SOC_SINGLE_EXT_TLV宏,在codec代码中自定义

​ snd_kcontrol_new结构体并没有numid这个成员,是因为numid是系统自动管理的,原则上是该control的注册次序,保存到snd_ctl_elem_value结构体中。

​ 可以通过private_value给info()、get()和put()函数传递参数。

添加contols

​ 有的用snd_soc_add_component_controls添加。有的直接在snd_soc_component_driver中添加。

snd_soc_component_driver static struct snd_soc_component_driver soc_codec_dev_rk3308 = { .probe = rk3308_probe, .remove = rk3308_remove, .suspend = rk3308_suspend, .resume = rk3308_resume, .set_bias_level = rk3308_set_bias_level, .controls = rk3308_codec_dapm_controls, .num_controls = ARRAY_SIZE(rk3308_codec_dapm_controls), }; snd_soc_add_component_controls [soc/soc-core.c]

snd_soc_add_component_controls

└── snd_soc_add_controls中snd_ctl_add(card, snd_soc_cnew(control, data,control->name, prefix));

​ └── snd_soc_cnew→snd_ctl_new1→snd_ctl_new

​ └── snd_ctl_add→__snd_ctl_add

驱动中自定义的put函数 static int rk3308_codec_mic_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(codec); unsigned int gain = ucontrol->value.integer.value[0]; …… return snd_soc_put_volsw_range(kcontrol, ucontrol); } snd_soc_put_volsw_range [soc/soc-ops.c] /** * snd_soc_put_volsw_range - single mixer put value callback with range. * @kcontrol: mixer control * @ucontrol: control element information * Callback to set the value, within a range, for a single mixer control. * Returns 0 for success. */ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int rreg = mc->rreg; unsigned int shift = mc->shift; int min = mc->min; int max = mc->max; unsigned int mask = (1 value.integer.value[0]) & mask; else val = ((ucontrol->value.integer.value[0] + min) & mask); val_mask = mask


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3