How to make an embedded Linux device driver (8)

8th: Interface for debugfs

About this series

This is a HowTo article for developing embedded Linux device drivers as kernel modules. All the content of this article can be run on a Raspberry Pi.

-1st time: Build environment preparation and simple kernel module creation -Second time: System call handler and driver registration (static method) -Third time: System call handler and driver registration (dynamic method) -4th: Read / write implementation and memory story -5th time: Implementation of GPIO driver for Raspberry Pi -6th time: Implementation of ioctl -7th time: Interface for procfs --__ 8th time: Interface for debugfs <--------------------- This time Contents __ -9th time: Call a function of another kernel module / Use GPIO control function -10th time: Create a device driver using I2C -11th time: Add I2C device to device tree -12th time: Load the created device driver at startup

The entire source code that appears in this article

https://github.com/take-iwiw/DeviceDriverLesson/tree/master/08_01

Contents of this time

Last time, I used procfs to create an interface for debugging device drivers. However, procfs is not a good place to use it for debugging, as it places information about the process. It is recommended to use debugfs for debugging. (Thank you for pointing out from @rarul.)

This time, I will try to implement the same thing as last time using this debugfs. In particular,

/sys/kernel/debug/MyDevice/prm1

Allows you to read and write debug parameters by accessing. Please note that this path may change depending on the environment.

Implementation of interface for debugfs

There are two main ways to create a debugfs interface. I will describe each method.

Method 1: Use file_operations

To create an interface for debugfs, just register the file name and the function you want to call during read / write with the function debugfs_create_file () where the driver is loaded (insmod). When registering, set the read / write handler function in the struct file_operations table. This is exactly the same as the previous procfs and regular device driver handler registration.

However, this will create a file for debugfs access in the root directory of debugfs. Of course, there are also files for other kernel modules, so organize them in a directory. Use debugfs_create_dir () to create a directory for debugfs. By putting the return value (entry) of debugfs_create_dir () in the third argument of debugfs_create_file (), the file will be created under the created directory.

Method 2: Use a helper function

Sometimes you just want to read and write parameters for debugging, but defining a read / write function every time can be tedious. If you only need read / write operations, you can create debugfs with just one helper function. For example, if you want to access a 32-bit variable, use debugfs_create_u32 () (the definition was unsigned, but you could also input and output negative numbers). If you want to read and write in hexadecimal, use debugfs_create_x32 (). There are also bool types, register sets (addresses and numbers), and functions for blobs that can be used for binary data. For the time being, I will use only 32-bit numbers.

End processing

You need to delete the debugfs file you created when you unload the kernel. To remove it, use debugfs_remove (). You can now delete the created file. However, it is troublesome to erase them one by one. Above all, it is troublesome to remember the entry information when it was created. For that, debugfs_remove_recursive () is provided. By putting the entry (return value of debugfs_create_dir ()) of the created directory in this function, the files under it will be deleted recursively.

code

The code looks like this: It has debug_prm1 statically as a parameter for debugging. Make your own directory name under the debugfs root directory "MyDevice" and keep the entry information in debug_entry_dir.

myDeviceDriver.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/debugfs.h>

/***Information about this device***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDevice"				/* /proc/Device name displayed on devices etc.*/

/*Debug variables*/
struct dentry *debug_entry_dir;		/*debugfs directory entry*/
static int debug_prm1;				/*Debug parameters(for test) */
static int  debug_read_size = 0;	/*Number of bytes to read in one open*/

/* /sys/kernel/debug/MyDevice/debug_Function called when accessing prm1*/
static int mydevice_debug_open(struct inode *inode, struct file *file)
{
	printk("mydevice_proc_open\n");
	debug_read_size = 4;	//Read 4 bytes at a time
	return 0;
}

/* /sys/kernel/debug/MyDevice/debug_Function called when reading prm1*/
static ssize_t mydevice_debug_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	printk("mydevice_proc_read\n");

	if (debug_read_size > 0) {
		/*When there is still data to output*/
		/*Integer type number that is held(debug_prm1)Is output as a character string*/
		int len;
		len = sprintf(buf, "%d\n", debug_prm1);	//Actually copy_to_should be user
		debug_read_size -= 4;
		return len;
	} else {
		return 0;
	}
}

/* /sys/kernel/debug/MyDevice/debug_Function called when writing prm1*/
static ssize_t mydevice_debug_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	printk("mydevice_proc_write\n");

	/*The entered character string is an integer type number(debug_prm1)Hold as*/
	sscanf(buf, "%d", &debug_prm1);			//Actually copy_from_should be user
	return count;	
}

/*Handler table for debugfs*/
static struct file_operations debug_debug_prm1_fops = {
	.owner = THIS_MODULE,
	.open  = mydevice_debug_open,
	.read  = mydevice_debug_read,
	.write = mydevice_debug_write,
};

/*Road(insmod)Functions sometimes called*/
static int mydevice_init(void)
{
	printk("mydevice_init\n");

	/*Create a directory for debufs*/
	debug_entry_dir = debugfs_create_dir(DRIVER_NAME, NULL);
	if (debug_entry_dir == NULL) {
		printk(KERN_ERR "debugfs_create_dir\n");
		return -ENOMEM;
	}

	/*Method 1:Handler table registration method*/
	debugfs_create_file("prm1", S_IRUGO | S_IWUGO, debug_entry_dir, NULL, &debug_debug_prm1_fops);

	/*Method 2:Helper function method*/
	debugfs_create_u32("_prm1", S_IRUGO | S_IWUGO, debug_entry_dir, &debug_prm1);
	debugfs_create_x32("_prm1_hex", S_IRUGO | S_IWUGO, debug_entry_dir, &debug_prm1);

	return 0;
}

/*Unload(rmmod)Functions sometimes called*/
static void mydevice_exit(void)
{
	printk("mydevice_exit\n");

	/*Get rid of for debufs(Child files are also deleted automatically) */
	debugfs_remove_recursive(debug_entry_dir);
}

module_init(mydevice_init);
module_exit(mydevice_exit);

With method 1, you had to define an entry function or handler table, but with method 2, you end up with one line. Of course, method 2 is easy, but if you need to process something at the same time as rewriting the parameters, it seems that you need to use method 1. (For example, when changing the register (parameter) of the device connected by i2c.)

Try reading and writing parameters via debugfs

Build and load with the following command.

make
sudo insmod  MyDeviceModule.ko
sudo ls /sys/kernel/debug/MyDevice
prm1  _prm1  _prm1_hex

Then, you can see that prm1, _prm1, and _prm1_hex are created under / sys / kernel / debug / MyDevice. prm1 is implemented by method 1, and _prm1 and _prm1_hex are implemented by method 2. All access to the same variable. (Since the name was implemented like this, it just became prm1 and has no particular meaning.)

sudo bash -c 'echo 12 >  /sys/kernel/debug/MyDevice/prm1'
sudo cat /sys/kernel/debug/MyDevice/prm1
12
sudo cat /sys/kernel/debug/MyDevice/_prm1
12
sudo cat /sys/kernel/debug/MyDevice/_prm1_hex
0x0000000c

sudo bash -c 'echo 13 >  /sys/kernel/debug/MyDevice/_prm1'
sudo bash -c 'echo 0x0f >  /sys/kernel/debug/MyDevice/_prm1_hex'

After that, you can read and write with cat and echo. The result will be the same because all the values of the same variable are input and output. It seems that 0x is automatically added when it is a hexadecimal number.

About access rights

This may only be a Raspberry Pi. This time, when creating each debugfs file, I specified S_IRUGO | S_IWUGO as the access right. This should be readable and writable by all users. But in reality, I had to add sudo when running. This is because general user access rights are not granted to the parent directories / sys, / sys / kernel, and / sys / kernel / debug.

I think this can be changed by changing the mount options for these directories (probably somewhere in the script that runs at startup). But I haven't followed it deeply and I don't think it should be changed.

Recommended Posts

How to make an embedded Linux device driver (11)
How to make an embedded Linux device driver (8)
How to make an embedded Linux device driver (1)
How to make an embedded Linux device driver (4)
How to make an embedded Linux device driver (7)
How to make an embedded Linux device driver (2)
How to make an embedded Linux device driver (3)
How to make an embedded Linux device driver (6)
How to make an embedded Linux device driver (5)
How to make an embedded Linux device driver (10)
How to make an embedded Linux device driver (9)
How to make an embedded Linux device driver (12) (Complete)
[Blender x Python] How to make an animation
How to make Linux compatible with Japanese keyboard
How to make Yubico Yubikey recognized in Manjaro Linux
How to make an interactive CLI tool in Golang
How to make an HTTPS server with Go / Gin
[Python] How to make an adjacency matrix / adjacency list [Graph theory]
How to make a hacking lab-Kali Linux (2020.1) VirtualBox 64-bit Part 2-
How to make a hacking lab-Kali Linux (2020.1) VirtualBox 64-bit edition-
How to make a Python package (written for an intern)
How to create an ISO file (CD image) on Linux
How to make a Japanese-English translation
How to make a slack bot
How to install wkhtmltopdf (Amazon Linux2)
How to create an email user
How to install VMware-Tools on Linux
How to make a crawler --Advanced
How to make a recursive function
How to install MBDyn (Linux Ubuntu)
How to make a deadman's switch
[Blender] How to make a Blender plugin
[Blender] How to make Blender scripts multilingual
How to make a crawler --Basic
How to build MongoDB C driver
How to check Linux OS version
How to make a string into an array or an array into a string in Python
How to get the printer driver for Oki Mac into Linux
How to make Word Cloud characters monochromatic
How to build my own Linux server
How to make Selenium as light as possible
[Linux] How to subdivide files and folders
How to make an artificial intelligence LINE bot with Flask + LINE Messaging API
How to install aws-session-manager-plugin on Manajro Linux
[Python] How to make a class iterable
python3 How to install an external module
How to create an NVIDIA Docker environment
How to convert Python to an exe file
I want to know how LINUX works!
[Linux] How to use the echo command
How to use the Linux grep command
How to update php on Amazon linux 2
How to display emoji on Manjaro Linux
How to install packages on Alpine Linux
[Cocos2d-x] How to make Script Binding (Part 2)
How to install Anisble on Amazon Linux 2
How to operate Linux from the console
How to install Windows Subsystem For Linux
How to power off Linux with Ultra96-V2
How to update security on CentOS Linux 8
I want to make an automation program!