Linux Character Device Driver Example

Linux Character Device Driver Example

globalmem

Looking at Linux device driver development details, the chapter on character device drivers provides test code and application programs that are very helpful for beginners.

The reason for writing this article is that I noticed I had not yet written about character devices, despite having previously published articles. As for the role of the structures mentioned, there are many detailed articles explaining them, so I won’t go into deeper discussion.

The code is on GitHub; click the link below to read the original text directly.

https://github.com/weiqifa0/globalmem/blob/main/README.md

Putting this part on GitHub is also beneficial, as it allows for the addition and deletion of items later, making it much easier to use when needed.

When discussing character device drivers, it is necessary to understand their structures and header files, as most of the later encapsulations still rely on operating the elements within these structures.

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_CDEV_H
#define _LINUX_CDEV_H

#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>
#include <linux/device.h>

struct file_operations;
struct inode;
struct module;

struct cdev {
 struct kobject kobj; /* Embedded kobject structure for future applications, will also generate related device files in sys */
 struct module *owner; /* The module it belongs to, normally this module THIS_MODULE */
 const struct file_operations *ops; /* File operation structure, device is also a file */
 struct list_head list; /* Head of the character device linked list */
 dev_t dev; /* Device number */
 unsigned int count;
} __randomize_layout;
/* Initialize cdev and establish connection with file_operation */
void cdev_init(struct cdev *, const struct file_operations *);
/* Allocate memory for cdev */
struct cdev *cdev_alloc(void);

void cdev_put(struct cdev *p);

int cdev_add(struct cdev *, dev_t, unsigned);

void cdev_set_parent(struct cdev *p, struct kobject *kobj);
int cdev_device_add(struct cdev *cdev, struct device *dev);
void cdev_device_del(struct cdev *cdev, struct device *dev);

void cdev_del(struct cdev *);

void cd_forget(struct inode *);

#endif

Linux Character Device Driver Example

Loading Kernel Module insmod globalmem.ko Requires Modifications.

insmod: can’t insert ‘globalmem.ko’: Device or resource busy

Reason for the Error:

The module uses a statically allocated device number, which is already occupied by another device in the system. To check for unoccupied device numbers, you need to look under /proc/devices.

Method to check device numbers:

# cat /proc/devices

Kernel Version

#uname -a
Linux bsp-ubuntu1804 4.15.0-117-generic #118-Ubuntu SMP Fri Sep 4 20:02:41 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

After loading the module, use lsmod to check the module

weiqifa@bsp-ubuntu1804:~/c/globalmem$ sudo insmod globalmem.ko
weiqifa@bsp-ubuntu1804:~/c/globalmem$ lsmod |grep global
globalmem              16384  0
weiqifa@bsp-ubuntu1804:~/c/globalmem$

weiqifa@bsp-ubuntu1804:~/c/globalmem$ cat /proc/devices |grep global
230 globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$

Create Device File Node

When using mknod to create a device node, the parameters following it need to correspond to what we see under /proc/devices, which is the major device number we applied for in the driver.

weiqifa@bsp-ubuntu1804:~/c/globalmem$ sudo mknod /dev/globalmem c 230 0
weiqifa@bsp-ubuntu1804:~/c/globalmem$ ls /dev/globalmem -al
crw-r--r-- 1 root root 230, 0 Dec 22 16:19 /dev/globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$

Using Commands to Read and Write Device Files

The echo and cat commands in Linux are very useful; these two commands can help debug read and write operations on devices without writing code.

weiqifa@bsp-ubuntu1804:~/c/globalmem$ sudo chmod 777 /dev/globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$ sudo echo "linux" > /dev/globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$ cat /dev/globalmem
linux
cat: /dev/globalmem: No such device or address
weiqifa@bsp-ubuntu1804:~/c/globalmem$ cat /dev/globalmem
linux
cat: /dev/globalmem: No such device or address
weiqifa@bsp-ubuntu1804:~/c/globalmem$ sudo echo "linuxgdb" > /dev/globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$ cat /dev/globalmem
linuxgdb
cat: /dev/globalmem: No such device or address
weiqifa@bsp-ubuntu1804:~/c/globalmem$

Reading and Writing Device Files via Code

The code can be found in the link to read the original text below.

weiqifa@bsp-ubuntu1804:~/c/globalmem$ gcc app-main.c && ./a.out
str:LINUX,GDB
weiqifa@bsp-ubuntu1804:~/c/globalmem$

Setting Major Device Number via Passed Parameters

Kernel module parameters are a relatively niche knowledge point, mainly because we rarely use this parameter when working on projects, but in reality, this parameter is very useful.

We can treat the kernel module like a main function, as the main function can receive parameters, and the kernel module can also accept parameters upon loading.

Below is how to pass the major device number to the kernel module, but note that this major device number must not be occupied.

weiqifa@bsp-ubuntu1804:~/c/globalmem$ sudo insmod globalmem.ko globalmem_major=231
weiqifa@bsp-ubuntu1804:~/c/globalmem$ cat /proc/devices |grep globalmem
231 globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$

Adding Automatic Device Node Creation in Driver File

Manually creating device file nodes each time is always cumbersome, and in actual device driver development, one would not manually create device nodes in such a rudimentary manner.

Of course, the clever kernel provides an interface that allows us to register the device file node when registering the driver.

Specific code can be found in the globalmem2.c file.

weiqifa@bsp-ubuntu1804:~/c/globalmem$ chmod 777 globalmem.ko
weiqifa@bsp-ubuntu1804:~/c/globalmem$ sudo insmod globalmem.ko
[sudo] password for weiqifa:
weiqifa@bsp-ubuntu1804:~/c/globalmem$ ls /dev/globalmem
/dev/globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$ ls /dev/globalmem -al
crw------- 1 root root 238, 0 Dec 22 17:18 /dev/globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$

/* Modify permissions before performing exclusive write operations */
weiqifa@bsp-ubuntu1804:~/c/globalmem$ sudo chmod 777 /dev/globalmem
weiqifa@bsp-ubuntu1804:~/c/globalmem$ gcc app-main.c && ./a.out
write data ok!
str:LINUX,GDB
weiqifa@bsp-ubuntu1804:~/c/globalmem$

Using lseek to Operate File Position

The corresponding file is app-main2.c

weiqifa@bsp-ubuntu1804:~/c/globalmem$ gcc app-main2.c && ./a.out
file ret:0
write data ok! fd:3
str:123456789ABCDEF10111213141516171819201617181920
lseek:1
str:23456789ABCDEF10111213141516171819201617181920
weiqifa@bsp-ubuntu1804:~/c/globalmem$
Recommended Reading:
Collection | Summary of Linux Articles
Collection | Programmer’s Life
Collection | C Language
My Knowledge Circle

Linux Character Device Driver Example

Leave a Comment