]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/misc/fastrpc.c
Merge tag 'mips_fixes_5.5_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
[linux.git] / drivers / misc / fastrpc.c
index 1b1a794d639d0d8518e1bbb351966aee8ee74b23..ae4ee27a63c41631f4ec66c3a53528d4cf5af446 100644 (file)
@@ -32,8 +32,9 @@
 #define FASTRPC_CTX_MAX (256)
 #define FASTRPC_INIT_HANDLE    1
 #define FASTRPC_CTXID_MASK (0xFF0)
-#define INIT_FILELEN_MAX (64 * 1024 * 1024)
+#define INIT_FILELEN_MAX (2 * 1024 * 1024)
 #define FASTRPC_DEVICE_NAME    "fastrpc"
+#define ADSP_MMAP_ADD_PAGES 0x1000
 
 /* Retrives number of input buffers from the scalars parameter */
 #define REMOTE_SCALARS_INBUFS(sc)      (((sc) >> 16) & 0x0ff)
@@ -66,6 +67,8 @@
 /* Remote Method id table */
 #define FASTRPC_RMID_INIT_ATTACH       0
 #define FASTRPC_RMID_INIT_RELEASE      1
+#define FASTRPC_RMID_INIT_MMAP         4
+#define FASTRPC_RMID_INIT_MUNMAP       5
 #define FASTRPC_RMID_INIT_CREATE       6
 #define FASTRPC_RMID_INIT_CREATE_ATTR  7
 #define FASTRPC_RMID_INIT_CREATE_STATIC        8
@@ -89,6 +92,23 @@ struct fastrpc_remote_arg {
        u64 len;
 };
 
+struct fastrpc_mmap_rsp_msg {
+       u64 vaddr;
+};
+
+struct fastrpc_mmap_req_msg {
+       s32 pgid;
+       u32 flags;
+       u64 vaddr;
+       s32 num;
+};
+
+struct fastrpc_munmap_req_msg {
+       s32 pgid;
+       u64 vaddr;
+       u64 size;
+};
+
 struct fastrpc_msg {
        int pid;                /* process group id */
        int tid;                /* thread id */
@@ -123,6 +143,9 @@ struct fastrpc_buf {
        /* Lock for dma buf attachments */
        struct mutex lock;
        struct list_head attachments;
+       /* mmap support */
+       struct list_head node; /* list of user requested mmaps */
+       uintptr_t raddr;
 };
 
 struct fastrpc_dma_buf_attachment {
@@ -192,6 +215,7 @@ struct fastrpc_user {
        struct list_head user;
        struct list_head maps;
        struct list_head pending;
+       struct list_head mmaps;
 
        struct fastrpc_channel_ctx *cctx;
        struct fastrpc_session_ctx *sctx;
@@ -269,6 +293,7 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
                return -ENOMEM;
 
        INIT_LIST_HEAD(&buf->attachments);
+       INIT_LIST_HEAD(&buf->node);
        mutex_init(&buf->lock);
 
        buf->fl = fl;
@@ -276,6 +301,7 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev,
        buf->phys = 0;
        buf->size = size;
        buf->dev = dev;
+       buf->raddr = 0;
 
        buf->virt = dma_alloc_coherent(dev, buf->size, (dma_addr_t *)&buf->phys,
                                       GFP_KERNEL);
@@ -934,8 +960,13 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl,  u32 kernel,
        if (err)
                goto bail;
 
-       /* Wait for remote dsp to respond or time out */
-       err = wait_for_completion_interruptible(&ctx->work);
+       if (kernel) {
+               if (!wait_for_completion_timeout(&ctx->work, 10 * HZ))
+                       err = -ETIMEDOUT;
+       } else {
+               err = wait_for_completion_interruptible(&ctx->work);
+       }
+
        if (err)
                goto bail;
 
@@ -954,12 +985,13 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl,  u32 kernel,
        }
 
 bail:
-       /* We are done with this compute context, remove it from pending list */
-       spin_lock(&fl->lock);
-       list_del(&ctx->node);
-       spin_unlock(&fl->lock);
-       fastrpc_context_put(ctx);
-
+       if (err != -ERESTARTSYS && err != -ETIMEDOUT) {
+               /* We are done with this compute context */
+               spin_lock(&fl->lock);
+               list_del(&ctx->node);
+               spin_unlock(&fl->lock);
+               fastrpc_context_put(ctx);
+       }
        if (err)
                dev_dbg(fl->sctx->dev, "Error: Invoke Failed %d\n", err);
 
@@ -1131,6 +1163,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
        struct fastrpc_channel_ctx *cctx = fl->cctx;
        struct fastrpc_invoke_ctx *ctx, *n;
        struct fastrpc_map *map, *m;
+       struct fastrpc_buf *buf, *b;
        unsigned long flags;
 
        fastrpc_release_current_dsp_process(fl);
@@ -1152,6 +1185,11 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
                fastrpc_map_put(map);
        }
 
+       list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
+               list_del(&buf->node);
+               fastrpc_buf_free(buf);
+       }
+
        fastrpc_session_free(cctx, fl->sctx);
        fastrpc_channel_ctx_put(cctx);
 
@@ -1180,6 +1218,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
        mutex_init(&fl->mutex);
        INIT_LIST_HEAD(&fl->pending);
        INIT_LIST_HEAD(&fl->maps);
+       INIT_LIST_HEAD(&fl->mmaps);
        INIT_LIST_HEAD(&fl->user);
        fl->tgid = current->tgid;
        fl->cctx = cctx;
@@ -1285,6 +1324,148 @@ static int fastrpc_invoke(struct fastrpc_user *fl, char __user *argp)
        return err;
 }
 
+static int fastrpc_req_munmap_impl(struct fastrpc_user *fl,
+                                  struct fastrpc_req_munmap *req)
+{
+       struct fastrpc_invoke_args args[1] = { [0] = { 0 } };
+       struct fastrpc_buf *buf, *b;
+       struct fastrpc_munmap_req_msg req_msg;
+       struct device *dev = fl->sctx->dev;
+       int err;
+       u32 sc;
+
+       spin_lock(&fl->lock);
+       list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
+               if ((buf->raddr == req->vaddrout) && (buf->size == req->size))
+                       break;
+               buf = NULL;
+       }
+       spin_unlock(&fl->lock);
+
+       if (!buf) {
+               dev_err(dev, "mmap not in list\n");
+               return -EINVAL;
+       }
+
+       req_msg.pgid = fl->tgid;
+       req_msg.size = buf->size;
+       req_msg.vaddr = buf->raddr;
+
+       args[0].ptr = (u64) (uintptr_t) &req_msg;
+       args[0].length = sizeof(req_msg);
+
+       sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MUNMAP, 1, 0);
+       err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc,
+                                     &args[0]);
+       if (!err) {
+               dev_dbg(dev, "unmmap\tpt 0x%09lx OK\n", buf->raddr);
+               spin_lock(&fl->lock);
+               list_del(&buf->node);
+               spin_unlock(&fl->lock);
+               fastrpc_buf_free(buf);
+       } else {
+               dev_err(dev, "unmmap\tpt 0x%09lx ERROR\n", buf->raddr);
+       }
+
+       return err;
+}
+
+static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
+{
+       struct fastrpc_req_munmap req;
+
+       if (copy_from_user(&req, argp, sizeof(req)))
+               return -EFAULT;
+
+       return fastrpc_req_munmap_impl(fl, &req);
+}
+
+static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
+{
+       struct fastrpc_invoke_args args[3] = { [0 ... 2] = { 0 } };
+       struct fastrpc_buf *buf = NULL;
+       struct fastrpc_mmap_req_msg req_msg;
+       struct fastrpc_mmap_rsp_msg rsp_msg;
+       struct fastrpc_req_munmap req_unmap;
+       struct fastrpc_phy_page pages;
+       struct fastrpc_req_mmap req;
+       struct device *dev = fl->sctx->dev;
+       int err;
+       u32 sc;
+
+       if (copy_from_user(&req, argp, sizeof(req)))
+               return -EFAULT;
+
+       if (req.flags != ADSP_MMAP_ADD_PAGES) {
+               dev_err(dev, "flag not supported 0x%x\n", req.flags);
+               return -EINVAL;
+       }
+
+       if (req.vaddrin) {
+               dev_err(dev, "adding user allocated pages is not supported\n");
+               return -EINVAL;
+       }
+
+       err = fastrpc_buf_alloc(fl, fl->sctx->dev, req.size, &buf);
+       if (err) {
+               dev_err(dev, "failed to allocate buffer\n");
+               return err;
+       }
+
+       req_msg.pgid = fl->tgid;
+       req_msg.flags = req.flags;
+       req_msg.vaddr = req.vaddrin;
+       req_msg.num = sizeof(pages);
+
+       args[0].ptr = (u64) (uintptr_t) &req_msg;
+       args[0].length = sizeof(req_msg);
+
+       pages.addr = buf->phys;
+       pages.size = buf->size;
+
+       args[1].ptr = (u64) (uintptr_t) &pages;
+       args[1].length = sizeof(pages);
+
+       args[2].ptr = (u64) (uintptr_t) &rsp_msg;
+       args[2].length = sizeof(rsp_msg);
+
+       sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MMAP, 2, 1);
+       err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc,
+                                     &args[0]);
+       if (err) {
+               dev_err(dev, "mmap error (len 0x%08llx)\n", buf->size);
+               goto err_invoke;
+       }
+
+       /* update the buffer to be able to deallocate the memory on the DSP */
+       buf->raddr = (uintptr_t) rsp_msg.vaddr;
+
+       /* let the client know the address to use */
+       req.vaddrout = rsp_msg.vaddr;
+
+       spin_lock(&fl->lock);
+       list_add_tail(&buf->node, &fl->mmaps);
+       spin_unlock(&fl->lock);
+
+       if (copy_to_user((void __user *)argp, &req, sizeof(req))) {
+               /* unmap the memory and release the buffer */
+               req_unmap.vaddrout = buf->raddr;
+               req_unmap.size = buf->size;
+               fastrpc_req_munmap_impl(fl, &req_unmap);
+               return -EFAULT;
+       }
+
+       dev_dbg(dev, "mmap\t\tpt 0x%09lx OK [len 0x%08llx]\n",
+               buf->raddr, buf->size);
+
+       return 0;
+
+err_invoke:
+       fastrpc_buf_free(buf);
+
+       return err;
+}
+
 static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
                                 unsigned long arg)
 {
@@ -1305,6 +1486,12 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd,
        case FASTRPC_IOCTL_ALLOC_DMA_BUFF:
                err = fastrpc_dmabuf_alloc(fl, argp);
                break;
+       case FASTRPC_IOCTL_MMAP:
+               err = fastrpc_req_mmap(fl, argp);
+               break;
+       case FASTRPC_IOCTL_MUNMAP:
+               err = fastrpc_req_munmap(fl, argp);
+               break;
        default:
                err = -ENOTTY;
                break;
@@ -1430,8 +1617,8 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
                return -ENOMEM;
 
        data->miscdev.minor = MISC_DYNAMIC_MINOR;
-       data->miscdev.name = kasprintf(GFP_KERNEL, "fastrpc-%s",
-                               domains[domain_id]);
+       data->miscdev.name = devm_kasprintf(rdev, GFP_KERNEL, "fastrpc-%s",
+                                           domains[domain_id]);
        data->miscdev.fops = &fastrpc_fops;
        err = misc_register(&data->miscdev);
        if (err)