2020-07-29 10:40:00 -07:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Character device interface driver for Remoteproc framework .
*
* Copyright ( c ) 2020 , The Linux Foundation . All rights reserved .
*/
# include <linux/cdev.h>
# include <linux/compat.h>
# include <linux/fs.h>
# include <linux/module.h>
# include <linux/remoteproc.h>
# include <linux/uaccess.h>
# include <uapi/linux/remoteproc_cdev.h>
# include "remoteproc_internal.h"
# define NUM_RPROC_DEVICES 64
static dev_t rproc_major ;
static ssize_t rproc_cdev_write ( struct file * filp , const char __user * buf , size_t len , loff_t * pos )
{
struct rproc * rproc = container_of ( filp - > f_inode - > i_cdev , struct rproc , cdev ) ;
int ret = 0 ;
char cmd [ 10 ] ;
if ( ! len | | len > sizeof ( cmd ) )
return - EINVAL ;
ret = copy_from_user ( cmd , buf , len ) ;
if ( ret )
return - EFAULT ;
if ( ! strncmp ( cmd , " start " , len ) ) {
ret = rproc_boot ( rproc ) ;
} else if ( ! strncmp ( cmd , " stop " , len ) ) {
2022-02-13 14:12:42 -06:00
ret = rproc_shutdown ( rproc ) ;
2021-03-12 09:24:52 -07:00
} else if ( ! strncmp ( cmd , " detach " , len ) ) {
ret = rproc_detach ( rproc ) ;
2020-07-29 10:40:00 -07:00
} else {
dev_err ( & rproc - > dev , " Unrecognized option \n " ) ;
ret = - EINVAL ;
}
return ret ? ret : len ;
}
static long rproc_device_ioctl ( struct file * filp , unsigned int ioctl , unsigned long arg )
{
struct rproc * rproc = container_of ( filp - > f_inode - > i_cdev , struct rproc , cdev ) ;
void __user * argp = ( void __user * ) arg ;
s32 param ;
switch ( ioctl ) {
case RPROC_SET_SHUTDOWN_ON_RELEASE :
if ( copy_from_user ( & param , argp , sizeof ( s32 ) ) )
return - EFAULT ;
rproc - > cdev_put_on_release = ! ! param ;
break ;
case RPROC_GET_SHUTDOWN_ON_RELEASE :
param = ( s32 ) rproc - > cdev_put_on_release ;
if ( copy_to_user ( argp , & param , sizeof ( s32 ) ) )
return - EFAULT ;
break ;
default :
dev_err ( & rproc - > dev , " Unsupported ioctl \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static int rproc_cdev_release ( struct inode * inode , struct file * filp )
{
struct rproc * rproc = container_of ( inode - > i_cdev , struct rproc , cdev ) ;
2021-03-12 09:24:53 -07:00
int ret = 0 ;
if ( ! rproc - > cdev_put_on_release )
return 0 ;
2020-07-29 10:40:00 -07:00
2021-03-12 09:24:53 -07:00
if ( rproc - > state = = RPROC_RUNNING )
2020-07-29 10:40:00 -07:00
rproc_shutdown ( rproc ) ;
2021-03-12 09:24:53 -07:00
else if ( rproc - > state = = RPROC_ATTACHED )
ret = rproc_detach ( rproc ) ;
2020-07-29 10:40:00 -07:00
2021-03-12 09:24:53 -07:00
return ret ;
2020-07-29 10:40:00 -07:00
}
static const struct file_operations rproc_fops = {
. write = rproc_cdev_write ,
. unlocked_ioctl = rproc_device_ioctl ,
. compat_ioctl = compat_ptr_ioctl ,
. release = rproc_cdev_release ,
} ;
int rproc_char_device_add ( struct rproc * rproc )
{
int ret ;
cdev_init ( & rproc - > cdev , & rproc_fops ) ;
rproc - > cdev . owner = THIS_MODULE ;
rproc - > dev . devt = MKDEV ( MAJOR ( rproc_major ) , rproc - > index ) ;
cdev_set_parent ( & rproc - > cdev , & rproc - > dev . kobj ) ;
ret = cdev_add ( & rproc - > cdev , rproc - > dev . devt , 1 ) ;
if ( ret < 0 )
dev_err ( & rproc - > dev , " Failed to add char dev for %s \n " , rproc - > name ) ;
return ret ;
}
void rproc_char_device_remove ( struct rproc * rproc )
{
2021-06-14 19:21:10 -07:00
cdev_del ( & rproc - > cdev ) ;
2020-07-29 10:40:00 -07:00
}
void __init rproc_init_cdev ( void )
{
int ret ;
ret = alloc_chrdev_region ( & rproc_major , 0 , NUM_RPROC_DEVICES , " remoteproc " ) ;
if ( ret < 0 )
pr_err ( " Failed to alloc rproc_cdev region, err %d \n " , ret ) ;
}