Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.10 124 lines 3.0 kB view raw
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 19static dev_t rproc_major; 20 21static 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 52static 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(&param, 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, &param, 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 79static 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 89static 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 96int 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 112void rproc_char_device_remove(struct rproc *rproc) 113{ 114 __unregister_chrdev(MAJOR(rproc->dev.devt), rproc->index, 1, "remoteproc"); 115} 116 117void __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}