diff options
| -rw-r--r-- | drivers/misc/fastrpc.c | 184 | ||||
| -rw-r--r-- | include/uapi/misc/fastrpc.h | 8 |
2 files changed, 192 insertions, 0 deletions
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index ceb498487569..4b0db33896df 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c | |||
| @@ -106,10 +106,20 @@ struct fastrpc_invoke_rsp { | |||
| 106 | 106 | ||
| 107 | struct fastrpc_buf { | 107 | struct fastrpc_buf { |
| 108 | struct fastrpc_user *fl; | 108 | struct fastrpc_user *fl; |
| 109 | struct dma_buf *dmabuf; | ||
| 109 | struct device *dev; | 110 | struct device *dev; |
| 110 | void *virt; | 111 | void *virt; |
| 111 | u64 phys; | 112 | u64 phys; |
| 112 | u64 size; | 113 | u64 size; |
| 114 | /* Lock for dma buf attachments */ | ||
| 115 | struct mutex lock; | ||
| 116 | struct list_head attachments; | ||
| 117 | }; | ||
| 118 | |||
| 119 | struct fastrpc_dma_buf_attachment { | ||
| 120 | struct device *dev; | ||
| 121 | struct sg_table sgt; | ||
| 122 | struct list_head node; | ||
| 113 | }; | 123 | }; |
| 114 | 124 | ||
| 115 | struct fastrpc_map { | 125 | struct fastrpc_map { |
| @@ -246,6 +256,9 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev, | |||
| 246 | if (!buf) | 256 | if (!buf) |
| 247 | return -ENOMEM; | 257 | return -ENOMEM; |
| 248 | 258 | ||
| 259 | INIT_LIST_HEAD(&buf->attachments); | ||
| 260 | mutex_init(&buf->lock); | ||
| 261 | |||
| 249 | buf->fl = fl; | 262 | buf->fl = fl; |
| 250 | buf->virt = NULL; | 263 | buf->virt = NULL; |
| 251 | buf->phys = 0; | 264 | buf->phys = 0; |
| @@ -360,6 +373,111 @@ err_idr: | |||
| 360 | return ERR_PTR(ret); | 373 | return ERR_PTR(ret); |
| 361 | } | 374 | } |
| 362 | 375 | ||
| 376 | static struct sg_table * | ||
| 377 | fastrpc_map_dma_buf(struct dma_buf_attachment *attachment, | ||
| 378 | enum dma_data_direction dir) | ||
| 379 | { | ||
| 380 | struct fastrpc_dma_buf_attachment *a = attachment->priv; | ||
| 381 | struct sg_table *table; | ||
| 382 | |||
| 383 | table = &a->sgt; | ||
| 384 | |||
| 385 | if (!dma_map_sg(attachment->dev, table->sgl, table->nents, dir)) | ||
| 386 | return ERR_PTR(-ENOMEM); | ||
| 387 | |||
| 388 | return table; | ||
| 389 | } | ||
| 390 | |||
| 391 | static void fastrpc_unmap_dma_buf(struct dma_buf_attachment *attach, | ||
| 392 | struct sg_table *table, | ||
| 393 | enum dma_data_direction dir) | ||
| 394 | { | ||
| 395 | dma_unmap_sg(attach->dev, table->sgl, table->nents, dir); | ||
| 396 | } | ||
| 397 | |||
| 398 | static void fastrpc_release(struct dma_buf *dmabuf) | ||
| 399 | { | ||
| 400 | struct fastrpc_buf *buffer = dmabuf->priv; | ||
| 401 | |||
| 402 | fastrpc_buf_free(buffer); | ||
| 403 | } | ||
| 404 | |||
| 405 | static int fastrpc_dma_buf_attach(struct dma_buf *dmabuf, | ||
| 406 | struct dma_buf_attachment *attachment) | ||
| 407 | { | ||
| 408 | struct fastrpc_dma_buf_attachment *a; | ||
| 409 | struct fastrpc_buf *buffer = dmabuf->priv; | ||
| 410 | int ret; | ||
| 411 | |||
| 412 | a = kzalloc(sizeof(*a), GFP_KERNEL); | ||
| 413 | if (!a) | ||
| 414 | return -ENOMEM; | ||
| 415 | |||
| 416 | ret = dma_get_sgtable(buffer->dev, &a->sgt, buffer->virt, | ||
| 417 | FASTRPC_PHYS(buffer->phys), buffer->size); | ||
| 418 | if (ret < 0) { | ||
| 419 | dev_err(buffer->dev, "failed to get scatterlist from DMA API\n"); | ||
| 420 | return -EINVAL; | ||
| 421 | } | ||
| 422 | |||
| 423 | a->dev = attachment->dev; | ||
| 424 | INIT_LIST_HEAD(&a->node); | ||
| 425 | attachment->priv = a; | ||
| 426 | |||
| 427 | mutex_lock(&buffer->lock); | ||
| 428 | list_add(&a->node, &buffer->attachments); | ||
| 429 | mutex_unlock(&buffer->lock); | ||
| 430 | |||
| 431 | return 0; | ||
| 432 | } | ||
| 433 | |||
| 434 | static void fastrpc_dma_buf_detatch(struct dma_buf *dmabuf, | ||
| 435 | struct dma_buf_attachment *attachment) | ||
| 436 | { | ||
| 437 | struct fastrpc_dma_buf_attachment *a = attachment->priv; | ||
| 438 | struct fastrpc_buf *buffer = dmabuf->priv; | ||
| 439 | |||
| 440 | mutex_lock(&buffer->lock); | ||
| 441 | list_del(&a->node); | ||
| 442 | mutex_unlock(&buffer->lock); | ||
| 443 | kfree(a); | ||
| 444 | } | ||
| 445 | |||
| 446 | static void *fastrpc_kmap(struct dma_buf *dmabuf, unsigned long pgnum) | ||
| 447 | { | ||
| 448 | struct fastrpc_buf *buf = dmabuf->priv; | ||
| 449 | |||
| 450 | return buf->virt ? buf->virt + pgnum * PAGE_SIZE : NULL; | ||
| 451 | } | ||
| 452 | |||
| 453 | static void *fastrpc_vmap(struct dma_buf *dmabuf) | ||
| 454 | { | ||
| 455 | struct fastrpc_buf *buf = dmabuf->priv; | ||
| 456 | |||
| 457 | return buf->virt; | ||
| 458 | } | ||
| 459 | |||
| 460 | static int fastrpc_mmap(struct dma_buf *dmabuf, | ||
| 461 | struct vm_area_struct *vma) | ||
| 462 | { | ||
| 463 | struct fastrpc_buf *buf = dmabuf->priv; | ||
| 464 | size_t size = vma->vm_end - vma->vm_start; | ||
| 465 | |||
| 466 | return dma_mmap_coherent(buf->dev, vma, buf->virt, | ||
| 467 | FASTRPC_PHYS(buf->phys), size); | ||
| 468 | } | ||
| 469 | |||
| 470 | static const struct dma_buf_ops fastrpc_dma_buf_ops = { | ||
| 471 | .attach = fastrpc_dma_buf_attach, | ||
| 472 | .detach = fastrpc_dma_buf_detatch, | ||
| 473 | .map_dma_buf = fastrpc_map_dma_buf, | ||
| 474 | .unmap_dma_buf = fastrpc_unmap_dma_buf, | ||
| 475 | .mmap = fastrpc_mmap, | ||
| 476 | .map = fastrpc_kmap, | ||
| 477 | .vmap = fastrpc_vmap, | ||
| 478 | .release = fastrpc_release, | ||
| 479 | }; | ||
| 480 | |||
| 363 | static int fastrpc_map_create(struct fastrpc_user *fl, int fd, | 481 | static int fastrpc_map_create(struct fastrpc_user *fl, int fd, |
| 364 | u64 len, struct fastrpc_map **ppmap) | 482 | u64 len, struct fastrpc_map **ppmap) |
| 365 | { | 483 | { |
| @@ -906,6 +1024,66 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) | |||
| 906 | return 0; | 1024 | return 0; |
| 907 | } | 1025 | } |
| 908 | 1026 | ||
| 1027 | static int fastrpc_dmabuf_free(struct fastrpc_user *fl, char __user *argp) | ||
| 1028 | { | ||
| 1029 | struct dma_buf *buf; | ||
| 1030 | int info; | ||
| 1031 | |||
| 1032 | if (copy_from_user(&info, argp, sizeof(info))) | ||
| 1033 | return -EFAULT; | ||
| 1034 | |||
| 1035 | buf = dma_buf_get(info); | ||
| 1036 | if (IS_ERR_OR_NULL(buf)) | ||
| 1037 | return -EINVAL; | ||
| 1038 | /* | ||
| 1039 | * one for the last get and other for the ALLOC_DMA_BUFF ioctl | ||
| 1040 | */ | ||
| 1041 | dma_buf_put(buf); | ||
| 1042 | dma_buf_put(buf); | ||
| 1043 | |||
| 1044 | return 0; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | static int fastrpc_dmabuf_alloc(struct fastrpc_user *fl, char __user *argp) | ||
| 1048 | { | ||
| 1049 | struct fastrpc_alloc_dma_buf bp; | ||
| 1050 | DEFINE_DMA_BUF_EXPORT_INFO(exp_info); | ||
| 1051 | struct fastrpc_buf *buf = NULL; | ||
| 1052 | int err; | ||
| 1053 | |||
| 1054 | if (copy_from_user(&bp, argp, sizeof(bp))) | ||
| 1055 | return -EFAULT; | ||
| 1056 | |||
| 1057 | err = fastrpc_buf_alloc(fl, fl->sctx->dev, bp.size, &buf); | ||
| 1058 | if (err) | ||
| 1059 | return err; | ||
| 1060 | exp_info.ops = &fastrpc_dma_buf_ops; | ||
| 1061 | exp_info.size = bp.size; | ||
| 1062 | exp_info.flags = O_RDWR; | ||
| 1063 | exp_info.priv = buf; | ||
| 1064 | buf->dmabuf = dma_buf_export(&exp_info); | ||
| 1065 | if (IS_ERR(buf->dmabuf)) { | ||
| 1066 | err = PTR_ERR(buf->dmabuf); | ||
| 1067 | fastrpc_buf_free(buf); | ||
| 1068 | return err; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | bp.fd = dma_buf_fd(buf->dmabuf, O_ACCMODE); | ||
| 1072 | if (bp.fd < 0) { | ||
| 1073 | dma_buf_put(buf->dmabuf); | ||
| 1074 | return -EINVAL; | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | if (copy_to_user(argp, &bp, sizeof(bp))) { | ||
| 1078 | dma_buf_put(buf->dmabuf); | ||
| 1079 | return -EFAULT; | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | get_dma_buf(buf->dmabuf); | ||
| 1083 | |||
| 1084 | return 0; | ||
| 1085 | } | ||
| 1086 | |||
| 909 | static int fastrpc_init_attach(struct fastrpc_user *fl) | 1087 | static int fastrpc_init_attach(struct fastrpc_user *fl) |
| 910 | { | 1088 | { |
| 911 | struct fastrpc_invoke_args args[1]; | 1089 | struct fastrpc_invoke_args args[1]; |
| @@ -970,6 +1148,12 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd, | |||
| 970 | case FASTRPC_IOCTL_INIT_CREATE: | 1148 | case FASTRPC_IOCTL_INIT_CREATE: |
| 971 | err = fastrpc_init_create_process(fl, argp); | 1149 | err = fastrpc_init_create_process(fl, argp); |
| 972 | break; | 1150 | break; |
| 1151 | case FASTRPC_IOCTL_FREE_DMA_BUFF: | ||
| 1152 | err = fastrpc_dmabuf_free(fl, argp); | ||
| 1153 | break; | ||
| 1154 | case FASTRPC_IOCTL_ALLOC_DMA_BUFF: | ||
| 1155 | err = fastrpc_dmabuf_alloc(fl, argp); | ||
| 1156 | break; | ||
| 973 | default: | 1157 | default: |
| 974 | err = -ENOTTY; | 1158 | err = -ENOTTY; |
| 975 | break; | 1159 | break; |
diff --git a/include/uapi/misc/fastrpc.h b/include/uapi/misc/fastrpc.h index 32d191c3b7bc..6d701af9fc42 100644 --- a/include/uapi/misc/fastrpc.h +++ b/include/uapi/misc/fastrpc.h | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | #include <linux/types.h> | 6 | #include <linux/types.h> |
| 7 | 7 | ||
| 8 | #define FASTRPC_IOCTL_ALLOC_DMA_BUFF _IOWR('R', 1, struct fastrpc_alloc_dma_buf) | ||
| 9 | #define FASTRPC_IOCTL_FREE_DMA_BUFF _IOWR('R', 2, __u32) | ||
| 8 | #define FASTRPC_IOCTL_INVOKE _IOWR('R', 3, struct fastrpc_invoke) | 10 | #define FASTRPC_IOCTL_INVOKE _IOWR('R', 3, struct fastrpc_invoke) |
| 9 | #define FASTRPC_IOCTL_INIT_ATTACH _IO('R', 4) | 11 | #define FASTRPC_IOCTL_INIT_ATTACH _IO('R', 4) |
| 10 | #define FASTRPC_IOCTL_INIT_CREATE _IOWR('R', 5, struct fastrpc_init_create) | 12 | #define FASTRPC_IOCTL_INIT_CREATE _IOWR('R', 5, struct fastrpc_init_create) |
| @@ -30,4 +32,10 @@ struct fastrpc_init_create { | |||
| 30 | __u64 file; /* pointer to elf file */ | 32 | __u64 file; /* pointer to elf file */ |
| 31 | }; | 33 | }; |
| 32 | 34 | ||
| 35 | struct fastrpc_alloc_dma_buf { | ||
| 36 | __s32 fd; /* fd */ | ||
| 37 | __u32 flags; /* flags to map with */ | ||
| 38 | __u64 size; /* size */ | ||
| 39 | }; | ||
| 40 | |||
| 33 | #endif /* __QCOM_FASTRPC_H__ */ | 41 | #endif /* __QCOM_FASTRPC_H__ */ |
