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