1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Character device interface driver for Remoteproc framework. 4 * 5 * Copyright (c) 2020, The Linux Foundation. All rights reserved. 6 */ 7 8 #include <linux/cdev.h> 9 #include <linux/compat.h> 10 #include <linux/fs.h> 11 #include <linux/module.h> 12 #include <linux/remoteproc.h> 13 #include <linux/uaccess.h> 14 #include <uapi/linux/remoteproc_cdev.h> 15 16 #include "remoteproc_internal.h" 17 18 #define NUM_RPROC_DEVICES 64 19 static dev_t rproc_major; 20 21 static ssize_t rproc_cdev_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos) 22 { 23 struct rproc *rproc = container_of(filp->f_inode->i_cdev, struct rproc, cdev); 24 int ret = 0; 25 char cmd[10]; 26 27 if (!len || len > sizeof(cmd)) 28 return -EINVAL; 29 30 ret = copy_from_user(cmd, buf, len); 31 if (ret) 32 return -EFAULT; 33 34 if (!strncmp(cmd, "start", len)) { 35 if (rproc->state == RPROC_RUNNING || 36 rproc->state == RPROC_ATTACHED) 37 return -EBUSY; 38 39 ret = rproc_boot(rproc); 40 } else if (!strncmp(cmd, "stop", len)) { 41 if (rproc->state != RPROC_RUNNING && 42 rproc->state != RPROC_ATTACHED) 43 return -EINVAL; 44 45 rproc_shutdown(rproc); 46 } else if (!strncmp(cmd, "detach", len)) { 47 if (rproc->state != RPROC_ATTACHED) 48 return -EINVAL; 49 50 ret = rproc_detach(rproc); 51 } else { 52 dev_err(&rproc->dev, "Unrecognized option\n"); 53 ret = -EINVAL; 54 } 55 56 return ret ? ret : len; 57 } 58 59 static long rproc_device_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) 60 { 61 struct rproc *rproc = container_of(filp->f_inode->i_cdev, struct rproc, cdev); 62 void __user *argp = (void __user *)arg; 63 s32 param; 64 65 switch (ioctl) { 66 case RPROC_SET_SHUTDOWN_ON_RELEASE: 67 if (copy_from_user(¶m, argp, sizeof(s32))) 68 return -EFAULT; 69 70 rproc->cdev_put_on_release = !!param; 71 break; 72 case RPROC_GET_SHUTDOWN_ON_RELEASE: 73 param = (s32)rproc->cdev_put_on_release; 74 if (copy_to_user(argp, ¶m, sizeof(s32))) 75 return -EFAULT; 76 77 break; 78 default: 79 dev_err(&rproc->dev, "Unsupported ioctl\n"); 80 return -EINVAL; 81 } 82 83 return 0; 84 } 85 86 static int rproc_cdev_release(struct inode *inode, struct file *filp) 87 { 88 struct rproc *rproc = container_of(inode->i_cdev, struct rproc, cdev); 89 int ret = 0; 90 91 if (!rproc->cdev_put_on_release) 92 return 0; 93 94 if (rproc->state == RPROC_RUNNING) 95 rproc_shutdown(rproc); 96 else if (rproc->state == RPROC_ATTACHED) 97 ret = rproc_detach(rproc); 98 99 return ret; 100 } 101 102 static const struct file_operations rproc_fops = { 103 .write = rproc_cdev_write, 104 .unlocked_ioctl = rproc_device_ioctl, 105 .compat_ioctl = compat_ptr_ioctl, 106 .release = rproc_cdev_release, 107 }; 108 109 int rproc_char_device_add(struct rproc *rproc) 110 { 111 int ret; 112 113 cdev_init(&rproc->cdev, &rproc_fops); 114 rproc->cdev.owner = THIS_MODULE; 115 116 rproc->dev.devt = MKDEV(MAJOR(rproc_major), rproc->index); 117 cdev_set_parent(&rproc->cdev, &rproc->dev.kobj); 118 ret = cdev_add(&rproc->cdev, rproc->dev.devt, 1); 119 if (ret < 0) 120 dev_err(&rproc->dev, "Failed to add char dev for %s\n", rproc->name); 121 122 return ret; 123 } 124 125 void rproc_char_device_remove(struct rproc *rproc) 126 { 127 cdev_del(&rproc->cdev); 128 } 129 130 void __init rproc_init_cdev(void) 131 { 132 int ret; 133 134 ret = alloc_chrdev_region(&rproc_major, 0, NUM_RPROC_DEVICES, "remoteproc"); 135 if (ret < 0) 136 pr_err("Failed to alloc rproc_cdev region, err %d\n", ret); 137 } 138