Linux Device driver Third article: write a simple character device driver

Source: Internet
Author: User

In the Linux device driver first: Introduction to the device driver briefly describes the character driver, this article briefly describes how to write a simple character device driver. This article draws on the source code in the LDD, realizes a hardware device-independent character device driver, only operates some memory allocated from the kernel.

Let's start learning how to write a simple character device driver. First, let's break it down. Character device drivers have structures or methods that make it possible to implement a character device that can be used to drive what we have to do.

1, the main device number and the secondary device number access to the character device is through the name of the device in the file system. They are usually located in the/dev directory. As follows:
[Email protected]:~$ ls-l/dev/total 0BRW-RW----  1 root disk        7,   0  March 10:34 loop0brw-rw----  1 root Disk        7,   1  March 10:34 loop1brw-rw----  1 root disk        7,   2  March 10:34 loop2crw-rw-rw-  1 root TTY         5,   0  March 12:48 ttycrw--w----  1 root TTY         4,   0  March 10:34 tty0crw-rw---- C20/>1 Root TTY         4,   1  March 10:34 tty1crw--w----  1 root TTY         4,  March 10:34 tty10
where b represents the block device, and C represents the character device. For ordinary files, Ls-l will list the length of the file, and for the device file, the above 7,5,4 and so on represents the corresponding device's main device number, and the back of the 0,1,2,10 and so on is the corresponding device's secondary device number. So what does the main device number and the secondary device number mean? In general, you can understand that the main device number identifies the driver for the device, which means that the 1 main device number corresponds to a driver. Of course, there are also cases where multiple drivers share the main device number. The secondary device number is used by the kernel to determine the specific device that corresponds to the device file in/dev. For example, the Virtual Console and serial terminal have driver 4 management, and different terminals have different secondary device number. 1.1, device number expression in the kernel, dev_t is used to save the device number, including the main device number and the secondary device number. In the kernel version of 2.6, dev_t is a 32-bit number, where 12 bits are used to represent the main device number, and the remaining 20 bits are used to identify the secondary device number. Get the main device number and the secondary device number by dev_t use the following macro: MAJOR (dev_t Dev); MINOR (dev_t dev); instead, the conversion of the main device number and the secondary device number to the dev_t type is used: MKDEV (int major, int MINOR); 1.2, assigning and releasing device numbers before building a character device, The driver first obtains one or more device numbers, which is similar to a business license, with a business license to operate in the kernel normally. The functions that complete this work are:
int Register_chrdev_region (dev_t first, unsigned int count, const char *name);
First is the starting value for the range of device numbers to assign. Count is the number of sequential devices. Name is the device name associated with the device number range, and he will appear in/proc/devices and Sysfs. This function successfully returns 0 and fails to return a negative error code. This function is used with a known master device number, and in the case of an unknown main device number, we use the following function:
int alloc_chrdev_region (dev_t *dev, unsigned int firstminor, unsigned int count, const char *name);
Dev is used to output the device number requested to, Firstminor the first device number to use. These device numbers need to be released when they are not in use, and other device programs are available:
void Unregister_chrdev_region (dev_t dev, unsigned int count);
This function is called more in the Purge function of the module. After assigning to the device number, we just got the business license, although it's almost ready now, but we just applied for the device number from the kernel, the application still can't do anything with the device, we need a simple function to connect the device number to the function that the device can implement, This allows our modules to provide specific functionality. This is a very simple operation and will be mentioned later, before you begin by introducing several important data structures.
2. Important data structure The registration device number is just the first step to complete a character device driver. The following is a description of the data structure of the three important cores that most drivers will contain. 2.1. File operationfile_operationsFile_operations is the first important structure, defined in <linux/fs.h>, is a collection of function pointers that most of the functionality that a device can provide is provided by this structure. These operations are also specific implementations of device-related system calls. The concrete implementation of this structure is as follows:
struct File_operations {//It is a pointer to the module that owns the structure. This member is used to block the module from being unloaded when its operation is still in use. Almost all the time, it was simply initialized to This_module Struc        T module *owner;        loff_t (*llseek) (struct file *, loff_t, int);        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);        ssize_t (*aio_read) (struct KIOCB *, const struct IOVEC *, unsigned long, loff_t);        ssize_t (*aio_write) (struct KIOCB *, const struct IOVEC *, unsigned long, loff_t);        ssize_t (*read_iter) (struct KIOCB *, struct iov_iter *);        ssize_t (*write_iter) (struct KIOCB *, struct iov_iter *);        Int (*iterate) (struct file *, struct dir_context *);        unsigned int (*poll) (struct file *, struct poll_table_struct *);        Long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);        Long (*compat_ioctl) (struct file *, unsigned int, unsigned long); Int (*mmap) (struct file *, struct vm_area_struct *);        Int (*open) (struct inode *, struct file *);        Int (*flush) (struct file *, fl_owner_t ID);        Int (*release) (struct inode *, struct file *);        Int (*fsync) (struct file *, loff_t, loff_t, int datasync);        Int (*aio_fsync) (struct KIOCB *, int datasync);        Int (*fasync) (int, struct file *, int);        Int (*lock) (struct file *, int, struct file_lock *);        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);        unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);        Int (*check_flags) (int);        Int (*flock) (struct file *, int, struct file_lock *);        ssize_t (*splice_write) (struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);        ssize_t (*splice_read) (struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);        Int (*setlease) (struct file *, long, struct file_lock * *); Long (*fallocate) (structFile *file, int mode, loff_t offset, loff_t len); Int (*show_fdinfo) (struct seq_file *m, struct file *f);};
It is necessary to note that the function in this is not fully implemented in the driver, the unsupported operation is null.2.2. File structure struct struct file, defined in <linux/fs.h>, is the second most important data structure in device drivers. The file structure represents an open file. (It is not specific to the device driver; each open file in the system has an associated struct file in kernel space). It is created by the kernel at open and passed to any function that operates on the file until the last close. After all instances of the file have been closed, the kernel releases this data structure. The file structure can be referenced in detail in Fs.h, which is listed here for several important members.
  • struct File_operations *f_op: is the collection structure of the file operations just described above.
  • mode_t f_mode: file mode Determines whether the file is readable or writable (or both), through Bits fmode_read and fmode_write. You may want to check the read and write permission of this member in your open or IOCTL function, but you do not need to check Write the license, because the kernel checks before calling your method. The attempt to read or write when the file has not yet opened for that access is rejected, and the driver is not even aware of the situation
  • loff_t F_pos: The current read and write location. loff_t is 64 bits on all platforms. The driver can read this value if it needs to know the current position in the file, but should not change it normally.
  • unsigned int f_flags: These are file flags, such as O_rdonly, O_nonblock, and O_sync. The driver should check the O_NONBLOCK flag to see if the request is a non-blocking operation.
  • void *private_data:Open System Call Setting this pointer is NULL before the open method is called for the drive. You are free to use this member or ignore it; You can use this member to point to the assigned data, But then you have to remember to release that memory in the release method before the kernel destroys the file structure. Private_data is a useful resource for preserving state information between system calls, and most of our example modules use it
2.3.inode Structure

The INODE structure is used internally by the kernel to represent files. Therefore, it differs from the file structure that represents the open file descriptor. There may be many file structures that represent multiple open descriptors for a single file, but they all point to a single inode structure.

The inode structure contains a large amount of information about the file. But for driver programming generally do not care, for the moment.

3, the registration of the character deviceThe kernel uses the structure of type struct Cdev internally to represent the character device. Before the kernel invokes your device operation, you write an assignment and register one or several of these structures.

There are 2 ways to assign and initialize one of these structures. If you want to get a separate Cdev structure at run time, you can use this code for this:

struct Cdev *my_cdev = Cdev_alloc (); my_cdev->ops = &my_fops;
It is more the case that the CDV structure is embedded in your own encapsulated device structure and needs to be allocated and initialized using the following methods:
void Cdev_init (struct cdev *cdev, struct file_operations *fops);
This is what the following example program does.Once the CDEV structure is established, the final step is to tell it to the kernel:
int Cdev_add (struct cdev *dev, dev_t num, unsigned int count)
Here, Dev is the CDEV structure, and NUM is the first device number that the device responds to, and count is the number of device numbers that should be associated to the device. Often count is 1.

To remove a character device from the system, call:

void Cdev_del (struct cdev *dev);
4, a simple character device above describes the implementation of a character device to do the work, the following is a real example to summarize the above described content. The key place in the source code has been commented.
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h > #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/ cdev.h> #include <asm/io.h> #include <asm/uaccess.h> #include <linux/timer.h> #include <asm/ atomic.h> #include <linux/slab.h> #include <linux/device.h> #define CDEVDEMO_MAJOR 255/*  The main device number of the preset cdevdemo */static int cdevdemo_major = cdevdemo_major;/* Device structure body, this structure can encapsulate some information such as device related signal quantity and so on can also be encapsulated in this structure, the subsequent device module generally One such struct should be encapsulated, but this struct must contain some members, and for character devices we must include struct Cdev cdev*/struct cdevdemo_dev{struct cdev Cdev;}; struct Cdevdemo_dev *cdevdemo_devp;/* device struct pointer *//* file Open function, the upper layer calls open on this device will execute */int cdevdemo_open (struct inode *inode, struct file *filp) {PRINTK (kern_notice "======== Cdevdemo_open"); return 0;} /* file is released, the upper layer calls close on this device to execute */int cdevdemo_release (struct inode *inode, struct file *filp) {PRINTK (Kern_notice) ======== Cdevdemo_release "); return 0;} /* Read operation of the file, upper for thisThe device calls read when it executes */static ssize_t cdevdemo_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos) {PRINTK (kern_n Otice "======== cdevdemo_read");} /* File operation structure, this structure has been described */static const struct file_operations cdevdemo_fops ={.owner = This_module,.open = Cdevdemo_open,. Release = Cdevdemo_release,.read = cdevdemo_read,};/* Initialize and register cdev*/static void Cdevdemo_setup_cdev (struct Cdevdemo_dev * dev, int index) {PRINTK (kern_notice "======== Cdevdemo_setup_cdev 1"); int err, Devno = MKDEV (cdevdemo_major, index); PRINTK (kern_notice "======== Cdevdemo_setup_cdev 2");/* Initializes a character device, the operation supported by the device Cdevdemo_fops in */cdev_init (&dev-> Cdev, &cdevdemo_fops);p rintk (kern_notice "======== cdevdemo_setup_cdev 3");d Ev->cdev.owner = THIS_MODULE;dev- >cdev.ops = &AMP;CDEVDEMO_FOPS;PRINTK (kern_notice "======== Cdevdemo_setup_cdev 4"); err = Cdev_add (&dev-> Cdev, Devno, 1);p rintk (kern_notice "======== cdevdemo_setup_cdev 5"); if (err) {PRINTK (kern_notice "Error%d add Cdevdemo% D ", err, Index);}} int CdevdemO_init (void) {PRINTK (kern_notice "======== cdevdemo_init"); int ret;dev_t devno = MKDEV (cdevdemo_major, 0); struct class * cdevdemo_class;/* Application Device number, if the application fails to use the dynamic application Method */if (cdevdemo_major) {PRINTK (kern_notice "======== cdevdemo_init 1"); ret = Register_chrdev_region (Devno, 1, "Cdevdemo");} ELSE{PRINTK (kern_notice "======== cdevdemo_init 2"), ret = Alloc_chrdev_region (&devno,0,1, "Cdevdemo"); cdevdemo_ Major = Major (DEVNO);} if (Ret < 0) {PRINTK (kern_notice "======== cdevdemo_init 3"); return ret;} /* Dynamic application device structure in vivo */CDEVDEMO_DEVP = kmalloc (sizeof (struct cdevdemo_dev), Gfp_kernel), if (!CDEVDEMO_DEVP)/* Request failed */{ret =- ENOMEM;PRINTK (kern_notice "Error add Cdevdemo"); goto Fail_malloc;} memset (cdevdemo_devp,0,sizeof (struct Cdevdemo_dev));p rintk (kern_notice "======== cdevdemo_init 3"); Cdevdemo_setup_  Cdev (CDEVDEMO_DEVP, 0);/* The following two lines are created with a bus type, and a Cdevdemo directory is generated under/sys/class here there is also a major function is to perform device_create will be automatically generated under/dev/ Cdevdemo the device node. If you do not call this function, if you want to access the device through the device node, you need to manually mknod to create the device node and then access. */cdevdemo_class = Class_create (This_module, "cdevdEmo ");d evice_create (Cdevdemo_class, NULL, MKDEV (cdevdemo_major, 0), NULL," Cdevdemo ");p rintk (kern_notice" ======== Cdevdemo_init 4 "); return 0;fail_malloc:unregister_chrdev_region (devno,1);} void Cdevdemo_exit (void)/* module unload */{PRINTK (kern_notice "End Cdevdemo"); Cdev_del (&cdevdemo_devp->cdev);/* Logoff Cdev*/kfree (CDEVDEMO_DEVP);/* release device structure in vivo */unregister_chrdev_region (MKDEV (cdevdemo_major,0), 1);//release device number}module_ LICENSE ("Dual BSD/GPL"); Module_param (Cdevdemo_major, Int., S_irugo); Module_init (Cdevdemo_init); Module_exit ( Cdevdemo_exit);
The makefile file is as follows:
Ifneq ($ (kernelrelease),) obj-m: = Cdevdemo.oelsekerneldir? =/lib/modules/$ (Shell uname-r)/buildpwd: = $ (shell pwd) default:$ (make)-C $ (Kerneldir) m=$ (PWD) MODULESENDIFCLEAN:RM-RF *.o *~ core depend. *.cmd *.ko *.mod.c. Tmp_versions mo Dules.order  Module.symvers
Warm tip: Test environment for Linux Ubuntu 3.16.0-33-generic.
5, summarized this article mainly introduces the simple character device's writing and the realization as well as the key point. The next chapter will explain some of the common debugging techniques for driving.

Programmer interaction Alliance (coder_online) , sweep the QR code below or search number coder_online can be followed, we can communicate online.


Linux Device driver Third article: write a simple character device driver

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.