| /* |
| * intel_sst_interface.c - Intel SST Driver for audio engine |
| * |
| * Copyright (C) 2008-10 Intel Corp |
| * Authors: Vinod Koul <vinod.koul@intel.com> |
| * Harsha Priya <priya.harsha@intel.com> |
| * Dharageswari R <dharageswari.r@intel.com> |
| * Jeeja KP <jeeja.kp@intel.com> |
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| * |
| * 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; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
| * |
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| * This driver exposes the audio engine functionalities to the ALSA |
| * and middleware. |
| * Upper layer interfaces (MAD driver, MMF) to SST driver |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #include <linux/pci.h> |
| #include <linux/fs.h> |
| #include <linux/uio.h> |
| #include <linux/aio.h> |
| #include <linux/uaccess.h> |
| #include <linux/firmware.h> |
| #include <linux/pm_runtime.h> |
| #include <linux/ioctl.h> |
| #ifdef CONFIG_MRST_RAR_HANDLER |
| #include <linux/rar_register.h> |
| #include "../../../drivers/staging/memrar/memrar.h" |
| #endif |
| #include "intel_sst.h" |
| #include "intel_sst_ioctl.h" |
| #include "intel_sst_fw_ipc.h" |
| #include "intel_sst_common.h" |
| |
| #define AM_MODULE 1 |
| #define STREAM_MODULE 0 |
| |
| |
| /** |
| * intel_sst_check_device - checks SST device |
| * |
| * This utility function checks the state of SST device and downlaods FW if |
| * not done, or resumes the device if suspended |
| */ |
| |
| static int intel_sst_check_device(void) |
| { |
| int retval = 0; |
| if (sst_drv_ctx->pmic_state != SND_MAD_INIT_DONE) { |
| pr_warn("Sound card not available\n"); |
| return -EIO; |
| } |
| if (sst_drv_ctx->sst_state == SST_SUSPENDED) { |
| pr_debug("Resuming from Suspended state\n"); |
| retval = intel_sst_resume(sst_drv_ctx->pci); |
| if (retval) { |
| pr_debug("Resume Failed= %#x,abort\n", retval); |
| return retval; |
| } |
| } |
| |
| if (sst_drv_ctx->sst_state == SST_UN_INIT) { |
| /* FW is not downloaded */ |
| retval = sst_download_fw(); |
| if (retval) |
| return -ENODEV; |
| if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { |
| retval = sst_drv_ctx->rx_time_slot_status; |
| if (retval != RX_TIMESLOT_UNINIT |
| && sst_drv_ctx->pmic_vendor != SND_NC) |
| sst_enable_rx_timeslot(retval); |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * intel_sst_open - opens a handle to driver |
| * |
| * @i_node: inode structure |
| * @file_ptr:pointer to file |
| * |
| * This function is called by OS when a user space component |
| * tries to get a driver handle. Only one handle at a time |
| * will be allowed |
| */ |
| int intel_sst_open(struct inode *i_node, struct file *file_ptr) |
| { |
| unsigned int retval; |
| |
| mutex_lock(&sst_drv_ctx->stream_lock); |
| pm_runtime_get_sync(&sst_drv_ctx->pci->dev); |
| retval = intel_sst_check_device(); |
| if (retval) { |
| pm_runtime_put(&sst_drv_ctx->pci->dev); |
| mutex_unlock(&sst_drv_ctx->stream_lock); |
| return retval; |
| } |
| |
| if (sst_drv_ctx->encoded_cnt < MAX_ENC_STREAM) { |
| struct ioctl_pvt_data *data = |
| kzalloc(sizeof(struct ioctl_pvt_data), GFP_KERNEL); |
| if (!data) { |
| pm_runtime_put(&sst_drv_ctx->pci->dev); |
| mutex_unlock(&sst_drv_ctx->stream_lock); |
| return -ENOMEM; |
| } |
| |
| sst_drv_ctx->encoded_cnt++; |
| mutex_unlock(&sst_drv_ctx->stream_lock); |
| data->pvt_id = sst_assign_pvt_id(sst_drv_ctx); |
| data->str_id = 0; |
| file_ptr->private_data = (void *)data; |
| pr_debug("pvt_id handle = %d!\n", data->pvt_id); |
| } else { |
| retval = -EUSERS; |
| pm_runtime_put(&sst_drv_ctx->pci->dev); |
| mutex_unlock(&sst_drv_ctx->stream_lock); |
| } |
| return retval; |
| } |
| |
| /** |
| * intel_sst_open_cntrl - opens a handle to driver |
| * |
| * @i_node: inode structure |
| * @file_ptr:pointer to file |
| * |
| * This function is called by OS when a user space component |
| * tries to get a driver handle to /dev/intel_sst_control. |
| * Only one handle at a time will be allowed |
| * This is for control operations only |
| */ |
| int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr) |
| { |
| unsigned int retval; |
| |
| /* audio manager open */ |
| mutex_lock(&sst_drv_ctx->stream_lock); |
| pm_runtime_get_sync(&sst_drv_ctx->pci->dev); |
| retval = intel_sst_check_device(); |
| if (retval) { |
| pm_runtime_put(&sst_drv_ctx->pci->dev); |
| mutex_unlock(&sst_drv_ctx->stream_lock); |
| return retval; |
| } |
| |
| if (sst_drv_ctx->am_cnt < MAX_AM_HANDLES) { |
| sst_drv_ctx->am_cnt++; |
| pr_debug("AM handle opened...\n"); |
| file_ptr->private_data = NULL; |
| } else { |
| retval = -EACCES; |
| pm_runtime_put(&sst_drv_ctx->pci->dev); |
| } |
| |
| mutex_unlock(&sst_drv_ctx->stream_lock); |
| return retval; |
| } |
| |
| /** |
| * intel_sst_release - releases a handle to driver |
| * |
| * @i_node: inode structure |
| * @file_ptr: pointer to file |
| * |
| * This function is called by OS when a user space component |
| * tries to release a driver handle. |
| */ |
| int intel_sst_release(struct inode *i_node, struct file *file_ptr) |
| { |
| struct ioctl_pvt_data *data = file_ptr->private_data; |
| |
| pr_debug("Release called, closing app handle\n"); |
| mutex_lock(&sst_drv_ctx->stream_lock); |
| sst_drv_ctx->encoded_cnt--; |
| sst_drv_ctx->stream_cnt--; |
| pm_runtime_put(&sst_drv_ctx->pci->dev); |
| mutex_unlock(&sst_drv_ctx->stream_lock); |
| free_stream_context(data->str_id); |
| kfree(data); |
| return 0; |
| } |
| |
| int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr) |
| { |
| /* audio manager close */ |
| mutex_lock(&sst_drv_ctx->stream_lock); |
| sst_drv_ctx->am_cnt--; |
| pm_runtime_put(&sst_drv_ctx->pci->dev); |
| mutex_unlock(&sst_drv_ctx->stream_lock); |
| pr_debug("AM handle closed\n"); |
| return 0; |
| } |
| |
| /** |
| * intel_sst_mmap - mmaps a kernel buffer to user space for copying data |
| * |
| * @vma: vm area structure instance |
| * @file_ptr: pointer to file |
| * |
| * This function is called by OS when a user space component |
| * tries to get mmap memory from driver |
| */ |
| int intel_sst_mmap(struct file *file_ptr, struct vm_area_struct *vma) |
| { |
| int retval, length; |
| struct ioctl_pvt_data *data = |
| (struct ioctl_pvt_data *)file_ptr->private_data; |
| int str_id = data->str_id; |
| void *mem_area; |
| |
| retval = sst_validate_strid(str_id); |
| if (retval) |
| return -EINVAL; |
| |
| length = vma->vm_end - vma->vm_start; |
| pr_debug("called for stream %d length 0x%x\n", str_id, length); |
| |
| if (length > sst_drv_ctx->mmap_len) |
| return -ENOMEM; |
| if (!sst_drv_ctx->mmap_mem) |
| return -EIO; |
| |
| /* round it up to the page boundary */ |
| /*mem_area = (void *)((((unsigned long)sst_drv_ctx->mmap_mem) |
| + PAGE_SIZE - 1) & PAGE_MASK);*/ |
| mem_area = (void *) PAGE_ALIGN((unsigned int) sst_drv_ctx->mmap_mem); |
| |
| /* map the whole physically contiguous area in one piece */ |
| retval = remap_pfn_range(vma, |
| vma->vm_start, |
| virt_to_phys((void *)mem_area) >> PAGE_SHIFT, |
| length, |
| vma->vm_page_prot); |
| if (retval) |
| sst_drv_ctx->streams[str_id].mmapped = false; |
| else |
| sst_drv_ctx->streams[str_id].mmapped = true; |
| |
| pr_debug("mmap ret 0x%x\n", retval); |
| return retval; |
| } |
| |
| /* sets mmap data buffers to play/capture*/ |
| static int intel_sst_mmap_play_capture(u32 str_id, |
| struct snd_sst_mmap_buffs *mmap_buf) |
| { |
| struct sst_stream_bufs *bufs; |
| int retval, i; |
| struct stream_info *stream; |
| struct snd_sst_mmap_buff_entry *buf_entry; |
| struct snd_sst_mmap_buff_entry *tmp_buf; |
| |
| pr_debug("called for str_id %d\n", str_id); |
| retval = sst_validate_strid(str_id); |
| if (retval) |
| return -EINVAL; |
| |
| stream = &sst_drv_ctx->streams[str_id]; |
| if (stream->mmapped != true) |
| return -EIO; |
| |
| if (stream->status == STREAM_UN_INIT || |
| stream->status == STREAM_DECODE) { |
| return -EBADRQC; |
| } |
| stream->curr_bytes = 0; |
| stream->cumm_bytes = 0; |
| |
| tmp_buf = kcalloc(mmap_buf->entries, sizeof(*tmp_buf), GFP_KERNEL); |
| if (!tmp_buf) |
| return -ENOMEM; |
| if (copy_from_user(tmp_buf, (void __user *)mmap_buf->buff, |
| mmap_buf->entries * sizeof(*tmp_buf))) { |
| retval = -EFAULT; |
| goto out_free; |
| } |
| |
| pr_debug("new buffers count %d status %d\n", |
| mmap_buf->entries, stream->status); |
| buf_entry = tmp_buf; |
| for (i = 0; i < mmap_buf->entries; i++) { |
| bufs = kzalloc(sizeof(*bufs), GFP_KERNEL); |
| if (!bufs) { |
| retval = -ENOMEM; |
| goto out_free; |
| } |
| bufs->size = buf_entry->size; |
| bufs->offset = buf_entry->offset; |
| bufs->addr = sst_drv_ctx->mmap_mem; |
| bufs->in_use = false; |
| buf_entry++; |
| /* locking here */ |
| mutex_lock(&stream->lock); |
| list_add_tail(&bufs->node, &stream->bufs); |
| mutex_unlock(&stream->lock); |
| } |
| |
| mutex_lock(&stream->lock); |
| stream->data_blk.condition = false; |
| stream->data_blk.ret_code = 0; |
| if (stream->status == STREAM_INIT && |
| stream->prev != STREAM_UN_INIT && |
| stream->need_draining != true) { |
| stream->prev = stream->status; |
| stream->status = STREAM_RUNNING; |
| if (stream->ops == STREAM_OPS_PLAYBACK) { |
| if (sst_play_frame(str_id) < 0) { |
| pr_warn("play frames fail\n"); |
| mutex_unlock(&stream->lock); |
| retval = -EIO; |
| goto out_free; |
| } |
| } else if (stream->ops == STREAM_OPS_CAPTURE) { |
| if (sst_capture_frame(str_id) < 0) { |
| pr_warn("capture frame fail\n"); |
| mutex_unlock(&stream->lock); |
| retval = -EIO; |
| goto out_free; |
| } |
| } |
| } |
| mutex_unlock(&stream->lock); |
| /* Block the call for reply */ |
| if (!list_empty(&stream->bufs)) { |
| stream->data_blk.on = true; |
| retval = sst_wait_interruptible(sst_drv_ctx, |
| &stream->data_blk); |
| } |
| |
| if (retval >= 0) |
| retval = stream->cumm_bytes; |
| pr_debug("end of play/rec ioctl bytes = %d!!\n", retval); |
| |
| out_free: |
| kfree(tmp_buf); |
| return retval; |
| } |
| |
| /*sets user data buffers to play/capture*/ |
| static int intel_sst_play_capture(struct stream_info *stream, int str_id) |
| { |
| int retval; |
| |
| stream->data_blk.ret_code = 0; |
| stream->data_blk.on = true; |
| stream->data_blk.condition = false; |
| |
| mutex_lock(&stream->lock); |
| if (stream->status == STREAM_INIT && stream->prev != STREAM_UN_INIT) { |
| /* stream is started */ |
| stream->prev = stream->status; |
| stream->status = STREAM_RUNNING; |
| } |
| |
| if (stream->status == STREAM_INIT && stream->prev == STREAM_UN_INIT) { |
| /* stream is not started yet */ |
| pr_debug("Stream isn't in started state %d, prev %d\n", |
| stream->status, stream->prev); |
| } else if ((stream->status == STREAM_RUNNING || |
| stream->status == STREAM_PAUSED) && |
| stream->need_draining != true) { |
| /* stream is started */ |
| if (stream->ops == STREAM_OPS_PLAYBACK || |
| stream->ops == STREAM_OPS_PLAYBACK_DRM) { |
| if (sst_play_frame(str_id) < 0) { |
| pr_warn("play frames failed\n"); |
| mutex_unlock(&stream->lock); |
| return -EIO; |
| } |
| } else if (stream->ops == STREAM_OPS_CAPTURE) { |
| if (sst_capture_frame(str_id) < 0) { |
| pr_warn("capture frames failed\n"); |
| mutex_unlock(&stream->lock); |
| return -EIO; |
| } |
| } |
| } else { |
| mutex_unlock(&stream->lock); |
| return -EIO; |
| } |
| mutex_unlock(&stream->lock); |
| /* Block the call for reply */ |
| |
| retval = sst_wait_interruptible(sst_drv_ctx, &stream->data_blk); |
| if (retval) { |
| stream->status = STREAM_INIT; |
| pr_debug("wait returned error...\n"); |
| } |
| return retval; |
| } |
| |
| /* fills kernel list with buffer addresses for SST DSP driver to process*/ |
| static int snd_sst_fill_kernel_list(struct stream_info *stream, |
| const struct iovec *iovec, unsigned long nr_segs, |
| struct list_head *copy_to_list) |
| { |
| struct sst_stream_bufs *stream_bufs; |
| unsigned long index, mmap_len; |
| unsigned char __user *bufp; |
| unsigned long size, copied_size; |
| int retval = 0, add_to_list = 0; |
| static int sent_offset; |
| static unsigned long sent_index; |
| |
| #ifdef CONFIG_MRST_RAR_HANDLER |
| if (stream->ops == STREAM_OPS_PLAYBACK_DRM) { |
| for (index = stream->sg_index; index < nr_segs; index++) { |
| __u32 rar_handle; |
| struct sst_stream_bufs *stream_bufs = |
| kzalloc(sizeof(*stream_bufs), GFP_KERNEL); |
| |
| stream->sg_index = index; |
| if (!stream_bufs) |
| return -ENOMEM; |
| if (copy_from_user((void *) &rar_handle, |
| iovec[index].iov_base, |
| sizeof(__u32))) { |
| kfree(stream_bufs); |
| return -EFAULT; |
| } |
| stream_bufs->addr = (char *)rar_handle; |
| stream_bufs->in_use = false; |
| stream_bufs->size = iovec[0].iov_len; |
| /* locking here */ |
| mutex_lock(&stream->lock); |
| list_add_tail(&stream_bufs->node, &stream->bufs); |
| mutex_unlock(&stream->lock); |
| } |
| stream->sg_index = index; |
| return retval; |
| } |
| #endif |
| stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL); |
| if (!stream_bufs) |
| return -ENOMEM; |
| stream_bufs->addr = sst_drv_ctx->mmap_mem; |
| mmap_len = sst_drv_ctx->mmap_len; |
| stream_bufs->addr = sst_drv_ctx->mmap_mem; |
| bufp = stream->cur_ptr; |
| |
| copied_size = 0; |
| |
| if (!stream->sg_index) |
| sent_index = sent_offset = 0; |
| |
| for (index = stream->sg_index; index < nr_segs; index++) { |
| stream->sg_index = index; |
| if (!stream->cur_ptr) |
| bufp = iovec[index].iov_base; |
| |
| size = ((unsigned long)iovec[index].iov_base |
| + iovec[index].iov_len) - (unsigned long) bufp; |
| |
| if ((copied_size + size) > mmap_len) |
| size = mmap_len - copied_size; |
| |
| |
| if (stream->ops == STREAM_OPS_PLAYBACK) { |
| if (copy_from_user((void *) |
| (stream_bufs->addr + copied_size), |
| bufp, size)) { |
| /* Clean up the list and return error code */ |
| retval = -EFAULT; |
| break; |
| } |
| } else if (stream->ops == STREAM_OPS_CAPTURE) { |
| struct snd_sst_user_cap_list *entry = |
| kzalloc(sizeof(*entry), GFP_KERNEL); |
| |
| if (!entry) { |
| kfree(stream_bufs); |
| return -ENOMEM; |
| } |
| entry->iov_index = index; |
| entry->iov_offset = (unsigned long) bufp - |
| (unsigned long)iovec[index].iov_base; |
| entry->offset = copied_size; |
| entry->size = size; |
| list_add_tail(&entry->node, copy_to_list); |
| } |
| |
| stream->cur_ptr = bufp + size; |
| |
| if (((unsigned long)iovec[index].iov_base |
| + iovec[index].iov_len) < |
| ((unsigned long)iovec[index].iov_base)) { |
| pr_debug("Buffer overflows\n"); |
| kfree(stream_bufs); |
| return -EINVAL; |
| } |
| |
| if (((unsigned long)iovec[index].iov_base |
| + iovec[index].iov_len) == |
| (unsigned long)stream->cur_ptr) { |
| stream->cur_ptr = NULL; |
| stream->sg_index++; |
| } |
| |
| copied_size += size; |
| pr_debug("copied_size - %lx\n", copied_size); |
| if ((copied_size >= mmap_len) || |
| (stream->sg_index == nr_segs)) { |
| add_to_list = 1; |
| } |
| |
| if (add_to_list) { |
| stream_bufs->in_use = false; |
| stream_bufs->size = copied_size; |
| /* locking here */ |
| mutex_lock(&stream->lock); |
| list_add_tail(&stream_bufs->node, &stream->bufs); |
| mutex_unlock(&stream->lock); |
| break; |
| } |
| } |
| return retval; |
| } |
| |
| /* This function copies the captured data returned from SST DSP engine |
| * to the user buffers*/ |
| static int snd_sst_copy_userbuf_capture(struct stream_info *stream, |
| const struct iovec *iovec, |
| struct list_head *copy_to_list) |
| { |
| struct snd_sst_user_cap_list *entry, *_entry; |
| struct sst_stream_bufs *kbufs = NULL, *_kbufs; |
| int retval = 0; |
| |
| /* copy sent buffers */ |
| pr_debug("capture stream copying to user now...\n"); |
| list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) { |
| if (kbufs->in_use == true) { |
| /* copy to user */ |
| list_for_each_entry_safe(entry, _entry, |
| copy_to_list, node) { |
| if (copy_to_user(iovec[entry->iov_index].iov_base + entry->iov_offset, |
| kbufs->addr + entry->offset, |
| entry->size)) { |
| /* Clean up the list and return error */ |
| retval = -EFAULT; |
| break; |
| } |
| list_del(&entry->node); |
| kfree(entry); |
| } |
| } |
| } |
| pr_debug("end of cap copy\n"); |
| return retval; |
| } |
| |
| /* |
| * snd_sst_userbufs_play_cap - constructs the list from user buffers |
| * |
| * @iovec:pointer to iovec structure |
| * @nr_segs:number entries in the iovec structure |
| * @str_id:stream id |
| * @stream:pointer to stream_info structure |
| * |
| * This function will traverse the user list and copy the data to the kernel |
| * space buffers. |
| */ |
| static int snd_sst_userbufs_play_cap(const struct iovec *iovec, |
| unsigned long nr_segs, unsigned int str_id, |
| struct stream_info *stream) |
| { |
| int retval; |
| LIST_HEAD(copy_to_list); |
| |
| |
| retval = snd_sst_fill_kernel_list(stream, iovec, nr_segs, |
| ©_to_list); |
| |
| retval = intel_sst_play_capture(stream, str_id); |
| if (retval < 0) |
| return retval; |
| |
| if (stream->ops == STREAM_OPS_CAPTURE) { |
| retval = snd_sst_copy_userbuf_capture(stream, iovec, |
| ©_to_list); |
| } |
| return retval; |
| } |
| |
| /* This function is common function across read/write |
| for user buffers called from system calls*/ |
| static int intel_sst_read_write(unsigned int str_id, char __user *buf, |
| size_t count) |
| { |
| int retval; |
| struct stream_info *stream; |
| struct iovec iovec; |
| unsigned long nr_segs; |
| |
| retval = sst_validate_strid(str_id); |
| if (retval) |
| return -EINVAL; |
| stream = &sst_drv_ctx->streams[str_id]; |
| if (stream->mmapped == true) { |
| pr_warn("user write and stream is mapped\n"); |
| return -EIO; |
| } |
| if (!count) |
| return -EINVAL; |
| stream->curr_bytes = 0; |
| stream->cumm_bytes = 0; |
| /* copy user buf details */ |
| pr_debug("new buffers %p, copy size %d, status %d\n" , |
| buf, (int) count, (int) stream->status); |
| |
| stream->buf_type = SST_BUF_USER_STATIC; |
| iovec.iov_base = buf; |
| iovec.iov_len = count; |
| nr_segs = 1; |
| |
| do { |
| retval = snd_sst_userbufs_play_cap( |
| &iovec, nr_segs, str_id, stream); |
| if (retval < 0) |
| break; |
| |
| } while (stream->sg_index < nr_segs); |
| |
| stream->sg_index = 0; |
| stream->cur_ptr = NULL; |
| if (retval >= 0) |
| retval = stream->cumm_bytes; |
| pr_debug("end of play/rec bytes = %d!!\n", retval); |
| return retval; |
| } |
| |
| /*** |
| * intel_sst_write - This function is called when user tries to play out data |
| * |
| * @file_ptr:pointer to file |
| * @buf:user buffer to be played out |
| * @count:size of tthe buffer |
| * @offset:offset to start from |
| * |
| * writes the encoded data into DSP |
| */ |
| int intel_sst_write(struct file *file_ptr, const char __user *buf, |
| size_t count, loff_t *offset) |
| { |
| struct ioctl_pvt_data *data = file_ptr->private_data; |
| int str_id = data->str_id; |
| struct stream_info *stream = &sst_drv_ctx->streams[str_id]; |
| |
| pr_debug("called for %d\n", str_id); |
| if (stream->status == STREAM_UN_INIT || |
| stream->status == STREAM_DECODE) { |
| return -EBADRQC; |
| } |
| return intel_sst_read_write(str_id, (char __user *)buf, count); |
| } |
| |
| /* |
| * intel_sst_aio_write - write buffers |
| * |
| * @kiocb:pointer to a structure containing file pointer |
| * @iov:list of user buffer to be played out |
| * @nr_segs:number of entries |
| * @offset:offset to start from |
| * |
| * This function is called when user tries to play out multiple data buffers |
| */ |
| ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov, |
| unsigned long nr_segs, loff_t offset) |
| { |
| int retval; |
| struct ioctl_pvt_data *data = kiocb->ki_filp->private_data; |
| int str_id = data->str_id; |
| struct stream_info *stream; |
| |
| pr_debug("entry - %ld\n", nr_segs); |
| |
| if (is_sync_kiocb(kiocb) == false) |
| return -EINVAL; |
| |
| pr_debug("called for str_id %d\n", str_id); |
| retval = sst_validate_strid(str_id); |
| if (retval) |
| return -EINVAL; |
| stream = &sst_drv_ctx->streams[str_id]; |
| if (stream->mmapped == true) |
| return -EIO; |
| if (stream->status == STREAM_UN_INIT || |
| stream->status == STREAM_DECODE) { |
| return -EBADRQC; |
| } |
| stream->curr_bytes = 0; |
| stream->cumm_bytes = 0; |
| pr_debug("new segs %ld, offset %d, status %d\n" , |
| nr_segs, (int) offset, (int) stream->status); |
| stream->buf_type = SST_BUF_USER_STATIC; |
| do { |
| retval = snd_sst_userbufs_play_cap(iov, nr_segs, |
| str_id, stream); |
| if (retval < 0) |
| break; |
| |
| } while (stream->sg_index < nr_segs); |
| |
| stream->sg_index = 0; |
| stream->cur_ptr = NULL; |
| if (retval >= 0) |
| retval = stream->cumm_bytes; |
| pr_debug("end of play/rec bytes = %d!!\n", retval); |
| return retval; |
| } |
| |
| /* |
| * intel_sst_read - read the encoded data |
| * |
| * @file_ptr: pointer to file |
| * @buf: user buffer to be filled with captured data |
| * @count: size of tthe buffer |
| * @offset: offset to start from |
| * |
| * This function is called when user tries to capture data |
| */ |
| int intel_sst_read(struct file *file_ptr, char __user *buf, |
| size_t count, loff_t *offset) |
| { |
| struct ioctl_pvt_data *data = file_ptr->private_data; |
| int str_id = data->str_id; |
| struct stream_info *stream = &sst_drv_ctx->streams[str_id]; |
| |
| pr_debug("called for %d\n", str_id); |
| if (stream->status == STREAM_UN_INIT || |
| stream->status == STREAM_DECODE) |
| return -EBADRQC; |
| return intel_sst_read_write(str_id, buf, count); |
| } |
| |
| /* |
| * intel_sst_aio_read - aio read |
| * |
| * @kiocb: pointer to a structure containing file pointer |
| * @iov: list of user buffer to be filled with captured |
| * @nr_segs: number of entries |
| * @offset: offset to start from |
| * |
| * This function is called when user tries to capture out multiple data buffers |
| */ |
| ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov, |
| unsigned long nr_segs, loff_t offset) |
| { |
| int retval; |
| struct ioctl_pvt_data *data = kiocb->ki_filp->private_data; |
| int str_id = data->str_id; |
| struct stream_info *stream; |
| |
| pr_debug("entry - %ld\n", nr_segs); |
| |
| if (is_sync_kiocb(kiocb) == false) { |
| pr_debug("aio_read from user space is not allowed\n"); |
| return -EINVAL; |
| } |
| |
| pr_debug("called for str_id %d\n", str_id); |
| retval = sst_validate_strid(str_id); |
| if (retval) |
| return -EINVAL; |
| stream = &sst_drv_ctx->streams[str_id]; |
| if (stream->mmapped == true) |
| return -EIO; |
| if (stream->status == STREAM_UN_INIT || |
| stream->status == STREAM_DECODE) |
| return -EBADRQC; |
| stream->curr_bytes = 0; |
| stream->cumm_bytes = 0; |
| |
| pr_debug("new segs %ld, offset %d, status %d\n" , |
| nr_segs, (int) offset, (int) stream->status); |
| stream->buf_type = SST_BUF_USER_STATIC; |
| do { |
| retval = snd_sst_userbufs_play_cap(iov, nr_segs, |
| str_id, stream); |
| if (retval < 0) |
| break; |
| |
| } while (stream->sg_index < nr_segs); |
| |
| stream->sg_index = 0; |
| stream->cur_ptr = NULL; |
| if (retval >= 0) |
| retval = stream->cumm_bytes; |
| pr_debug("end of play/rec bytes = %d!!\n", retval); |
| return retval; |
| } |
| |
| /* sst_print_stream_params - prints the stream parameters (debug fn)*/ |
| static void sst_print_stream_params(struct snd_sst_get_stream_params *get_prm) |
| { |
| pr_debug("codec params:result = %d\n", |
| get_prm->codec_params.result); |
| pr_debug("codec params:stream = %d\n", |
| get_prm->codec_params.stream_id); |
| pr_debug("codec params:codec = %d\n", |
| get_prm->codec_params.codec); |
| pr_debug("codec params:ops = %d\n", |
| get_prm->codec_params.ops); |
| pr_debug("codec params:stream_type = %d\n", |
| get_prm->codec_params.stream_type); |
| pr_debug("pcmparams:sfreq = %d\n", |
| get_prm->pcm_params.sfreq); |
| pr_debug("pcmparams:num_chan = %d\n", |
| get_prm->pcm_params.num_chan); |
| pr_debug("pcmparams:pcm_wd_sz = %d\n", |
| get_prm->pcm_params.pcm_wd_sz); |
| return; |
| } |
| |
| /** |
| * sst_create_algo_ipc - create ipc msg for algorithm parameters |
| * |
| * @algo_params: Algorithm parameters |
| * @msg: post msg pointer |
| * |
| * This function is called to create ipc msg |
| */ |
| int sst_create_algo_ipc(struct snd_ppp_params *algo_params, |
| struct ipc_post **msg) |
| { |
| if (sst_create_large_msg(msg)) |
| return -ENOMEM; |
| sst_fill_header(&(*msg)->header, |
| IPC_IA_ALG_PARAMS, 1, algo_params->str_id); |
| (*msg)->header.part.data = sizeof(u32) + |
| sizeof(*algo_params) + algo_params->size; |
| memcpy((*msg)->mailbox_data, &(*msg)->header, sizeof(u32)); |
| memcpy((*msg)->mailbox_data + sizeof(u32), |
| algo_params, sizeof(*algo_params)); |
| return 0; |
| } |
| |
| /** |
| * sst_send_algo_ipc - send ipc msg for algorithm parameters |
| * |
| * @msg: post msg pointer |
| * |
| * This function is called to send ipc msg |
| */ |
| int sst_send_algo_ipc(struct ipc_post **msg) |
| { |
| sst_drv_ctx->ppp_params_blk.condition = false; |
| sst_drv_ctx->ppp_params_blk.ret_code = 0; |
| sst_drv_ctx->ppp_params_blk.on = true; |
| sst_drv_ctx->ppp_params_blk.data = NULL; |
| spin_lock(&sst_drv_ctx->list_spin_lock); |
| list_add_tail(&(*msg)->node, &sst_drv_ctx->ipc_dispatch_list); |
| spin_unlock(&sst_drv_ctx->list_spin_lock); |
| sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); |
| return sst_wait_interruptible_timeout(sst_drv_ctx, |
| &sst_drv_ctx->ppp_params_blk, SST_BLOCK_TIMEOUT); |
| } |
| |
| /** |
| * intel_sst_ioctl_dsp - receives the device ioctl's |
| * |
| * @cmd:Ioctl cmd |
| * @arg:data |
| * |
| * This function is called when a user space component |
| * sends a DSP Ioctl to SST driver |
| */ |
| long intel_sst_ioctl_dsp(unsigned int cmd, unsigned long arg) |
| { |
| int retval = 0; |
| struct snd_ppp_params algo_params; |
| struct snd_ppp_params *algo_params_copied; |
| struct ipc_post *msg; |
| |
| switch (_IOC_NR(cmd)) { |
| case _IOC_NR(SNDRV_SST_SET_ALGO): |
| if (copy_from_user(&algo_params, (void __user *)arg, |
| sizeof(algo_params))) |
| return -EFAULT; |
| if (algo_params.size > SST_MAILBOX_SIZE) |
| return -EMSGSIZE; |
| |
| pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", |
| algo_params.algo_id, algo_params.str_id, |
| algo_params.enable, algo_params.size); |
| retval = sst_create_algo_ipc(&algo_params, &msg); |
| if (retval) |
| break; |
| algo_params.reserved = 0; |
| if (copy_from_user(msg->mailbox_data + sizeof(algo_params), |
| algo_params.params, algo_params.size)) |
| return -EFAULT; |
| |
| retval = sst_send_algo_ipc(&msg); |
| if (retval) { |
| pr_debug("Error in sst_set_algo = %d\n", retval); |
| retval = -EIO; |
| } |
| break; |
| |
| case _IOC_NR(SNDRV_SST_GET_ALGO): |
| if (copy_from_user(&algo_params, (void __user *)arg, |
| sizeof(algo_params))) |
| return -EFAULT; |
| pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", |
| algo_params.algo_id, algo_params.str_id, |
| algo_params.enable, algo_params.size); |
| retval = sst_create_algo_ipc(&algo_params, &msg); |
| if (retval) |
| break; |
| algo_params.reserved = 1; |
| retval = sst_send_algo_ipc(&msg); |
| if (retval) { |
| pr_debug("Error in sst_get_algo = %d\n", retval); |
| retval = -EIO; |
| break; |
| } |
| algo_params_copied = (struct snd_ppp_params *) |
| sst_drv_ctx->ppp_params_blk.data; |
| if (algo_params_copied->size > algo_params.size) { |
| pr_debug("mem insufficient to copy\n"); |
| retval = -EMSGSIZE; |
| goto free_mem; |
| } else { |
| char __user *tmp; |
| |
| if (copy_to_user(algo_params.params, |
| algo_params_copied->params, |
| algo_params_copied->size)) { |
| retval = -EFAULT; |
| goto free_mem; |
| } |
| tmp = (char __user *)arg + offsetof( |
| struct snd_ppp_params, size); |
| if (copy_to_user(tmp, &algo_params_copied->size, |
| sizeof(__u32))) { |
| retval = -EFAULT; |
| goto free_mem; |
| } |
| |
| } |
| free_mem: |
| kfree(algo_params_copied->params); |
| kfree(algo_params_copied); |
| break; |
| } |
| return retval; |
| } |
| |
| |
| int sst_ioctl_tuning_params(unsigned long arg) |
| { |
| struct snd_sst_tuning_params params; |
| struct ipc_post *msg; |
| |
| if (copy_from_user(¶ms, (void __user *)arg, sizeof(params))) |
| return -EFAULT; |
| if (params.size > SST_MAILBOX_SIZE) |
| return -ENOMEM; |
| pr_debug("Parameter %d, Stream %d, Size %d\n", params.type, |
| params.str_id, params.size); |
| if (sst_create_large_msg(&msg)) |
| return -ENOMEM; |
| |
| sst_fill_header(&msg->header, IPC_IA_TUNING_PARAMS, 1, params.str_id); |
| msg->header.part.data = sizeof(u32) + sizeof(params) + params.size; |
| memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); |
| memcpy(msg->mailbox_data + sizeof(u32), ¶ms, sizeof(params)); |
| if (copy_from_user(msg->mailbox_data + sizeof(params), |
| (void __user *)(unsigned long)params.addr, |
| params.size)) { |
| kfree(msg->mailbox_data); |
| kfree(msg); |
| return -EFAULT; |
| } |
| return sst_send_algo_ipc(&msg); |
| } |
| /** |
| * intel_sst_ioctl - receives the device ioctl's |
| * @file_ptr:pointer to file |
| * @cmd:Ioctl cmd |
| * @arg:data |
| * |
| * This function is called by OS when a user space component |
| * sends an Ioctl to SST driver |
| */ |
| long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg) |
| { |
| int retval = 0; |
| struct ioctl_pvt_data *data = NULL; |
| int str_id = 0, minor = 0; |
| |
| data = file_ptr->private_data; |
| if (data) { |
| minor = 0; |
| str_id = data->str_id; |
| } else |
| minor = 1; |
| |
| if (sst_drv_ctx->sst_state != SST_FW_RUNNING) |
| return -EBUSY; |
| |
| switch (_IOC_NR(cmd)) { |
| case _IOC_NR(SNDRV_SST_STREAM_PAUSE): |
| pr_debug("IOCTL_PAUSE received for %d!\n", str_id); |
| if (minor != STREAM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| retval = sst_pause_stream(str_id); |
| break; |
| |
| case _IOC_NR(SNDRV_SST_STREAM_RESUME): |
| pr_debug("SNDRV_SST_IOCTL_RESUME received!\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| retval = sst_resume_stream(str_id); |
| break; |
| |
| case _IOC_NR(SNDRV_SST_STREAM_SET_PARAMS): { |
| struct snd_sst_params str_param; |
| |
| pr_debug("IOCTL_SET_PARAMS received!\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| |
| if (copy_from_user(&str_param, (void __user *)arg, |
| sizeof(str_param))) { |
| retval = -EFAULT; |
| break; |
| } |
| |
| if (!str_id) { |
| |
| retval = sst_get_stream(&str_param); |
| if (retval > 0) { |
| struct stream_info *str_info; |
| char __user *dest; |
| |
| sst_drv_ctx->stream_cnt++; |
| data->str_id = retval; |
| str_info = &sst_drv_ctx->streams[retval]; |
| str_info->src = SST_DRV; |
| dest = (char __user *)arg + offsetof(struct snd_sst_params, stream_id); |
| retval = copy_to_user(dest, &retval, sizeof(__u32)); |
| if (retval) |
| retval = -EFAULT; |
| } else { |
| if (retval == -SST_ERR_INVALID_PARAMS) |
| retval = -EINVAL; |
| } |
| } else { |
| pr_debug("SET_STREAM_PARAMS received!\n"); |
| /* allocated set params only */ |
| retval = sst_set_stream_param(str_id, &str_param); |
| /* Block the call for reply */ |
| if (!retval) { |
| int sfreq = 0, word_size = 0, num_channel = 0; |
| sfreq = str_param.sparams.uc.pcm_params.sfreq; |
| word_size = str_param.sparams.uc.pcm_params.pcm_wd_sz; |
| num_channel = str_param.sparams.uc.pcm_params.num_chan; |
| if (str_param.ops == STREAM_OPS_CAPTURE) { |
| sst_drv_ctx->scard_ops->\ |
| set_pcm_audio_params(sfreq, |
| word_size, num_channel); |
| } |
| } |
| } |
| break; |
| } |
| case _IOC_NR(SNDRV_SST_SET_VOL): { |
| struct snd_sst_vol set_vol; |
| |
| if (copy_from_user(&set_vol, (void __user *)arg, |
| sizeof(set_vol))) { |
| pr_debug("copy failed\n"); |
| retval = -EFAULT; |
| break; |
| } |
| pr_debug("SET_VOLUME received for %d!\n", |
| set_vol.stream_id); |
| if (minor == STREAM_MODULE && set_vol.stream_id == 0) { |
| pr_debug("invalid operation!\n"); |
| retval = -EPERM; |
| break; |
| } |
| retval = sst_set_vol(&set_vol); |
| break; |
| } |
| case _IOC_NR(SNDRV_SST_GET_VOL): { |
| struct snd_sst_vol get_vol; |
| |
| if (copy_from_user(&get_vol, (void __user *)arg, |
| sizeof(get_vol))) { |
| retval = -EFAULT; |
| break; |
| } |
| pr_debug("IOCTL_GET_VOLUME received for stream = %d!\n", |
| get_vol.stream_id); |
| if (minor == STREAM_MODULE && get_vol.stream_id == 0) { |
| pr_debug("invalid operation!\n"); |
| retval = -EPERM; |
| break; |
| } |
| retval = sst_get_vol(&get_vol); |
| if (retval) { |
| retval = -EIO; |
| break; |
| } |
| pr_debug("id:%d\n, vol:%d, ramp_dur:%d, ramp_type:%d\n", |
| get_vol.stream_id, get_vol.volume, |
| get_vol.ramp_duration, get_vol.ramp_type); |
| if (copy_to_user((struct snd_sst_vol __user *)arg, |
| &get_vol, sizeof(get_vol))) { |
| retval = -EFAULT; |
| break; |
| } |
| /*sst_print_get_vol_info(str_id, &get_vol);*/ |
| break; |
| } |
| |
| case _IOC_NR(SNDRV_SST_MUTE): { |
| struct snd_sst_mute set_mute; |
| |
| if (copy_from_user(&set_mute, (void __user *)arg, |
| sizeof(set_mute))) { |
| retval = -EFAULT; |
| break; |
| } |
| pr_debug("SNDRV_SST_SET_VOLUME received for %d!\n", |
| set_mute.stream_id); |
| if (minor == STREAM_MODULE && set_mute.stream_id == 0) { |
| retval = -EPERM; |
| break; |
| } |
| retval = sst_set_mute(&set_mute); |
| break; |
| } |
| case _IOC_NR(SNDRV_SST_STREAM_GET_PARAMS): { |
| struct snd_sst_get_stream_params get_params; |
| |
| pr_debug("IOCTL_GET_PARAMS received!\n"); |
| if (minor != 0) { |
| retval = -EBADRQC; |
| break; |
| } |
| |
| retval = sst_get_stream_params(str_id, &get_params); |
| if (retval) { |
| retval = -EIO; |
| break; |
| } |
| if (copy_to_user((struct snd_sst_get_stream_params __user *)arg, |
| &get_params, sizeof(get_params))) { |
| retval = -EFAULT; |
| break; |
| } |
| sst_print_stream_params(&get_params); |
| break; |
| } |
| |
| case _IOC_NR(SNDRV_SST_MMAP_PLAY): |
| case _IOC_NR(SNDRV_SST_MMAP_CAPTURE): { |
| struct snd_sst_mmap_buffs mmap_buf; |
| |
| pr_debug("SNDRV_SST_MMAP_PLAY/CAPTURE received!\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| if (copy_from_user(&mmap_buf, (void __user *)arg, |
| sizeof(mmap_buf))) { |
| retval = -EFAULT; |
| break; |
| } |
| retval = intel_sst_mmap_play_capture(str_id, &mmap_buf); |
| break; |
| } |
| case _IOC_NR(SNDRV_SST_STREAM_DROP): |
| pr_debug("SNDRV_SST_IOCTL_DROP received!\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EINVAL; |
| break; |
| } |
| retval = sst_drop_stream(str_id); |
| break; |
| |
| case _IOC_NR(SNDRV_SST_STREAM_GET_TSTAMP): { |
| struct snd_sst_tstamp tstamp = {0}; |
| unsigned long long time, freq, mod; |
| |
| pr_debug("SNDRV_SST_STREAM_GET_TSTAMP received!\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| memcpy_fromio(&tstamp, |
| sst_drv_ctx->mailbox + SST_TIME_STAMP + str_id * sizeof(tstamp), |
| sizeof(tstamp)); |
| time = tstamp.samples_rendered; |
| freq = (unsigned long long) tstamp.sampling_frequency; |
| time = time * 1000; /* converting it to ms */ |
| mod = do_div(time, freq); |
| if (copy_to_user((void __user *)arg, &time, |
| sizeof(unsigned long long))) |
| retval = -EFAULT; |
| break; |
| } |
| |
| case _IOC_NR(SNDRV_SST_STREAM_START):{ |
| struct stream_info *stream; |
| |
| pr_debug("SNDRV_SST_STREAM_START received!\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EINVAL; |
| break; |
| } |
| retval = sst_validate_strid(str_id); |
| if (retval) |
| break; |
| stream = &sst_drv_ctx->streams[str_id]; |
| mutex_lock(&stream->lock); |
| if (stream->status == STREAM_INIT && |
| stream->need_draining != true) { |
| stream->prev = stream->status; |
| stream->status = STREAM_RUNNING; |
| if (stream->ops == STREAM_OPS_PLAYBACK || |
| stream->ops == STREAM_OPS_PLAYBACK_DRM) { |
| retval = sst_play_frame(str_id); |
| } else if (stream->ops == STREAM_OPS_CAPTURE) |
| retval = sst_capture_frame(str_id); |
| else { |
| retval = -EINVAL; |
| mutex_unlock(&stream->lock); |
| break; |
| } |
| if (retval < 0) { |
| stream->status = STREAM_INIT; |
| mutex_unlock(&stream->lock); |
| break; |
| } |
| } else { |
| retval = -EINVAL; |
| } |
| mutex_unlock(&stream->lock); |
| break; |
| } |
| |
| case _IOC_NR(SNDRV_SST_SET_TARGET_DEVICE): { |
| struct snd_sst_target_device target_device; |
| |
| pr_debug("SET_TARGET_DEVICE received!\n"); |
| if (copy_from_user(&target_device, (void __user *)arg, |
| sizeof(target_device))) { |
| retval = -EFAULT; |
| break; |
| } |
| if (minor != AM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| retval = sst_target_device_select(&target_device); |
| break; |
| } |
| |
| case _IOC_NR(SNDRV_SST_DRIVER_INFO): { |
| struct snd_sst_driver_info info; |
| |
| pr_debug("SNDRV_SST_DRIVER_INFO received\n"); |
| info.version = SST_VERSION_NUM; |
| /* hard coding, shud get sumhow later */ |
| info.active_pcm_streams = sst_drv_ctx->stream_cnt - |
| sst_drv_ctx->encoded_cnt; |
| info.active_enc_streams = sst_drv_ctx->encoded_cnt; |
| info.max_pcm_streams = MAX_ACTIVE_STREAM - MAX_ENC_STREAM; |
| info.max_enc_streams = MAX_ENC_STREAM; |
| info.buf_per_stream = sst_drv_ctx->mmap_len; |
| if (copy_to_user((void __user *)arg, &info, |
| sizeof(info))) |
| retval = -EFAULT; |
| break; |
| } |
| |
| case _IOC_NR(SNDRV_SST_STREAM_DECODE): { |
| struct snd_sst_dbufs param; |
| struct snd_sst_dbufs dbufs_local; |
| struct snd_sst_buffs ibufs, obufs; |
| struct snd_sst_buff_entry *ibuf_tmp, *obuf_tmp; |
| char __user *dest; |
| |
| pr_debug("SNDRV_SST_STREAM_DECODE received\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| if (copy_from_user(¶m, (void __user *)arg, |
| sizeof(param))) { |
| retval = -EFAULT; |
| break; |
| } |
| |
| dbufs_local.input_bytes_consumed = param.input_bytes_consumed; |
| dbufs_local.output_bytes_produced = |
| param.output_bytes_produced; |
| |
| if (copy_from_user(&ibufs, (void __user *)param.ibufs, sizeof(ibufs))) { |
| retval = -EFAULT; |
| break; |
| } |
| if (copy_from_user(&obufs, (void __user *)param.obufs, sizeof(obufs))) { |
| retval = -EFAULT; |
| break; |
| } |
| |
| ibuf_tmp = kcalloc(ibufs.entries, sizeof(*ibuf_tmp), GFP_KERNEL); |
| obuf_tmp = kcalloc(obufs.entries, sizeof(*obuf_tmp), GFP_KERNEL); |
| if (!ibuf_tmp || !obuf_tmp) { |
| retval = -ENOMEM; |
| goto free_iobufs; |
| } |
| |
| if (copy_from_user(ibuf_tmp, (void __user *)ibufs.buff_entry, |
| ibufs.entries * sizeof(*ibuf_tmp))) { |
| retval = -EFAULT; |
| goto free_iobufs; |
| } |
| ibufs.buff_entry = ibuf_tmp; |
| dbufs_local.ibufs = &ibufs; |
| |
| if (copy_from_user(obuf_tmp, (void __user *)obufs.buff_entry, |
| obufs.entries * sizeof(*obuf_tmp))) { |
| retval = -EFAULT; |
| goto free_iobufs; |
| } |
| obufs.buff_entry = obuf_tmp; |
| dbufs_local.obufs = &obufs; |
| |
| retval = sst_decode(str_id, &dbufs_local); |
| if (retval) { |
| retval = -EAGAIN; |
| goto free_iobufs; |
| } |
| |
| dest = (char __user *)arg + offsetof(struct snd_sst_dbufs, input_bytes_consumed); |
| if (copy_to_user(dest, |
| &dbufs_local.input_bytes_consumed, |
| sizeof(unsigned long long))) { |
| retval = -EFAULT; |
| goto free_iobufs; |
| } |
| |
| dest = (char __user *)arg + offsetof(struct snd_sst_dbufs, input_bytes_consumed); |
| if (copy_to_user(dest, |
| &dbufs_local.output_bytes_produced, |
| sizeof(unsigned long long))) { |
| retval = -EFAULT; |
| goto free_iobufs; |
| } |
| free_iobufs: |
| kfree(ibuf_tmp); |
| kfree(obuf_tmp); |
| break; |
| } |
| |
| case _IOC_NR(SNDRV_SST_STREAM_DRAIN): |
| pr_debug("SNDRV_SST_STREAM_DRAIN received\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EINVAL; |
| break; |
| } |
| retval = sst_drain_stream(str_id); |
| break; |
| |
| case _IOC_NR(SNDRV_SST_STREAM_BYTES_DECODED): { |
| unsigned long long __user *bytes = (unsigned long long __user *)arg; |
| struct snd_sst_tstamp tstamp = {0}; |
| |
| pr_debug("STREAM_BYTES_DECODED received!\n"); |
| if (minor != STREAM_MODULE) { |
| retval = -EINVAL; |
| break; |
| } |
| memcpy_fromio(&tstamp, |
| sst_drv_ctx->mailbox + SST_TIME_STAMP + str_id * sizeof(tstamp), |
| sizeof(tstamp)); |
| if (copy_to_user(bytes, &tstamp.bytes_processed, |
| sizeof(*bytes))) |
| retval = -EFAULT; |
| break; |
| } |
| case _IOC_NR(SNDRV_SST_FW_INFO): { |
| struct snd_sst_fw_info *fw_info; |
| |
| pr_debug("SNDRV_SST_FW_INFO received\n"); |
| |
| fw_info = kzalloc(sizeof(*fw_info), GFP_ATOMIC); |
| if (!fw_info) { |
| retval = -ENOMEM; |
| break; |
| } |
| retval = sst_get_fw_info(fw_info); |
| if (retval) { |
| retval = -EIO; |
| kfree(fw_info); |
| break; |
| } |
| if (copy_to_user((struct snd_sst_dbufs __user *)arg, |
| fw_info, sizeof(*fw_info))) { |
| kfree(fw_info); |
| retval = -EFAULT; |
| break; |
| } |
| /*sst_print_fw_info(fw_info);*/ |
| kfree(fw_info); |
| break; |
| } |
| case _IOC_NR(SNDRV_SST_GET_ALGO): |
| case _IOC_NR(SNDRV_SST_SET_ALGO): |
| if (minor != AM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| retval = intel_sst_ioctl_dsp(cmd, arg); |
| break; |
| |
| case _IOC_NR(SNDRV_SST_TUNING_PARAMS): |
| if (minor != AM_MODULE) { |
| retval = -EBADRQC; |
| break; |
| } |
| retval = sst_ioctl_tuning_params(arg); |
| break; |
| |
| default: |
| retval = -EINVAL; |
| } |
| pr_debug("intel_sst_ioctl:complete ret code = %d\n", retval); |
| return retval; |
| } |
| |