/* * sound\sunxi-rpaf\component\component-core.c * (C) Copyright 2019-2025 * Allwinner Technology Co., Ltd. * yumingfeng * * some simple description for this code * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define RPAF_MEM_DEBUG_LOG static unsigned int component_id[RPAF_COMPONENT_MAX_NUM] = {0}; static DEFINE_MUTEX(comp_id_mutex); static DEFINE_SPINLOCK(comp_stream_lock); static LIST_HEAD(snd_soc_rpaf_info_list); static LIST_HEAD(snd_soc_dsp_component_list); static DEFINE_MUTEX(rpaf_sub_list_mutex); static DEFINE_MUTEX(rpaf_info_list_mutex); void snd_soc_rpaf_pcm_stream_component_lock(void) { spin_lock(&comp_stream_lock); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_pcm_stream_component_lock); void snd_soc_rpaf_pcm_stream_component_unlock(void) { spin_unlock(&comp_stream_lock); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_pcm_stream_component_unlock); void snd_soc_rpaf_pcm_stream_lock(struct snd_dsp_component *dsp_component) { spin_lock(&dsp_component->lock); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_pcm_stream_lock); void snd_soc_rpaf_pcm_stream_lock_irq(struct snd_dsp_component *dsp_component) { local_irq_disable(); snd_soc_rpaf_pcm_stream_lock(dsp_component); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_pcm_stream_lock_irq); unsigned long snd_soc_rpaf_pcm_stream_lock_irqsave(struct snd_dsp_component *dsp_component) { unsigned long flags = 0; local_irq_save(flags); snd_soc_rpaf_pcm_stream_lock(dsp_component); return flags; } EXPORT_SYMBOL_GPL(snd_soc_rpaf_pcm_stream_lock_irqsave); void snd_soc_rpaf_pcm_stream_unlock(struct snd_dsp_component *dsp_component) { spin_unlock(&dsp_component->lock); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_pcm_stream_unlock); void snd_soc_rpaf_pcm_stream_unlock_irq(struct snd_dsp_component *dsp_component) { snd_soc_rpaf_pcm_stream_unlock(dsp_component); local_irq_enable(); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_pcm_stream_unlock_irq); void snd_soc_rpaf_pcm_stream_unlock_irqrestore(struct snd_dsp_component *dsp_component, unsigned long flags) { snd_soc_rpaf_pcm_stream_unlock(dsp_component); local_irq_restore(flags); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_pcm_stream_unlock_irqrestore); /* for snd_soc_dsp_component list operation api */ void snd_soc_dsp_component_list_add_tail(struct snd_soc_dsp_component *component) { mutex_lock(&rpaf_sub_list_mutex); list_add_tail(&component->list, &snd_soc_dsp_component_list); mutex_unlock(&rpaf_sub_list_mutex); } EXPORT_SYMBOL_GPL(snd_soc_dsp_component_list_add_tail); void snd_soc_dsp_component_list_del(struct snd_soc_dsp_component *component) { mutex_lock(&rpaf_sub_list_mutex); list_del(&component->list); mutex_unlock(&rpaf_sub_list_mutex); } EXPORT_SYMBOL_GPL(snd_soc_dsp_component_list_del); struct snd_soc_dsp_component *snd_soc_dsp_component_get_from_list_by_pcmdev( int card, int device, int stream) { struct snd_soc_dsp_component *component = NULL; struct snd_soc_dsp_pcm_params *pcm_params = NULL; mutex_lock(&rpaf_sub_list_mutex); list_for_each_entry(component, &snd_soc_dsp_component_list, list) { pcm_params = &(component->params); if (pcm_params->card == card && pcm_params->device == device && pcm_params->stream == stream) { mutex_unlock(&rpaf_sub_list_mutex); return component; } } mutex_unlock(&rpaf_sub_list_mutex); return NULL; } EXPORT_SYMBOL_GPL(snd_soc_dsp_component_get_from_list_by_pcmdev); /* for snd_soc_rpaf_info list operation api */ void snd_soc_rpaf_info_list_add_tail(struct snd_soc_rpaf_info *rpaf_info) { mutex_lock(&rpaf_info_list_mutex); list_add_tail(&rpaf_info->list, &snd_soc_rpaf_info_list); mutex_unlock(&rpaf_info_list_mutex); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_info_list_add_tail); void snd_soc_rpaf_info_list_del(struct snd_soc_rpaf_info *rpaf_info) { mutex_lock(&rpaf_info_list_mutex); list_del(&rpaf_info->list); mutex_unlock(&rpaf_info_list_mutex); } EXPORT_SYMBOL_GPL(snd_soc_rpaf_info_list_del); struct snd_soc_rpaf_info *snd_soc_rpaf_info_get_from_list_by_minor(int minor) { struct snd_soc_rpaf_info *rpaf_info; mutex_lock(&rpaf_info_list_mutex); list_for_each_entry(rpaf_info, &snd_soc_rpaf_info_list, list) { if (rpaf_info->misc_dev.minor == minor) { mutex_unlock(&rpaf_info_list_mutex); return rpaf_info; } } mutex_unlock(&rpaf_info_list_mutex); return NULL; } EXPORT_SYMBOL_GPL(snd_soc_rpaf_info_get_from_list_by_minor); struct snd_soc_rpaf_info *snd_soc_rpaf_info_get_from_list_by_dspid(unsigned int id) { struct snd_soc_rpaf_info *rpaf_info; mutex_lock(&rpaf_info_list_mutex); list_for_each_entry(rpaf_info, &snd_soc_rpaf_info_list, list) { if (rpaf_info->dsp_id == id) { mutex_unlock(&rpaf_info_list_mutex); return rpaf_info; } } mutex_unlock(&rpaf_info_list_mutex); return NULL; } EXPORT_SYMBOL_GPL(snd_soc_rpaf_info_get_from_list_by_dspid); struct snd_soc_rpaf_info *snd_soc_rpaf_info_get_from_list_by_miscdevice( struct miscdevice *device) { struct snd_soc_rpaf_info *rpaf_info; mutex_lock(&rpaf_info_list_mutex); list_for_each_entry(rpaf_info, &snd_soc_rpaf_info_list, list) { if (&rpaf_info->misc_dev == device) { mutex_unlock(&rpaf_info_list_mutex); return rpaf_info; } } mutex_unlock(&rpaf_info_list_mutex); return NULL; } EXPORT_SYMBOL_GPL(snd_soc_rpaf_info_get_from_list_by_miscdevice); static int snd_soc_rpaf_misc_open(struct inode *inode, struct file *file) { struct snd_soc_rpaf_info *rpaf_info = NULL; struct snd_dsp_component *dsp_component = NULL; struct msg_component_package *msg_component = NULL; int minor = iminor(inode); int err = -ENODEV; err = nonseekable_open(inode, file); if (err < 0) return err; rpaf_info = snd_soc_rpaf_info_get_from_list_by_minor(minor); if (IS_ERR_OR_NULL(rpaf_info)) return -EBADF; dsp_component = kzalloc(sizeof(struct snd_dsp_component), GFP_KERNEL); if (IS_ERR_OR_NULL(dsp_component)) { dev_err(rpaf_info->dev, "%s cannot malloc for dsp_component.\n", __func__); return -ENOMEM; } msg_component = &dsp_component->msg_component; init_waitqueue_head(&msg_component->tsleep); spin_lock_init(&msg_component->lock); snd_soc_dsp_component_list_add_tail(&msg_component->soc_component); dsp_component->rpaf_info = rpaf_info; file->private_data = dsp_component; dsp_component->state = SND_DSP_COMPONENT_STATE_OPEN; mutex_init(&dsp_component->comp_rw_lock); spin_lock_init(&dsp_component->lock); #ifdef RPAF_MEM_DEBUG_LOG dev_info(rpaf_info->dev, "%s line:%d dsp_component:%p, pa:0x%llx, __pa.component:0x%llx\n", __func__, __LINE__, dsp_component, __pa(dsp_component), __pa(&msg_component->soc_component)); #endif return err; } static int snd_soc_rpaf_misc_common_stop(struct snd_dsp_component *dsp_component); static int snd_soc_rpaf_misc_common_remove(struct snd_dsp_component *dsp_component); static int snd_soc_rpaf_misc_release(struct inode *inode, struct file *file) { struct snd_dsp_component *dsp_component = file->private_data; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; int i = 0; int result; /* 判断当前状态 */ switch (dsp_component->state) { case SND_DSP_COMPONENT_STATE_START: case SND_DSP_COMPONENT_STATE_RUNNING: result = snd_soc_rpaf_misc_common_stop(dsp_component); if (result < 0) return result; case SND_DSP_COMPONENT_STATE_STOP: result = snd_soc_rpaf_misc_common_remove(dsp_component); if (result < 0) return result; break; case SND_DSP_COMPONENT_STATE_CLOSE: default: return -EINVAL; case SND_DSP_COMPONENT_STATE_OPEN: case SND_DSP_COMPONENT_STATE_CREATE: case SND_DSP_COMPONENT_STATE_REMOVE: break; } dsp_component->state = SND_DSP_COMPONENT_STATE_CLOSE; /* 其它stream持有需要互斥 */ if (component->read_addr) { dma_free_coherent(rpaf_info->dev, component->read_size, dsp_component->read_area, component->read_addr); #ifdef RPAF_MEM_DEBUG_LOG dev_err(rpaf_info->dev, "read_area:%p, read_addr:0x%x\n", dsp_component->read_area, component->read_addr); #endif component->read_addr = 0; dsp_component->read_area = NULL; } for (i = 0; i < ARRAY_SIZE(component->dump_addr); i++) { if (!component->dump_addr[i]) continue; dma_free_coherent(rpaf_info->dev, component->dump_size, dsp_component->dump_area[i], component->dump_addr[i]); #ifdef RPAF_MEM_DEBUG_LOG dev_err(rpaf_info->dev, "dump_area[%d]:%p, dump_addr:0x%x\n", i, dsp_component->dump_area[i], component->dump_addr[i]); #endif component->dump_addr[i] = 0; dsp_component->dump_area[i] = NULL; } if (component->write_addr) { dma_free_coherent(rpaf_info->dev, component->write_size, dsp_component->write_area, component->write_addr); #ifdef RPAF_MEM_DEBUG_LOG dev_err(rpaf_info->dev, "write_area:%p, write_addr:0x%x\n", dsp_component->write_area, component->write_addr); #endif component->write_addr = 0; dsp_component->write_area = NULL; } snd_soc_dsp_component_list_del(component); mutex_destroy(&dsp_component->comp_rw_lock); kfree(dsp_component); return 0; } #if 0 /* 需要在lib_write接口返回后才可以调用操作,目前只支持一个dump地址 */ static ssize_t snd_soc_rpaf_misc_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { struct snd_dsp_component *dsp_component = file->private_data; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; unsigned int read_size = count; if (count > component->read_size) read_size = component->read_size; /* 加个互斥锁 */ mutex_lock(&dsp_component->comp_rw_lock); __dma_flush_range(dsp_component->read_area, read_size); /* 无需和dsp通信,只要等待lib_write接口返回后即可操作获取对应的dump数据 */ if (copy_to_user(buf, dsp_component->read_area, read_size)) { mutex_unlock(&dsp_component->comp_rw_lock); return -EFAULT; } mutex_unlock(&dsp_component->comp_rw_lock); return read_size; } #endif /* 需要在lib_write接口返回后才可以调用操作,目前只支持一个dump地址 */ ssize_t snd_soc_rpaf_misc_component_lib_read(struct snd_dsp_component *dsp_component, struct snd_rpaf_xferi *xferi) { ssize_t size = xferi->dump_length; ssize_t read_size = size; char __user *buf = (char __user *)(xferi->dump_buf); struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; // struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; // struct snd_soc_dsp_pcm_params *pcm_params = &(component->params); if (xferi->dump_type >= RPAF_COMPONENT_MAX_NUM) return -EINVAL; if (IS_ERR_OR_NULL(dsp_component->dump_area[xferi->dump_type])) return -EFAULT; /* 加个互斥锁 */ mutex_lock(&dsp_component->comp_rw_lock); if (size > component->dump_size) read_size = component->dump_size; if (size > component->dump_length[xferi->dump_type]) read_size = component->dump_length[xferi->dump_type]; __dma_flush_range(dsp_component->dump_area[xferi->dump_type], component->dump_size); /* 无需和dsp通信,只要等待lib_write接口返回后即可操作获取对应的dump数据 */ if (copy_to_user(buf, dsp_component->dump_area[xferi->dump_type], read_size)) { mutex_unlock(&dsp_component->comp_rw_lock); return -EFAULT; } xferi->dump_length = read_size; mutex_unlock(&dsp_component->comp_rw_lock); return read_size; } static int snd_soc_rpaf_pcm_wait_for_avail(struct snd_dsp_component *dsp_component, snd_pcm_uframes_t *availp) { struct snd_soc_rpaf_pcm_runtime *runtime = &dsp_component->runtime; wait_queue_t wait; int err = 0; snd_pcm_uframes_t avail = 0; long wait_time, tout; init_waitqueue_entry(&wait, current); set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&runtime->tsleep, &wait); wait_time = msecs_to_jiffies(5 * 1000); for (;;) { if (signal_pending(current)) { err = -ERESTARTSYS; break; } /* * We need to check if space became available already * (and thus the wakeup happened already) first to close * the race of space already having become available. * This check must happen after been added to the waitqueue * and having current state be INTERRUPTIBLE. */ avail = snd_soc_rpaf_pcm_capture_avail(runtime); if (avail >= runtime->twake) break; snd_soc_rpaf_pcm_stream_unlock_irq(dsp_component); tout = schedule_timeout(wait_time); snd_soc_rpaf_pcm_stream_lock_irq(dsp_component); set_current_state(TASK_INTERRUPTIBLE); if (!tout) { pr_alert("[%s] line:%d cannot get avail data.\n", __func__, __LINE__); err = -EIO; break; } } set_current_state(TASK_RUNNING); remove_wait_queue(&runtime->tsleep, &wait); *availp = avail; return err; } static int32_t snd_soc_rpaf_pcm_stream_lib_read_transfer(struct snd_dsp_component *dsp_component, unsigned int hwoff, unsigned long data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_soc_rpaf_pcm_runtime *runtime = &dsp_component->runtime; char __user *buf = (char __user *) data + snd_soc_rpaf_pcm_frames_to_bytes(runtime, off); char *hwbuf = dsp_component->read_area + snd_soc_rpaf_pcm_frames_to_bytes(runtime, hwoff); ssize_t sizes = snd_soc_rpaf_pcm_frames_to_bytes(runtime, frames); snd_soc_rpaf_pcm_stream_component_lock(); if (!dsp_component->private_data) { awrpaf_err("pcm stream had been closed.\n"); snd_soc_rpaf_pcm_stream_component_unlock(); return -EFAULT; } if (copy_to_user(buf, hwbuf, sizes)) { snd_soc_rpaf_pcm_stream_component_unlock(); return -EFAULT; } snd_soc_rpaf_pcm_stream_component_unlock(); return 0; } static int snd_soc_rpaf_pcm_update_hw_ptr(struct snd_dsp_component *dsp_component, unsigned int in_interrupt) { struct snd_soc_rpaf_pcm_runtime *runtime = &dsp_component->runtime; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *soc_component = &msg_component->soc_component; struct snd_soc_dsp_pcm_params *pcm_params = &(soc_component->params); snd_pcm_uframes_t avail; snd_pcm_uframes_t pos; snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; snd_pcm_sframes_t delta; old_hw_ptr = runtime->hw_ptr; pos = snd_soc_rpaf_pcm_pointer(runtime); if (pos >= runtime->buffer_size) pos = 0; hw_base = runtime->hw_ptr_base; new_hw_ptr = hw_base + pos; /* * new_hw_ptr might be lower than old_hw_ptr in case * when pointer cross the end of the ring buffer */ if (new_hw_ptr < old_hw_ptr) { hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) { hw_base = 0; } new_hw_ptr = hw_base + pos; } delta = new_hw_ptr - old_hw_ptr; /* it means cross the end of boundary */ if (delta < 0) delta += runtime->boundary; /* something must be really wrong */ if (delta >= runtime->buffer_size + runtime->period_size) { pr_err("[%s] Unexpected hw_ptr, stream:%d, pos:%lu, new_hw_ptr:%lu, old_hw_ptr:%lu\n", (in_interrupt) ? "Q" : "P", pcm_params->stream, pos, new_hw_ptr, old_hw_ptr); return 0; } if (delta > runtime->period_size + runtime->period_size / 2) { pr_debug("[%s] Lost interrupts? stream:%d, pos:%lu, new_hw_ptr:%lu, old_hw_ptr:%lu\n", (in_interrupt) ? "Q" : "P", pcm_params->stream, pos, new_hw_ptr, old_hw_ptr); } if (runtime->hw_ptr == new_hw_ptr) return 0; runtime->hw_ptr_base = hw_base; runtime->hw_ptr = new_hw_ptr; /* update avail */ avail = snd_soc_rpaf_pcm_capture_avail(runtime); if (avail > runtime->avail_max) runtime->avail_max = avail; if (avail >= runtime->buffer_size) { return -EPIPE; } if (runtime->twake) { if (avail >= runtime->twake) wake_up(&runtime->tsleep); } return 0; } void snd_soc_rpaf_pcm_stream_update_complete(void *arg) { struct snd_dsp_component *dsp_component = arg; struct snd_soc_rpaf_pcm_runtime *runtime = &dsp_component->runtime; unsigned long flags; flags = snd_soc_rpaf_pcm_stream_lock_irqsave(dsp_component); runtime->pos += snd_soc_rpaf_pcm_frames_to_bytes(runtime, runtime->period_size); if (runtime->pos >= snd_soc_rpaf_pcm_frames_to_bytes(runtime, runtime->buffer_size)) runtime->pos = 0; if (snd_soc_rpaf_pcm_update_hw_ptr(dsp_component, 1) < 0) { goto _end; } _end: snd_soc_rpaf_pcm_stream_unlock_irqrestore(dsp_component, flags); } EXPORT_SYMBOL(snd_soc_rpaf_pcm_stream_update_complete); static int snd_soc_rpaf_misc_common_start(struct snd_dsp_component *dsp_component); ssize_t snd_soc_rpaf_misc_stream_lib_read(struct snd_dsp_component *dsp_component, struct snd_rpaf_xferi *xferi, transfer_f transfer) { char __user *buf = (char __user *)(xferi->dump_buf); struct snd_soc_rpaf_pcm_runtime *runtime = &dsp_component->runtime; ssize_t size = xferi->dump_length; snd_pcm_sframes_t xfer = 0; snd_pcm_uframes_t offset = 0; snd_pcm_uframes_t avail = 0; ssize_t err = 0; if (xferi->dump_type >= RPAF_COMPONENT_MAX_NUM) return -EINVAL; if (IS_ERR_OR_NULL(dsp_component->read_area)) return -EFAULT; if (dsp_component->state != SND_DSP_COMPONENT_STATE_RUNNING) { err = snd_soc_rpaf_misc_common_start(dsp_component); if (err < 0) { awrpaf_err("start failed.\n"); return err; } } snd_soc_rpaf_pcm_stream_lock_irq(dsp_component); if (!dsp_component->private_data) { err = -EFAULT; goto _end_unlock; } runtime->dump_type = xferi->dump_type; runtime->dump_start = 1; snd_soc_rpaf_pcm_update_hw_ptr(dsp_component, 0); avail = snd_soc_rpaf_pcm_capture_avail(runtime); while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t cont; if (!avail) { runtime->twake = 1; err = snd_soc_rpaf_pcm_wait_for_avail(dsp_component, &avail); if (err < 0) goto _end_unlock; if (!avail) continue; } frames = size > avail ? avail : size; cont = runtime->buffer_size - runtime->appl_ptr % runtime->buffer_size; if (frames > cont) frames = cont; if (WARN_ON(!frames)) { runtime->twake = 0; snd_soc_rpaf_pcm_stream_unlock_irq(dsp_component); return -EINVAL; } appl_ptr = runtime->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_soc_rpaf_pcm_stream_unlock_irq(dsp_component); err = transfer(dsp_component, appl_ofs, (unsigned long)buf, offset, frames); snd_soc_rpaf_pcm_stream_lock_irq(dsp_component); if (err < 0) goto _end_unlock; appl_ptr += frames; if (appl_ptr >= runtime->boundary) appl_ptr -= runtime->boundary; runtime->appl_ptr = appl_ptr; offset += frames; size -= frames; xfer += frames; avail -= frames; } _end_unlock: runtime->twake = 0; snd_soc_rpaf_pcm_stream_unlock_irq(dsp_component); return xfer > 0 ? xfer : err; } ssize_t snd_soc_rpaf_misc_lib_read(struct snd_dsp_component *dsp_component, struct snd_rpaf_xferi *xferi) { struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; ssize_t read_size; switch (component->comp_mode) { case SND_DSP_COMPONENT_MODE_INDEPENDENCE: read_size = snd_soc_rpaf_misc_component_lib_read(dsp_component, xferi); dsp_component->state = SND_DSP_COMPONENT_STATE_RUNNING; break; case SND_DSP_COMPONENT_MODE_STREAM: read_size = snd_soc_rpaf_misc_stream_lib_read(dsp_component, xferi, snd_soc_rpaf_pcm_stream_lib_read_transfer); dsp_component->state = SND_DSP_COMPONENT_STATE_RUNNING; break; default: read_size = -EINVAL; break; } return read_size; } static ssize_t snd_soc_rpaf_write_to_user(struct snd_dsp_component *dsp_component, void __user * const data, ssize_t *size) { struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; // struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; ssize_t write_size = *size; char __user * const buf = (char __user * const)data; // __dma_flush_range(component, sizeof(struct snd_soc_dsp_component)); if (*size > component->read_size) write_size = component->read_size; if (*size > component->read_length) write_size = component->read_length; __dma_flush_range(dsp_component->read_area, component->read_size); if (copy_to_user(buf, dsp_component->read_area, write_size)) { *size = 0; return -EFAULT; } *size = write_size; return write_size; } static ssize_t snd_soc_rpaf_misc_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { struct snd_dsp_component *dsp_component = file->private_data; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; ssize_t read_size = count; int ret; if (count > component->read_size) read_size = component->read_size; mutex_lock(&dsp_component->comp_rw_lock); if (copy_from_user(dsp_component->write_area, buf, read_size)) { mutex_unlock(&dsp_component->comp_rw_lock); return 0; } __dma_flush_range(dsp_component->write_area, read_size); component->cmd_val = SND_SOC_DSP_COMPONENT_WRITE; /* 发送给msgbox to dsp */ ret = sunxi_hifi_component_block_send(rpaf_info->dsp_id, msg_component); mutex_unlock(&dsp_component->comp_rw_lock); if (ret < 0) { pr_err("%s line:%d error:%d\n", __func__, __LINE__, ret); return ret; } return read_size; } ssize_t snd_soc_rpaf_misc_lib_write(struct snd_dsp_component *dsp_component, const void __user *data, ssize_t size) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; ssize_t read_size = size; const char __user *buf = (const char __user *)data; int ret; if (IS_ERR_OR_NULL(dsp_component->write_area) || IS_ERR_OR_NULL(dsp_component->read_area)) return -EFAULT; mutex_lock(&dsp_component->comp_rw_lock); if (size > component->write_size) read_size = component->write_size; component->write_length = read_size; if (copy_from_user(dsp_component->write_area, buf, read_size)) { mutex_unlock(&dsp_component->comp_rw_lock); return 0; } __dma_flush_range(dsp_component->write_area, read_size); component->cmd_val = SND_SOC_DSP_COMPONENT_WRITE; /* 发送给msgbox to dsp */ ret = sunxi_hifi_component_block_send(rpaf_info->dsp_id, msg_component); mutex_unlock(&dsp_component->comp_rw_lock); if (ret < 0) { pr_err("%s line:%d error:%d\n", __func__, __LINE__, ret); return ret; } return read_size; } static int snd_soc_rpaf_misc_lib_pcm_params(struct snd_soc_dsp_component *component, struct snd_soc_dsp_pcm_params *params) { struct snd_soc_dsp_pcm_params *pcm_params = &(component->params); pcm_params->channels = params->channels; pcm_params->rate = params->rate; pcm_params->card = params->card; pcm_params->device = params->device; pcm_params->format = params->format; pcm_params->stream = params->stream; pcm_params->resample_rate = params->resample_rate; pcm_params->period_size = params->period_size; pcm_params->periods = params->periods; pcm_params->buffer_size = params->buffer_size; pcm_params->pcm_frames = params->pcm_frames; pcm_params->data_type = params->data_type; /* mp3 - aac */ pcm_params->codec_type = params->codec_type; pcm_params->hw_stream = params->hw_stream; pcm_params->data_mode = params->data_mode; strncpy(pcm_params->driver, params->driver, 31); pcm_params->input_size = params->input_size; pcm_params->output_size = params->output_size; pcm_params->dump_size = params->dump_size; memcpy(pcm_params->algo_params, params->algo_params, sizeof(params->algo_params)); #if 1//def RPAF_MEM_DEBUG_LOG awrpaf_info("==================================\n"); awrpaf_info("channels = %d\n", pcm_params->channels); awrpaf_info("rate = %d\n", pcm_params->rate); awrpaf_info("card = %d\n", pcm_params->card); awrpaf_info("device = %d\n", pcm_params->device); awrpaf_info("format = %d\n", pcm_params->format); awrpaf_info("stream = %d\n", pcm_params->stream); awrpaf_info("resample_rate = %d\n", pcm_params->resample_rate); awrpaf_info("period_size = %u\n", pcm_params->period_size); awrpaf_info("buffer_size = %u\n", pcm_params->buffer_size); awrpaf_info("data_type = %d\n", pcm_params->periods); awrpaf_info("codec_type = %d\n", pcm_params->codec_type); awrpaf_info("hw_stream = %d\n", pcm_params->hw_stream); awrpaf_info("driver = %s\n", pcm_params->driver); awrpaf_info("==================================\n"); #endif return 0; } static void snd_soc_rpaf_runtime_pcm_params(struct snd_soc_rpaf_pcm_runtime *runtime, struct snd_soc_dsp_pcm_params *pcm_params) { int bits = 0; /* -- HW params -- */ switch (pcm_params->format) { case SNDRV_PCM_FORMAT_S16: case SNDRV_PCM_FORMAT_U16: bits = 16; break; case SNDRV_PCM_FORMAT_S24: case SNDRV_PCM_FORMAT_U24: case SNDRV_PCM_FORMAT_S32: case SNDRV_PCM_FORMAT_U32: bits = 32; break; } runtime->frame_bits = pcm_params->channels * bits; /* period size */ runtime->period_size = pcm_params->period_size; /* buffer size */ runtime->buffer_size = pcm_params->buffer_size; runtime->avail_max = runtime->buffer_size; /* -- SW params -- */ /* Silence filling size */ runtime->silence_size = runtime->buffer_size; /* pointers wrap point */ runtime->boundary = runtime->buffer_size; while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) runtime->boundary *= 2; /* transfer sleep */ init_waitqueue_head(&runtime->tsleep); } static int snd_soc_rpaf_misc_set_component_config_from_user( struct snd_dsp_component *dsp_component, struct snd_soc_dsp_component_config __user *_component_config) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; struct snd_soc_rpaf_pcm_runtime *runtime = &dsp_component->runtime; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; struct snd_soc_dsp_pcm_params *params; struct snd_soc_dsp_component_config *component_config; struct msg_component_package *stream_msg_component = NULL; struct snd_dsp_component *stream_dsp_component = NULL; int i = 0; int ret = 0; component_config = memdup_user(_component_config, sizeof(*component_config)); if (IS_ERR(component_config)) return PTR_ERR(component_config); params = &component_config->pcm_params; component->comp_mode = component_config->comp_mode; component->component_type = component_config->component_type; component->transfer_type = component_config->transfer_type; memcpy(component->component_sort, component_config->component_sort, sizeof(component->component_sort)); if (params->pcm_frames == 0) { kfree(component_config); return -EFAULT; } ret = snd_soc_rpaf_misc_lib_pcm_params(component, params); if (ret < 0) goto err_set_misc_lib_pcm_params; snd_soc_rpaf_runtime_pcm_params(&dsp_component->runtime, params); switch (component_config->comp_mode) { case SND_DSP_COMPONENT_MODE_INDEPENDENCE: component->write_size = params->input_size; component->read_size = params->output_size; component->dump_size = params->dump_size; break; case SND_DSP_COMPONENT_MODE_STREAM: component->write_size = 0; component->dump_size = 0; component->read_size = snd_soc_rpaf_pcm_frames_to_bytes(runtime, runtime->buffer_size); break; default: goto err_set_misc_lib_pcm_params; return -EINVAL; } if ((component->write_size) && (!component->write_addr)) { dma_addr_t write_addr; dsp_component->write_area = dma_alloc_coherent(rpaf_info->dev, component->write_size, &write_addr, GFP_KERNEL); if (IS_ERR_OR_NULL(dsp_component->write_area)) { ret = -ENOMEM; goto err_malloc_write_addr; } component->write_addr = write_addr; } if ((component->read_size) && (!component->read_addr)) { dma_addr_t read_addr; dsp_component->read_area = dma_alloc_coherent(rpaf_info->dev, component->read_size, &read_addr, GFP_KERNEL); if (IS_ERR_OR_NULL(dsp_component->read_area)) { ret = -ENOMEM; goto err_malloc_read_addr; } component->read_addr = read_addr; } dsp_component->state = SND_DSP_COMPONENT_STATE_SETUP; if (component_config->comp_mode == SND_DSP_COMPONENT_MODE_STREAM) goto _wait_msg_component_stream; if (!component->dump_size) goto _ignore_dump_area; for (i = 0; i < ARRAY_SIZE(component->dump_addr); i++) { dma_addr_t dump_addr; if (component->dump_addr[i]) continue; if ((component->component_type >> i) & 0x1) { dsp_component->dump_area[i] = dma_alloc_coherent(rpaf_info->dev, component->dump_size, &dump_addr, GFP_KERNEL); if (IS_ERR_OR_NULL(dsp_component->dump_area[i])) { ret = -ENOMEM; goto err_malloc_dump_addr; } component->dump_addr[i] = dump_addr; } } _ignore_dump_area: kfree(component_config); return ret; _wait_msg_component_stream: /* 找到对应的音频流,寻找不超过10秒? */ do { static int i = 1; stream_msg_component = sunxi_hifi_list_msg_component_find_item(msg_component); if (i++ >= 100) { i = 0; awrpaf_err("cannot find running msg_component.\n"); ret = -EFAULT; goto err_find_msg_component; } msleep(100); } while (!stream_msg_component); stream_dsp_component = container_of(stream_msg_component, struct snd_dsp_component, msg_component); /* 把参数给到pcm stream处理那一端 */ snd_soc_rpaf_pcm_stream_component_lock(); stream_dsp_component->private_data = dsp_component; dsp_component->private_data = stream_dsp_component; snd_soc_rpaf_pcm_stream_component_unlock(); kfree(component_config); return ret; err_find_msg_component: err_malloc_dump_addr: dma_free_coherent(rpaf_info->dev, component->read_size, dsp_component->read_area, component->read_addr); component->read_addr = 0; dsp_component->read_area = NULL; for (i = 0; i < ARRAY_SIZE(component->dump_addr); i++) { if (!component->dump_addr[i]) continue; dma_free_coherent(rpaf_info->dev, component->dump_size, dsp_component->dump_area[i], component->dump_addr[i]); component->dump_addr[i] = 0; dsp_component->dump_area[i] = NULL; } err_malloc_read_addr: dma_free_coherent(rpaf_info->dev, component->write_size, dsp_component->write_area, component->write_addr); component->write_addr = 0; dsp_component->write_area = NULL; err_malloc_write_addr: err_set_misc_lib_pcm_params: kfree(component_config); return ret; } static int snd_soc_rpaf_misc_set_pcm_params_to_user(struct snd_dsp_component *dsp_component, struct snd_soc_dsp_pcm_params __user *_params) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; dev_err(rpaf_info->dev, "%s line:%d\n", __func__, __LINE__); return 0; } static int snd_soc_rpaf_misc_common_set_algo(struct snd_dsp_component *dsp_component, void __user *arg) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *soc_component = &msg_component->soc_component; struct snd_soc_dsp_component_config __user *_component_config = arg; struct snd_soc_dsp_component_config component_config; int ret; if (copy_from_user(&component_config, _component_config, sizeof(struct snd_soc_dsp_component_config))) { dev_err(rpaf_info->dev, "cannot copy component_config.\n"); return -EFAULT; } soc_component->comp_mode = component_config.comp_mode; soc_component->component_type = component_config.component_type; strcpy(soc_component->params.driver, component_config.pcm_params.driver); soc_component->transfer_type = component_config.transfer_type; soc_component->sort_index = component_config.sort_index; soc_component->used = component_config.component_used; soc_component->params.stream = component_config.pcm_params.stream; dev_dbg(rpaf_info->dev, "%s:Line:%d, driver:%s\n" "component_config.comp_mode:%d, soc_component->comp_mode:%d\n" "component_config.component_type:%d, soc_component->component_type:%d\n" "component_config.sort_index:%d, soc_componen->sort_index:%d\n" "component_config.pcm_params.stream:%d, soc_component->params.stream:%d\n" "component_config.component_used:%d, soc_component->used:%d.\n", __func__, __LINE__, soc_component->params.driver, component_config.comp_mode, soc_component->comp_mode, component_config.component_type, soc_component->component_type, component_config.sort_index, soc_component->sort_index, component_config.pcm_params.stream, soc_component->params.stream, component_config.component_used, soc_component->used); soc_component->cmd_val = SND_SOC_DSP_COMPONENT_ALGO_SET; /* 发送给msgbox to dsp */ ret = sunxi_hifi_component_block_send(rpaf_info->dsp_id, msg_component); if (ret < 0) { pr_err("%s line:%d error:%d\n", __func__, __LINE__, ret); return ret; } return 0; } static int snd_soc_rpaf_misc_common_get_algo(struct snd_dsp_component *dsp_component, void __user *arg) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *soc_component = &msg_component->soc_component; struct snd_soc_dsp_component_config __user *_component_config = arg; struct snd_soc_dsp_component_config component_config; int ret; if (copy_from_user(&component_config, _component_config, sizeof(struct snd_soc_dsp_component_config))) { dev_err(rpaf_info->dev, "cannot copy component_config.\n"); return -EFAULT; } soc_component->cmd_val = SND_SOC_DSP_COMPONENT_ALGO_GET; soc_component->comp_mode = SND_DSP_COMPONENT_MODE_ALGO; soc_component->component_type = component_config.component_type; strcpy(soc_component->params.driver, component_config.pcm_params.driver); soc_component->transfer_type = component_config.transfer_type; soc_component->sort_index = component_config.sort_index; soc_component->used = component_config.component_used; soc_component->params.stream = component_config.pcm_params.stream; /* 发送给msgbox to dsp */ ret = sunxi_hifi_component_block_send(rpaf_info->dsp_id, msg_component); if (ret < 0) { pr_err("%s line:%d error:%d\n", __func__, __LINE__, ret); return ret; } dev_dbg(rpaf_info->dev, "%s:Line:%d, driver:%s\n" "component_config.comp_mode:%d, soc_component->comp_mode:%d\n" "component_config.component_type:%d, soc_component->component_type:%d\n" "component_config.sort_index:%d, soc_componen->sort_index:%d\n" "component_config.pcm_params.stream:%d, soc_component->params.stream:%d\n" "component_config.component_used:%d, soc_component->used:%d.\n", __func__, __LINE__, soc_component->params.driver, component_config.comp_mode, soc_component->comp_mode, component_config.component_type, soc_component->component_type, component_config.sort_index, soc_component->sort_index, component_config.pcm_params.stream, soc_component->params.stream, component_config.component_used, soc_component->used); component_config.pcm_params.stream = soc_component->params.stream; component_config.comp_mode = soc_component->comp_mode; component_config.component_type = soc_component->component_type; component_config.sort_index = soc_component->sort_index; strcpy(component_config.pcm_params.driver, soc_component->params.driver); if (copy_to_user(_component_config, &component_config, sizeof(struct snd_soc_dsp_component_config))) { dev_err(rpaf_info->dev, "cannot copy component_config.\n"); return -EFAULT; } return 0; } static int snd_soc_rpaf_misc_common_create(struct snd_dsp_component *dsp_component, void __user *arg) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *soc_component = &msg_component->soc_component; int i = 0; int result = 0; /* 判断当前状态 */ switch (dsp_component->state) { case SND_DSP_COMPONENT_STATE_CREATE: case SND_DSP_COMPONENT_STATE_SETUP: case SND_DSP_COMPONENT_STATE_START: case SND_DSP_COMPONENT_STATE_RUNNING: case SND_DSP_COMPONENT_STATE_STOP: case SND_DSP_COMPONENT_STATE_REMOVE: case SND_DSP_COMPONENT_STATE_CLOSE: default: return -EINVAL; case SND_DSP_COMPONENT_STATE_OPEN: break; } /* 检查下是否有设置ID */ mutex_lock(&comp_id_mutex); for (i = 0; i < RPAF_COMPONENT_MAX_NUM; i++) { if (!component_id[i]) { soc_component->id = i; component_id[i] = 1; break; } } mutex_unlock(&comp_id_mutex); if (soc_component->id != i) { awrpaf_err("component id num [%d] set id:%u failed, should be less than %lu!\n", i, soc_component->id, RPAF_COMPONENT_MAX_NUM); return -EINVAL; } /* 状态跳转 */ dsp_component->state = SND_DSP_COMPONENT_STATE_CREATE; /* 状态跳转SETUP */ result = snd_soc_rpaf_misc_set_component_config_from_user(dsp_component, arg); if (result < 0) { awrpaf_err("component[%u] setting pcm_params failed!\n", soc_component->id); return result; } mutex_lock(&comp_id_mutex); component_id[soc_component->id] = 1; mutex_unlock(&comp_id_mutex); if (soc_component->comp_mode == SND_DSP_COMPONENT_MODE_STREAM) { return result; } soc_component->cmd_val = SND_SOC_DSP_COMPONENT_CREATE; /* 发送给msgbox to dsp */ result = sunxi_hifi_component_block_send(rpaf_info->dsp_id, msg_component); if (result < 0) { pr_err("%s line:%d error:%d\n", __func__, __LINE__, result); return result; } return result; } static int snd_soc_rpaf_misc_common_remove(struct snd_dsp_component *dsp_component) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *soc_component = &msg_component->soc_component; struct snd_dsp_component *stream_dsp_component = NULL; int ret; /* 判断当前状态 */ switch (dsp_component->state) { case SND_DSP_COMPONENT_STATE_CREATE: case SND_DSP_COMPONENT_STATE_START: case SND_DSP_COMPONENT_STATE_RUNNING: case SND_DSP_COMPONENT_STATE_REMOVE: case SND_DSP_COMPONENT_STATE_CLOSE: default: return -EINVAL; case SND_DSP_COMPONENT_STATE_OPEN: case SND_DSP_COMPONENT_STATE_SETUP: case SND_DSP_COMPONENT_STATE_STOP: break; } mutex_lock(&comp_id_mutex); component_id[soc_component->id] = 0; mutex_unlock(&comp_id_mutex); if (soc_component->comp_mode == SND_DSP_COMPONENT_MODE_STREAM) { snd_soc_rpaf_pcm_stream_component_lock(); stream_dsp_component = dsp_component->private_data; if (stream_dsp_component && stream_dsp_component->private_data) { stream_dsp_component->private_data = NULL; dsp_component->private_data = NULL; } snd_soc_rpaf_pcm_stream_component_unlock(); dsp_component->state = SND_DSP_COMPONENT_STATE_REMOVE; return 0; } soc_component->cmd_val = SND_SOC_DSP_COMPONENT_REMOVE; /* 发送给msgbox to dsp */ ret = sunxi_hifi_component_block_send(rpaf_info->dsp_id, msg_component); if (ret < 0) { pr_err("%s line:%d error:%d\n", __func__, __LINE__, ret); return ret; } /* 状态跳转 */ dsp_component->state = SND_DSP_COMPONENT_STATE_REMOVE; return 0; } static int snd_soc_rpaf_misc_common_status(struct snd_dsp_component *dsp_component) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; dev_err(rpaf_info->dev, "%s line:%d\n", __func__, __LINE__); /* 发送/不发送给msgbox to dsp */ return 0; } static void snd_soc_rpaf_runtime_prepare(struct snd_soc_rpaf_pcm_runtime *runtime) { runtime->pos = 0; runtime->hw_ptr = 0; runtime->appl_ptr = 0; /* Position at buffer restart */ runtime->hw_ptr_base = 0; /* Position at interrupt time */ runtime->hw_ptr_interrupt = 0; /* offset for hw_ptr due to boundary wrap-around */ runtime->hw_ptr_wrap = 0; } static int snd_soc_rpaf_misc_common_start(struct snd_dsp_component *dsp_component) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *soc_component = &msg_component->soc_component; struct snd_soc_rpaf_pcm_runtime *runtime = &dsp_component->runtime; int ret; /* 判断当前状态 */ switch (dsp_component->state) { case SND_DSP_COMPONENT_STATE_OPEN: case SND_DSP_COMPONENT_STATE_CREATE: case SND_DSP_COMPONENT_STATE_START: case SND_DSP_COMPONENT_STATE_RUNNING: case SND_DSP_COMPONENT_STATE_REMOVE: case SND_DSP_COMPONENT_STATE_CLOSE: default: return -EINVAL; case SND_DSP_COMPONENT_STATE_SETUP: case SND_DSP_COMPONENT_STATE_STOP: break; } if (soc_component->comp_mode == SND_DSP_COMPONENT_MODE_STREAM) { snd_soc_rpaf_pcm_stream_lock_irq(dsp_component); /* 复位指针位置 */ snd_soc_rpaf_runtime_prepare(runtime); snd_soc_rpaf_pcm_stream_unlock_irq(dsp_component); } soc_component->cmd_val = SND_SOC_DSP_COMPONENT_START; /* 发送给msgbox to dsp */ ret = sunxi_hifi_component_block_send(rpaf_info->dsp_id, msg_component); if (ret < 0) { pr_err("%s line:%d error:%d\n", __func__, __LINE__, ret); return ret; } /* 状态跳转 */ dsp_component->state = SND_DSP_COMPONENT_STATE_START; return 0; } static int snd_soc_rpaf_misc_common_stop(struct snd_dsp_component *dsp_component) { struct snd_soc_rpaf_info *rpaf_info = dsp_component->rpaf_info; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *soc_component = &msg_component->soc_component; struct snd_soc_rpaf_pcm_runtime *runtime = &dsp_component->runtime; int ret; /* 判断当前状态 */ switch (dsp_component->state) { case SND_DSP_COMPONENT_STATE_OPEN: case SND_DSP_COMPONENT_STATE_CREATE: case SND_DSP_COMPONENT_STATE_SETUP: case SND_DSP_COMPONENT_STATE_STOP: case SND_DSP_COMPONENT_STATE_REMOVE: case SND_DSP_COMPONENT_STATE_CLOSE: default: return -EINVAL; case SND_DSP_COMPONENT_STATE_START: case SND_DSP_COMPONENT_STATE_RUNNING: break; } runtime->dump_start = 0; soc_component->cmd_val = SND_SOC_DSP_COMPONENT_STOP; /* 发送给msgbox to dsp */ ret = sunxi_hifi_component_block_send(rpaf_info->dsp_id, msg_component); if (ret < 0) { pr_err("%s line:%d error:%d\n", __func__, __LINE__, ret); return ret; } /* 状态跳转 */ dsp_component->state = SND_DSP_COMPONENT_STATE_STOP; return 0; } static long snd_soc_rpaf_misc_common_ioctl(struct file *file, struct snd_dsp_component *dsp_component, unsigned int cmd, void __user *arg) { int result; struct snd_soc_rpaf_info *rpaf_info; if (IS_ERR_OR_NULL(dsp_component)) return -ENXIO; rpaf_info = dsp_component->rpaf_info; switch (cmd) { case SND_SOC_DSP_COMPONENT_IOCTL_ALGO_SET: result = snd_soc_rpaf_misc_common_set_algo(dsp_component, arg); break; case SND_SOC_DSP_COMPONENT_IOCTL_ALGO_GET: result = snd_soc_rpaf_misc_common_get_algo(dsp_component, arg); break; case SND_SOC_DSP_COMPONENT_IOCTL_CREATE: result = snd_soc_rpaf_misc_common_create(dsp_component, arg); break; case SND_SOC_DSP_COMPONENT_IOCTL_REMOVE: result = snd_soc_rpaf_misc_common_remove(dsp_component); put_user(result, (int __user *)arg); break; case SND_SOC_DSP_COMPONENT_IOCTL_STATUS: result = snd_soc_rpaf_misc_common_status(dsp_component); put_user(result, (int __user *)arg); break; case SND_SOC_DSP_COMPONENT_IOCTL_SW_PARAMS: result = snd_soc_rpaf_misc_set_pcm_params_to_user(dsp_component, arg); break; case SND_SOC_DSP_COMPONENT_IOCTL_START: result = snd_soc_rpaf_misc_common_start(dsp_component); put_user(result, (int __user *)arg); break; case SND_SOC_DSP_COMPONENT_IOCTL_STOP: result = snd_soc_rpaf_misc_common_stop(dsp_component); put_user(result, (int __user *)arg); break; case SND_SOC_DSP_COMPONENT_IOCTL_READ32: { struct snd_rpaf_xferi xferi; struct snd_rpaf_xferi32 __user *_xferi32 = arg; int result; compat_caddr_t buf; get_user(xferi.result, &_xferi32->result); get_user(buf, &_xferi32->input_buf); xferi.input_buf = compat_ptr(buf); get_user(xferi.input_length, &_xferi32->input_length); get_user(buf, &_xferi32->output_buf); xferi.output_buf = compat_ptr(buf); get_user(xferi.output_length, &_xferi32->output_length); get_user(buf, &_xferi32->dump_buf); xferi.dump_buf = compat_ptr(buf); get_user(xferi.dump_length, &_xferi32->dump_length); get_user(xferi.dump_type, &_xferi32->dump_type); result = snd_soc_rpaf_misc_lib_read(dsp_component, &xferi); __put_user(result, &_xferi32->result); return result < 0 ? result : 0; } case SND_SOC_DSP_COMPONENT_IOCTL_READ: { struct snd_rpaf_xferi xferi; struct snd_rpaf_xferi __user *_xferi = arg; if (put_user(0, &_xferi->result)) return -EFAULT; if (copy_from_user(&xferi, _xferi, sizeof(struct snd_rpaf_xferi))) return -EFAULT; result = snd_soc_rpaf_misc_lib_read(dsp_component, &xferi); if (copy_to_user(_xferi, &xferi, sizeof(*_xferi))) return -EFAULT; __put_user(result, &_xferi->result); return result < 0 ? result : 0; } case SND_SOC_DSP_COMPONENT_IOCTL_WRITE: { struct snd_rpaf_xferi xferi; struct snd_rpaf_xferi __user *_xferi = arg; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; int result; if (put_user(0, &_xferi->result)) return -EFAULT; if (copy_from_user(&xferi, _xferi, sizeof(*_xferi))) return -EFAULT; dsp_component->state = SND_DSP_COMPONENT_STATE_RUNNING; component->write_length = xferi.input_length; component->read_length = xferi.output_length; result = snd_soc_rpaf_misc_lib_write(dsp_component, xferi.input_buf, xferi.input_length); if (result < 0) { __put_user(result, &_xferi->result); return result < 0 ? result : 0; } /* 回写回来 */ result = snd_soc_rpaf_write_to_user(dsp_component, xferi.output_buf, &xferi.output_length); if (copy_to_user(_xferi, &xferi, sizeof(*_xferi))) return -EFAULT; __put_user(result, &_xferi->result); return result < 0 ? result : 0; } case SND_SOC_DSP_COMPONENT_IOCTL_WRITE32: { struct snd_rpaf_xferi xferi; struct snd_rpaf_xferi32 __user *_xferi32 = arg; struct msg_component_package *msg_component = &dsp_component->msg_component; struct snd_soc_dsp_component *component = &msg_component->soc_component; int result; compat_caddr_t buf; get_user(xferi.result, &_xferi32->result); get_user(buf, &_xferi32->input_buf); xferi.input_buf = compat_ptr(buf); get_user(xferi.input_length, &_xferi32->input_length); get_user(buf, &_xferi32->output_buf); xferi.output_buf = compat_ptr(buf); get_user(xferi.output_length, &_xferi32->output_length); get_user(buf, &_xferi32->dump_buf); xferi.dump_buf = compat_ptr(buf); get_user(xferi.dump_length, &_xferi32->dump_length); get_user(xferi.dump_type, &_xferi32->dump_type); dsp_component->state = SND_DSP_COMPONENT_STATE_RUNNING; component->write_length = xferi.input_length; component->read_length = xferi.output_length; result = snd_soc_rpaf_misc_lib_write(dsp_component, xferi.input_buf, xferi.input_length); if (result < 0) { __put_user(result, &_xferi32->result); return result < 0 ? result : 0; } /* 回写回来 */ result = snd_soc_rpaf_write_to_user(dsp_component, xferi.output_buf, &xferi.output_length); __put_user(result, &_xferi32->result); __put_user(xferi.output_length, &_xferi32->output_length); return result < 0 ? result : 0; } default: dev_err(rpaf_info->dev, "%s cmd:%d not exist.\n", __func__, cmd); return -EINVAL; } return result; } static long snd_soc_rpaf_misc_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_dsp_component *dsp_component = file->private_data; void __user *argp = compat_ptr(arg); if (((cmd >> 8) & 0xFF) != 'C') return -ENOTTY; return snd_soc_rpaf_misc_common_ioctl(file, dsp_component, cmd, argp); } static long snd_soc_rpaf_misc_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_dsp_component *dsp_component = file->private_data; if (((cmd >> 8) & 0xFF) != 'C') return -ENOTTY; return snd_soc_rpaf_misc_common_ioctl(file, dsp_component, cmd, (void __user *)arg); } static const struct file_operations snd_soc_rpaf_misc_f_ops = { .owner = THIS_MODULE, .open = snd_soc_rpaf_misc_open, .unlocked_ioctl = snd_soc_rpaf_misc_unlocked_ioctl, .compat_ioctl = snd_soc_rpaf_misc_compat_ioctl, // .read = snd_soc_rpaf_misc_read, .write = snd_soc_rpaf_misc_write, .llseek = no_llseek, .release = snd_soc_rpaf_misc_release, }; static struct miscdevice snd_soc_rpaf_misc_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "sunxi-rpaf-dsp", .fops = &snd_soc_rpaf_misc_f_ops, }; int snd_soc_rpaf_misc_register_device(struct device *dev, unsigned int dsp_id) { char *str_name = NULL; struct snd_soc_rpaf_info *rpaf_info = NULL; int ret = 0; if (IS_ERR_OR_NULL(dev)) { pr_alert("[%s] dev id:%u is null.\n", __func__, dsp_id); return -EFAULT; } if (snd_soc_rpaf_info_get_from_list_by_dspid(dsp_id) != NULL) { dev_err(dev, "the dsp_id had been register!\n"); return -EINVAL; } rpaf_info = devm_kzalloc(dev, sizeof(struct snd_soc_rpaf_info), GFP_KERNEL); if (IS_ERR_OR_NULL(rpaf_info)) { dev_err(dev, "kzalloc for snd_soc_rpaf_info failed.\n"); return -ENOMEM; } rpaf_info->dev = dev; rpaf_info->dsp_id = dsp_id; memset(rpaf_info->name, 0, sizeof(rpaf_info->name)); snprintf(rpaf_info->name, sizeof(rpaf_info->name), "rpaf_misc%d", rpaf_info->dsp_id); snd_soc_rpaf_info_set_drvdata(rpaf_info, dev_get_drvdata(rpaf_info->dev)); memcpy(&rpaf_info->misc_dev, &snd_soc_rpaf_misc_dev, sizeof(struct miscdevice)); str_name = devm_kzalloc(dev, SUNXI_RPAF_INFO_NAME_LEN, GFP_KERNEL); if (IS_ERR_OR_NULL(str_name)) { dev_err(dev, "failed to kzalloc attr group name!\n"); ret = -ENOMEM; goto err_kzalloc_misc_dev_name; } memset(str_name, 0, SUNXI_RPAF_INFO_NAME_LEN); snprintf(str_name, SUNXI_RPAF_INFO_NAME_LEN, "rpaf-dsp%d", rpaf_info->dsp_id); rpaf_info->misc_dev.name = str_name; ret = misc_register(&rpaf_info->misc_dev); if (ret < 0) { dev_err(dev, "sunxi-rpaf register driver as misc device error!\n"); goto err_misc_register; } snd_soc_rpaf_info_list_add_tail(rpaf_info); dev_info(dev, "register device finished!\n"); return 0; err_misc_register: if (!IS_ERR_OR_NULL(rpaf_info->misc_dev.name)) { str_name = (char *)rpaf_info->misc_dev.name; devm_kfree(dev, str_name); } err_kzalloc_misc_dev_name: devm_kfree(dev, rpaf_info); return ret; } EXPORT_SYMBOL_GPL(snd_soc_rpaf_misc_register_device); int snd_soc_rpaf_misc_deregister_device(struct device *dev, unsigned int dsp_id) { struct snd_soc_rpaf_info *rpaf_info = NULL; int ret = 0; if (IS_ERR_OR_NULL(dev)) { pr_alert("[%s] dev id:%u is null.\n", __func__, dsp_id); return -EFAULT; } rpaf_info = snd_soc_rpaf_info_get_from_list_by_dspid(dsp_id); if (IS_ERR_OR_NULL(rpaf_info)) { dev_err(dev, "the dsp_id had been del from rpaf_info list.!\n"); return -EINVAL; } misc_deregister(&rpaf_info->misc_dev); if (!IS_ERR_OR_NULL(rpaf_info->misc_dev.name)) devm_kfree(dev, (char *)rpaf_info->misc_dev.name); snd_soc_rpaf_info_list_del(rpaf_info); dev_info(dev, "deregister device finished!\n"); devm_kfree(dev, rpaf_info); return ret; } EXPORT_SYMBOL_GPL(snd_soc_rpaf_misc_deregister_device); MODULE_DESCRIPTION("sunxi dsp remote processor audio framework core"); MODULE_AUTHOR("yumingfeng@allwinnertech.com"); MODULE_LICENSE("GPL");