📄 Linux kernel Character Device Drivers
tl;dr
- Character devices
- Major and Minor Numbers
- File operations
- Bringing device IDs and file operations together
- A character device driver example
- Testing the simple_char driver
Commands
cat /proc/devices
stat <file>
mknod <file_name> <type> <major_num> <minor_num>
dmesg -w
@host
aarch64-linux-gnu-gcc write_prog.c -o write_prog
scp write_prog root@<VM-IP-ADDRESS>:~/
@VM
root@localhost:~# ./write_prog simple_char_node
Notes
- Character devices
- Abstraction provided by Linux to support read/write devices with relatively small data transfers (one or a few bytes size).
- Abstracted as files in the file system and accessed through file access system calls.
- Example of devices: serial ports, keyboards, mice, etc.
- Example of character device files:
/dev/ttyS0,dev/input/mouse0,/dev/kmsg,/dev/zero.
- Major and Minor Numbers
- Files associated with character devices are special types of files.
- Allow users to interface with devices from the user space.
- Under the hood, device files are the device drivers that handle the system calls for them.
- The association between device files and devices is made with a device ID that consists of two parts: a major and a minor number
- The device ID (major/minor number) is used to choose which driver to run to support a particular device.
- File operations
- Character device drivers may implement functions to handle file access system calls.
- The syscalls implemented by a device driver are set into a struct file_operations object.
- There are several different file operations a character driver can implement.
- Basic system calls: open, close, read, write.
- Handled by open, release, read, and write functions stored in struct file_operations objects.
Tutorial steps
The following steps were executed to test the simple_char driver. This could be considered a basic example of a workflow for adding a module to the kernel, building, and installing it.
- Create an example character device
- Enable the device:
make -C "$IIO_TREE" menuconfig - Build linux image:
kw build --clean && kw build - Install the module:
mkdir "${VM_DIR}/arm64_rootfs" sudo guestmount --rw --add "${VM_DIR}/arm64_img.qcow2" --mount /dev/vda2 "${VM_DIR}/arm64_rootfs" sudo --preserve-env make -C "${IIO_TREE}" INSTALL_MOD_PATH="${VM_DIR}/arm64_rootfs" modules_install sudo guestunmount "${VM_DIR}/arm64_rootfs" - Start the VM:
sudo virsh start arm64 - Connect to the VM:
ssh root@$(sudo virsh net-dhcp-leases default | grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b') - Get information about the module:
modinfo simple_char - Load the module:
modprobe simple_char - See kernel logs:
dmesg | tail
Initially the device wasn’t writing to the buffer, giving me the following error:
root@localhost:~# ./write_prog simple_char_node
Error: 9wrote -1 bytes to buffer
But this was caused by a misconfiguration with the device interface (simple_char_node). By deleting and recreating it
with the correct major number (cat /proc/devices | grep simp) I was able to write and read using the device.
root@localhost:~# ./write_prog simple_char_node
wrote 256 bytes to buffer
root@localhost:~# ./read_prog simple_char_node
Read buffer: A new message for simple_char.