An example of linux driver coded virtual character device

  • 2020-05-17 07:28:21
  • OfStack

linux driver writing

Preface:

Yesterday, we talked about some simple module writing methods, but after all, we did not involve the writing content of the device. Today, we can learn about the relevant content of 1, and use an example to illustrate how the device is written on Linux. Although I don't specialize in linux drivers, I often hear from my friends. In the letter, a lot of friends do driver is not very satisfied with their work, think their job is to copy the code to copy, or to change, no technical content. Many of my friends have this idea, and I think it is mainly due to their lack of understanding of their work. If possible, here are some questions to ask ourselves:

(1) do I really understand the device-driven process? Can I start at 0 and write a single driver?
(2) do I really understand the process of initialization, shutdown and operation of the device?
(3) is the current device driving process reasonable and can it be improved?
(4) for the api calls involved in kernel development, do I really know and understand the differences in their use?
(5) if the device I want to drive is only running in a front and background system, do I have the confidence to get it up and running without the help of the framework?

Of course, the above content is only my personal ideas, not necessarily all right. However, it is important to know why, and to understand the strengths and weaknesses of the current development process so as to truly grasp and understand the nature of what drives development. This sounds a little mysterious, in fact, it is also very simple, is to have a spirit of inquiry, continuous improvement, so as to do their own work. Because we are learning driven on pc linux, we don't have a real external device to use at the moment, but that doesn't stop us from learning at all. Through the timer, process, we can simulate the real equipment needs, so for the system, it does not matter the real equipment, fake equipment, the basic processing process for it is a kind of. As long as you do step by step, you can certainly understand the development project of linux drive equipment.

Now, to illustrate, we can write a simple char device driver code named char.c.


#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/fs.h> 
#include <linux/cdev.h> 
 
static struct cdev chr_dev; 
static dev_t ndev; 
 
static int chr_open(struct inode* nd, struct file* filp) 
{ 
  int major ; 
  int minor; 
   
  major = MAJOR(nd->i_rdev); 
  minor = MINOR(nd->i_rdev); 
   
  printk("chr_open, major = %d, minor = %d\n", major, minor); 
  return 0; 
} 
 
static ssize_t chr_read(struct file* filp, char __user* u, size_t sz, loff_t* off) 
{ 
  printk("chr_read process!\n"); 
  return 0; 
} 
 
struct file_operations chr_ops = { 
  .owner = THIS_MODULE, 
  .open = chr_open, 
  .read = chr_read 
}; 
 
static int demo_init(void) 
{ 
  int ret; 
   
  cdev_init(&chr_dev, &chr_ops); 
  ret = alloc_chrdev_region(&ndev, 0, 1, "chr_dev"); 
  if(ret < 0 ) 
  { 
    return ret; 
  } 
   
  printk("demo_init(): major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev)); 
  ret = cdev_add(&chr_dev, ndev, 1); 
  if(ret < 0) 
  { 
    return ret; 
  } 
   
  return 0; 
} 
 
static void demo_exit(void) 
{ 
  printk("demo_exit process!\n"); 
  cdev_del(&chr_dev); 
  unregister_chrdev_region(ndev, 1); 
} 
 
module_init(demo_init); 
module_exit(demo_exit); 
 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("feixiaoxing@163.com"); 
MODULE_DESCRIPTION("A simple device example!"); 

The functions in module_init are the functions that are handled when the module is loaded, and the functions that the module unloads are in module_exit. Each device should correspond to a basic device data. Of course, in order to register this device in the whole system, we also need to allocate 1 device node. alloc_chrdev_region completes such a function. By the time cdev_add arrives, the entire device registration process is complete, and it's as simple as that. Of course, in order to write this file, we also need to write an Makefile file,


ifneq ($(KERNELRELEASE),) 
obj-m := char.o 
 
else 
PWD := $(shell pwd) 
KVER := $(shell uname -r) 
KDIR := /lib/modules/$(KVER)/build 
all: 
  $(MAKE) -C $(KDIR) M=$(PWD) modules 
clean: 
  rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.* Module.* 
endif 

This Makefile file is basically the same as the makefile we wrote before, except that the name of the file is changed to char.o, and that's all. To write the module, we simply type make. At this point, the char.ko file can be generated. Then, modules need to be registered in the system, insmod char.ko is indispensable. If at this point, we are not sure whether the module has been added to the system, we can search and verify it by entering lsmod | grep char. In order to create the device node, we need to know what the major and minor values the device created for us are, so dmesg | tail looks for 1. On my hp machine, these two values are 249 and 0, so now I can use them to directly create device nodes. Enter mknod /dev/chr_dev c 249 0, and then enter ls /dev/chr_dev to verify 1. So, in this way, is it really possible to access this virtual device, so we can write a simple piece of code to verify that 1,


#include <stdio.h> 
#include <fcntl.h> 
#include <unistd.h> 
 
#define CHAR_DEV_NAME "/dev/chr_dev" 
 
int main() 
{ 
  int ret; 
  int fd; 
  char buf[32]; 
 
  fd = open(CHAR_DEV_NAME, O_RDONLY | O_NDELAY); 
  if(fd < 0) 
  { 
    printf("open failed!\n"); 
    return -1; 
  } 
   
  read(fd, buf, 32); 
  close(fd); 
   
  return 0; 
} 

The content of the code is very simple, that is, CHAR_DEV_NAME directly open the device, read and write devices. Of course. First, you need to compile this file, test.c, gcc test.c-o test, and then you need to run this file and type./test directly. If there are no problems, then our code is ok, but we still haven't seen anything. It doesn't matter, let's check whether there is any relevant print content in the kernel by dmesg, and directly enter dmesg | tail. At this point, if there is no accident, we can see the printk print left in chr_open and chr_read, which indicates that our code is completely ok.

The code above is just a small example, but the real thing is more complicated than that. But now that we've gotten started, there's nothing to be afraid of. Finally, there are two things to add :(1) if you want to delete the device node after the node is created, rm-rf /dev/chr_dev; (2) the prototype of the above code is from the book "in-depth linux device driver kernel mechanism", which is slightly modified. If you are interested in the content of the kernel mechanism, you can refer to the content of this book.

Thank you for reading, I hope to help you, thank you for your support of this site!


Related articles: