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 ret = rproc_boot(rproc); 36 } else if (!strncmp(cmd, "stop", len)) { 37 ret = rproc_shutdown(rproc); 38 } else if (!strncmp(cmd, "detach", len)) { 39 ret = rproc_detach(rproc); 40 } else { 41 dev_err(&rproc->dev, "Unrecognized option\n"); 42 ret = -EINVAL; 43 } 44 45 return ret ? ret : len; 46 } 47 48 static long rproc_device_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) 49 { 50 struct rproc *rproc = container_of(filp->f_inode->i_cdev, struct rproc, cdev); 51 void __user *argp = (void __user *)arg; 52 s32 param; 53 54 switch (ioctl) { 55 case RPROC_SET_SHUTDOWN_ON_RELEASE: 56 if (copy_from_user(¶m, argp, sizeof(s32))) 57 return -EFAULT; 58 59 rproc->cdev_put_on_release = !!param; 60 break; 61 case RPROC_GET_SHUTDOWN_ON_RELEASE: 62 param = (s32)rproc->cdev_put_on_release; 63 if (copy_to_user(argp, ¶m, sizeof(s32))) 64 return -EFAULT; 65 66 break; 67 default: 68 dev_err(&rproc->dev, "Unsupported ioctl\n"); 69 return -EINVAL; 70 } 71 72 return 0; 73 } 74 75 static int rproc_cdev_release(struct inode *inode, struct file *filp) 76 { 77 struct rproc *rproc = container_of(inode->i_cdev, struct rproc, cdev); 78 int ret = 0; 79 80 if (!rproc->cdev_put_on_release) 81 return 0; 82 83 if (rproc->state == RPROC_RUNNING) 84 rproc_shutdown(rproc); 85 else if (rproc->state == RPROC_ATTACHED) 86 ret = rproc_detach(rproc); 87 88 return ret; 89 } 90 91 static const struct file_operations rproc_fops = { 92 .write = rproc_cdev_write, 93 .unlocked_ioctl = rproc_device_ioctl, 94 .compat_ioctl = compat_ptr_ioctl, 95 .release = rproc_cdev_release, 96 }; 97 98 int rproc_char_device_add(struct rproc *rproc) 99 { 100 int ret; 101 102 cdev_init(&rproc->cdev, &rproc_fops); 103 rproc->cdev.owner = THIS_MODULE; 104 105 rproc->dev.devt = MKDEV(MAJOR(rproc_major), rproc->index); 106 cdev_set_parent(&rproc->cdev, &rproc->dev.kobj); 107 ret = cdev_add(&rproc->cdev, rproc->dev.devt, 1); 108 if (ret < 0) 109 dev_err(&rproc->dev, "Failed to add char dev for %s\n", rproc->name); 110 111 return ret; 112 } 113 114 void rproc_char_device_remove(struct rproc *rproc) 115 { 116 cdev_del(&rproc->cdev); 117 } 118 119 void __init rproc_init_cdev(void) 120 { 121 int ret; 122 123 ret = alloc_chrdev_region(&rproc_major, 0, NUM_RPROC_DEVICES, "remoteproc"); 124 if (ret < 0) 125 pr_err("Failed to alloc rproc_cdev region, err %d\n", ret); 126 } 127