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 <--------------------- This time Contents __ -8th time: Interface for debugfs -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
https://github.com/take-iwiw/DeviceDriverLesson/tree/master/07_01
Until last time, we implemented ioctl in addition to the basic system calls (open, close, read, write). This almost covers the minimum required interfaces for a device driver (skip select / poll).
In fact, if you are developing to control a device (eg a sensor or actuator), you may want to change or get the parameters for debugging. It would be very convenient to be able to do this from the shell. This can be achieved by reading / writing to files in the proc file system (procfs). For example, suppose you create the following file.
You can check the sensor value by reading the value of / proc / mydevice_sensor0
(for example, cat
), or you can control the motor by writing the value to / proc / mydevice_motor0
(for example, ʻecho). ). Basically, it is the same as read / write of the device driver that I have made so far. But until now it was read / write to the device file (
/ dev / mydevice`). This time we will do it for the procfs file. You can make as many as you like. This is useful when you have just one device and many parameters you want to check.
In the comments, @rarul pointed out. It seems that procfs is only used to place process information, and debugfs is recommended for debugging purposes as described above. I will describe debugfs next time. It shouldn't be used for debugging, but I think it's a good way to create procfs, so I'll leave this article as it is.
To create an interface for procfs, simply register the file name and the function you want to call during read / write with the function proc_create
where the driver is loaded (insmod). When registering, set the read / write handler function in the struct file_operations
table. In fact, this is exactly the same procedure as registering a handler for a normal device driver. The only difference is the function used. Use the remove_proc_entry
function to remove it.
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>
/***Information about this device***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDevice" /* /proc/Device name displayed on devices etc.*/
#define PROC_NAME "MyDevice_test" /* /The name of procfs to create in proc*/
/*procfs test variables*/
static char proc_test_string[16];
static int flag_read = 0;
/* /proc/MyDevice_Function called when accessing test*/
static int mydevice_proc_open(struct inode *inode, struct file *file)
{
printk("mydevice_proc_open\n");
flag_read = 0;
return 0;
}
/* /proc/MyDevice_Function called when reading test*/
static ssize_t mydevice_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk("mydevice_proc_read\n");
if (flag_read == 0) {
int len;
len = sprintf(buf, "%s\n", proc_test_string);
flag_read = 1;
return len;
} else {
return 0;
}
}
/* /proc/MyDevice_Function called when writing test*/
static ssize_t mydevice_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
printk("mydevice_proc_write\n");
if (count > sizeof(proc_test_string)) count = sizeof(proc_test_string) - 1;
if (copy_from_user(proc_test_string, buf, count)) {
return -EFAULT;
}
proc_test_string[count] = '\0';
return count;
}
/*Handler table for procfs*/
static struct file_operations mydevice_proc_fops = {
.owner = THIS_MODULE,
.open = mydevice_proc_open,
.read = mydevice_proc_read,
.write = mydevice_proc_write,
};
/*Road(insmod)Functions sometimes called*/
static int mydevice_init(void)
{
printk("mydevice_init\n");
struct proc_dir_entry *entry;
/*Create procfs*/
entry = proc_create(PROC_NAME, S_IRUGO | S_IWUGO, NULL, &mydevice_proc_fops);
if (entry == NULL) {
printk(KERN_ERR "proc_create\n");
return -ENOMEM;
}
return 0;
}
/*Unload(rmmod)Functions sometimes called*/
static void mydevice_exit(void)
{
printk("mydevice_exit\n");
/*Get rid of procfs*/
remove_proc_entry(PROC_NAME, NULL);
}
module_init(mydevice_init);
module_exit(mydevice_exit);
Let's create a file called / proc / MyDevice_test
. The processing when this file is read (mydevice_proc_read
), the processing when writing (mydevice_proc_write
), and the processing when opening (mydevice_proc_open
) are implemented. The process at open is always called every time you read or write from the shell. I think it's okay without it. In fact, close is omitted.
mydevice_proc_write
keeps the user-set string in an internal static variable (proc_test_string
). Returns the contents held by mydevice_proc_read
. I want you to call it only once per reading (cat
), so I manage the flag and clear it at the time of open. I think there is a better way to do this.
When loading the module, use the proc_create
function to create a / proc / MyDevice_test
file. S_IRUGO | S_IWUGO
is the same as 0666, giving all users RW access.
Build and load with the following command.
make
sudo insmod MyDeviceModule.ko
ls /proc/MyDevice_test
/proc/MyDevice_test
echo "abc" > /proc/MyDevice_test
cat /proc/MyDevice_test
abc
After loading, / proc / MyDevice_test
will be created. On the other hand, write the value with echo. After that, when you read it with cat, the value you wrote earlier is output.
The content of this article is in line with the content of "Linux Device Driver Programming (Yutaka Hirata)".
The book used create_proc_entry
to register with the proc filesystem. However, it is now obsolete. This article uses proc_create
instead.
As you pointed out in the comments, procfs essentially exchanges information about processes. As mentioned in the next article, debugfs exchanges information for debugging.
Apart from these, there is a mechanism for exchanging parameters with modules. You can check the module information and parameters as sysfs that can be placed under the / sys directory. It looks the same as procfs, but procfs is the old way, and as I wrote in this article, it's free to be added by developer developers. As a result, it has become chaotic. sysfs must be registered in the correct manner. And each file is organized and managed by directory.
There are various methods, but if you just want to show the parameters, you can do it with a helper macro called module_param ()
. This allows you to set parameters and read the values of parameters (variables) when loading a module. However, it seems that the parameters cannot be changed after loading the kernel.
I will actually use it. All you have to do is declare the variable and read module_param ()
. An example of using the variable param1
as a parameter is shown below.
static int param1 = 10;
module_param(param1, int, S_IRUGO);
You can check the value of param1
by reading / sys / module / MyDeviceModule / parameters / param1
. You can also specify a value during insmod.
sudo insmod MyDeviceModule.ko
cat /sys/module/MyDeviceModule/parameters/param1
10
sudo rmmod MyDeviceModule
sudo insmod MyDeviceModule.ko param1=20
cat /sys/module/MyDeviceModule/parameters/param1
20
Recommended Posts