How to make an embedded Linux device driver (2)

Second time: System call handler and driver registration (static method)

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 --__ 2nd time: System call handler and driver registration (static method) <------------ --------- Contents of this time __ -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 -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/02_01

Contents of this time

Allow users access to device drivers in the old (?) Way. Last time, I created a halo world-like thing that prepares only the handler when the module is loaded / unloaded (insmod / rmmod). This time, open / close from the actual program or shell so that the value can be read / written. To do this, implement processing for system calls such as open / close / read / write. In addition, the user registers the device in the kernel in order to access this device driver as a device file (/ dev / XXX). This time, the device is fixedly registered statically. This method is old and seems to be deprecated now, but I'll take this step to understand the content.

Makefile for build

Since there is only one file, prepare the following Makefile. Create MyDeviceModule.ko from the source code myDeviceDriver.c.

CFILES = myDeviceDriver.c

obj-m := MyDeviceModule.o
MyDeviceModule-objs := $(CFILES:.c=.o)

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

Device driver source code

myDeviceDriver.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <asm/uaccess.h>

#define DRIVER_NAME "MyDevice_NAME"
#define DRIVER_MAJOR 63

/*Function called at open*/
static int myDevice_open(struct inode *inode, struct file *file)
{
	printk("myDevice_open\n");
	return 0;
}

/*Function called at close*/
static int myDevice_close(struct inode *inode, struct file *file)
{
	printk("myDevice_close\n");
	return 0;
}

/*Function called when reading*/
static ssize_t myDevice_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	printk("myDevice_read\n");
	buf[0] = 'A';
	return 1;
}

/*Function called when writing*/
static ssize_t myDevice_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	printk("myDevice_write\n");
	return 1;
}

/*Handler table for various system calls*/
struct file_operations s_myDevice_fops = {
	.open    = myDevice_open,
	.release = myDevice_close,
	.read    = myDevice_read,
	.write   = myDevice_write,
};

/*Road(insmod)Functions sometimes called*/
static int myDevice_init(void)
{
	printk("myDevice_init\n");
	/*★ Register this driver in the kernel*/
	register_chrdev(DRIVER_MAJOR, DRIVER_NAME, &s_myDevice_fops);
	return 0;
}

/*Unload(rmmod)Functions sometimes called*/
static void myDevice_exit(void)
{
	printk("myDevice_exit\n");
	unregister_chrdev(DRIVER_MAJOR, DRIVER_NAME);
}

module_init(myDevice_init);
module_exit(myDevice_exit);

Definition of handler for system call

Define a handler function for system calls (open, close, read, write) from the user. For the time being, I'll just log or return a fixed value. Store these functions in s_myDevice_fops.

Register the driver in the kernel

When the module is loaded (that is, in myDevice_init), the register_chrdev function registers this device driver in the kernel as a character device. "The major number of this device driver is DRIVER_MAJOR (63) and the name is DRIVER_NAME (" MyDevice_NAME "). The handler table for each system call is in s_myDevice_fops, "he says. The major number specified here is a very important number used to identify the device.

By the way, major numbers 60-63, 120-127, 240-254 seem to be reserved numbers for local experiments. Therefore, I used 63 this time.

Operation check

Build and load

Build and load with the following command.

make
sudo insmod MyDeviceModule.ko

After that, check the registered device list. Then, as registered in the code, you can see that this device ("MyDevice_NAME") is registered as the measure number 63 in the place of Character devices.

cat /proc/devices
Character devices:
  1 mem
abridgement
 63 MyDevice_NAME

Create a device file for user access

Users typically access device drivers using device files. I will prepare the device file. Use the mknod command for that. The first argument is the name of the device file. This is OK with anything. The second argument is the device type. This time it is a character device, so set c. The third argument is the major number of the device corresponding to the device file to be created. This should match the device driver number you created earlier. This time it was 63, so specify 63. The 4th argument is a minor number. It is used to distinguish when creating multiple device files for the same device. Put 1 for the time being.

After creating / dev / myDevice with mknod, change the access right so that anyone can access it.

sudo mknod /dev/myDevice c 63 1
sudo chmod 666 /dev/myDevice

ls -la /dev
crw-rw-rw-  1 root root    63,   1 Dec 17 23:08 myDevice

Try reading and writing from the shell

To check the operation, you can write C code that opens the / dev / myDevice created this time, reads / writes it, and closes it, but for the sake of simplicity, check it from the shell.

First, check write with the following command

echo "a" > /dev/myDevice
dmesg

If you look at the log with dmesg after issuing the command, you can see that the implemented write function is called. I think it's called twice because of'a'and'\ 0'. Also, open and close are called automatically.

[11974.888831] myDevice_open
[11974.888934] myDevice_write
[11974.888944] myDevice_write
[11974.888968] myDevice_close

Then, check the read with the following command.

cat /dev/myDevice

If you type this command,'A' will be output endlessly on the console, so please stop it with Ctrl-c. This is because the myDevice_read function always returns a value. In fact, it should return 0 when there are no more values to read.

End processing

When you are finished using it, use the following command to unload the device driver and delete the device file. Since it is a device driver, I don't think it's an actual use case to finish using it, but I think it will be used when debugging. Once the device file (/ dev / myDevice) is created, the kernel will call the newly loaded device driver as long as the major number is the same no matter how many device drivers are rmmod and insmod. It seems that it will come out.

sudo rmmod MyDeviceModule
sudo rm /dev/myDevice

Recommended Posts

How to make an embedded Linux device driver (11)
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
python3 How to install an external module
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!
How to install php7.4 on Linux (Ubuntu)
How to make multi-boot USB (Windows 10 compatible)
How to make a Backtrader custom indicator
How to make a Pelican site map