linux/drivers/remoteproc/remoteproc_cdev.c
<<
>>
Prefs
   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                    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
  59static 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(&param, 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, &param, 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
  86static 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
 102static 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
 109int 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
 125void rproc_char_device_remove(struct rproc *rproc)
 126{
 127        cdev_del(&rproc->cdev);
 128}
 129
 130void __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