Skip to content

Instantly share code, notes, and snippets.

@paranlee
Created August 3, 2022 13:11
Show Gist options
  • Save paranlee/fd49fef1e800fb99017cd521a79c3ff7 to your computer and use it in GitHub Desktop.
Save paranlee/fd49fef1e800fb99017cd521a79c3ff7 to your computer and use it in GitHub Desktop.
Proc Filesystem Example.
/**
* Linux Device Driver Tutorial Part 9 – Procfs in Linux
* - https://embetronicx.com/tutorials/linux/device-drivers/procfs-in-linux/
**/
/***************************************************************************//**
* \file driver.c
*
* \details Simple Linux device driver (procfs)
*
* \author EmbeTronicX
*
* \Tested with Linux raspberrypi 5.10.27-v7l-embetronicx-custom+
*
* *******************************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h> //kmalloc()
#include<linux/uaccess.h> //copy_to/from_user()
#include <linux/ioctl.h>
#include<linux/proc_fs.h>
/*
** I am using the kernel 5.10.27-v7l. So I have set this as 510.
** If you are using the kernel 3.10, then set this as 310,
** and for kernel 5.1, set this as 501. Because the API proc_create()
** changed in kernel above v5.5.
**
*/
#define LINUX_KERNEL_VERSION 510
#define WR_VALUE _IOW('a','a',int32_t*)
#define RD_VALUE _IOR('a','b',int32_t*)
int32_t value = 0;
char etx_array[20]="try_proc_array\n";
static int len = 1;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
static struct proc_dir_entry *parent;
/*
** Function Prototypes
*/
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
/*************** Driver Functions **********************/
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/***************** Procfs Functions *******************/
static int open_proc(struct inode *inode, struct file *file);
static int release_proc(struct inode *inode, struct file *file);
static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length,loff_t * offset);
static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t * off);
/*
** File operation sturcture
*/
static struct file_operations fops =
{
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.unlocked_ioctl = etx_ioctl,
.release = etx_release,
};
#if ( LINUX_KERNEL_VERSION > 505 )
/*
** procfs operation sturcture
*/
static struct proc_ops proc_fops = {
.proc_open = open_proc,
.proc_read = read_proc,
.proc_write = write_proc,
.proc_release = release_proc
};
#else //LINUX_KERNEL_VERSION > 505
/*
** procfs operation sturcture
*/
static struct file_operations proc_fops = {
.open = open_proc,
.read = read_proc,
.write = write_proc,
.release = release_proc
};
#endif //LINUX_KERNEL_VERSION > 505
/*
** This function will be called when we open the procfs file
*/
static int open_proc(struct inode *inode, struct file *file)
{
pr_info("proc file opend.....\t");
return 0;
}
/*
** This function will be called when we close the procfs file
*/
static int release_proc(struct inode *inode, struct file *file)
{
pr_info("proc file released.....\n");
return 0;
}
/*
** This function will be called when we read the procfs file
*/
static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length,loff_t * offset)
{
pr_info("proc file read.....\n");
if(len)
{
len=0;
}
else
{
len=1;
return 0;
}
if( copy_to_user(buffer,etx_array,20) )
{
pr_err("Data Send : Err!\n");
}
return length;;
}
/*
** This function will be called when we write the procfs file
*/
static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t * off)
{
pr_info("proc file wrote.....\n");
if( copy_from_user(etx_array,buff,len) )
{
pr_err("Data Write : Err!\n");
}
return len;
}
/*
** This function will be called when we open the Device file
*/
static int etx_open(struct inode *inode, struct file *file)
{
pr_info("Device File Opened...!!!\n");
return 0;
}
/*
** This function will be called when we close the Device file
*/
static int etx_release(struct inode *inode, struct file *file)
{
pr_info("Device File Closed...!!!\n");
return 0;
}
/*
** This function will be called when we read the Device file
*/
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
pr_info("Read function\n");
return 0;
}
/*
** This function will be called when we write the Device file
*/
static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
pr_info("Write Function\n");
return len;
}
/*
** This function will be called when we write IOCTL on the Device file
*/
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case WR_VALUE:
if( copy_from_user(&value ,(int32_t*) arg, sizeof(value)) )
{
pr_err("Data Write : Err!\n");
}
pr_info("Value = %d\n", value);
break;
case RD_VALUE:
if( copy_to_user((int32_t*) arg, &value, sizeof(value)) )
{
pr_err("Data Read : Err!\n");
}
break;
default:
pr_info("Default\n");
break;
}
return 0;
}
/*
** Module Init function
*/
static int __init etx_driver_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
pr_info("Cannot allocate major number\n");
return -1;
}
pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
/*Creating cdev structure*/
cdev_init(&etx_cdev,&fops);
/*Adding character device to the system*/
if((cdev_add(&etx_cdev,dev,1)) < 0){
pr_info("Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
pr_info("Cannot create the struct class\n");
goto r_class;
}
/*Creating device*/
if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
pr_info("Cannot create the Device 1\n");
goto r_device;
}
/*Create proc directory. It will create a directory under "/proc" */
parent = proc_mkdir("etx",NULL);
if( parent == NULL )
{
pr_info("Error creating proc entry");
goto r_device;
}
/*Creating Proc entry under "/proc/etx/" */
proc_create("etx_proc", 0666, parent, &proc_fops);
pr_info("Device Driver Insert...Done!!!\n");
return 0;
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev,1);
return -1;
}
/*
** Module exit function
*/
static void __exit etx_driver_exit(void)
{
/* Removes single proc entry */
//remove_proc_entry("etx/etx_proc", parent);
/* remove complete /proc/etx */
proc_remove(parent);
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
pr_info("Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <[email protected]>");
MODULE_DESCRIPTION("Simple Linux device driver (procfs)");
MODULE_VERSION("1.6");
/**
* Device Drivers, Part 16: Kernel Window — Peeping through /proc
* - https://www.opensourceforu.com/2012/03/device-drivers-kernel-window-peeping-through-proc/
*
* Kernel Window - Peeping through /proc
* - https://sysplay.github.io/books/LinuxDrivers/book/Content/Part16.html
**/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/jiffies.h>
static struct proc_dir_entry *parent, *file, *link;
static int state = 0;
int time_read(char *page, char **start, off_t off, int count, int *eof, void *data) {
int len, val;
unsigned long act_jiffies;
len = sprintf(page, "state = %d\n", state);
act_jiffies = jiffies - INITIAL_JIFFIES;
val = jiffies_to_msecs(act_jiffies);
switch (state) {
case 0:
len += sprintf(page + len, "time = %ld jiffies\n", act_jiffies);
break;
case 1:
len += sprintf(page + len, "time = %d msecs\n", val);
break;
case 2:
len += sprintf(page + len, "time = %ds %dms\n",
val / 1000, val % 1000);
break;
case 3:
val /= 1000;
len += sprintf(page + len, "time = %02d:%02d:%02d\n",
val / 3600, (val / 60) % 60, val % 60);
break;
default:
len += sprintf(page + len, "<not implemented>\n");
break;
}
len += sprintf(page + len, "{offset = %ld; count = %d;}\n", off, count);
return len;
}
int time_write(struct file *file, const char __user *buffer, unsigned long count, void *data) {
if (count > 2)
return count;
if ((count == 2) && (buffer[1] != '\n'))
return count;
if ((buffer[0] < '0') || ('9' < buffer[0]))
return count;
state = buffer[0] - '0';
return count;
}
static int __init proc_win_init(void) {
if ((parent = proc_mkdir("anil", NULL)) == NULL) {
return -1;
}
if ((file = create_proc_entry("rel_time", 0666, parent)) == NULL) {
remove_proc_entry("anil", NULL);
return -1;
}
file->read_proc = time_read;
file->write_proc = time_write;
if ((link = proc_symlink("rel_time_l", parent, "rel_time")) == NULL) {
remove_proc_entry("rel_time", parent);
remove_proc_entry("anil", NULL);
return -1;
}
link->uid = 0;
link->gid = 100;
return 0;
}
static void __exit proc_win_exit(void) {
remove_proc_entry("rel_time_l", parent);
remove_proc_entry("rel_time", parent);
remove_proc_entry("anil", NULL);
}
module_init(proc_win_init);
module_exit(proc_win_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("Kernel window /proc Demonstration Driver");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment