Linux Device Driver Article 3: How to Write a simple character device driver ?, Linux Article 3

Source: Internet
Author: User

Linux Device Driver Article 3: How to Write a simple character device driver ?, Linux Article 3

In the first article on linux Device Drivers: the device drivers briefly introduce character drivers. This article briefly introduces how to write a simple character device driver. This article draws on the source code in LDD to implement a character device driver unrelated to the hardware device, and only operates some memory allocated from the kernel.

The following describes how to write a simple character device driver. First, let's take a look at the structures or methods that the character device drivers have. That is to say, what do we have to do to implement a usable character device driver.

1. The master and secondary device numbers access character devices through the device names in the file system. They are usually located in the/dev directory. As follows:
Xxx @ ubuntu :~ $ Ls-l/dev/total 0brw-rw ---- 1 root disk 7, 0 March 25 10:34 loop0brw-rw ---- 1 root disk 7, 1 March 25 10:34 loop1brw-rw ---- 1 root disk 7, 2 March 25 10:34 loop2crw-rw-rw-1 root tty 5, 0 March 25 12:48 ttycrw -- w ---- 1 root tty 4, 0 March 25 10:34 tty0crw-rw ---- 1 root tty 4, march 25 10:34 tty1crw -- w ---- 1 root tty 4, 10 March 25 10:34 tty10
B Indicates the block device, and c indicates the character device. For common files, ls-l lists the file lengths. For device files, the above 7, 5, 4 and so on represent the master device Number of the corresponding device, and the following 0, 1, the second device Number of the corresponding device. What are the meanings of the master and secondary device numbers? Generally, it can be understood that the main device number identifies the driver corresponding to the device, that is, one master device number corresponds to one driver. Of course, there are also multiple drivers sharing the master device number. The device number is used by the kernel to determine the specific device corresponding to the device file under/dev. For example, the virtual console and serial port terminals have driver 4 Management, while different terminals have different device numbers. 1.1 The expression of the device number is in the kernel. dev_t is used to save the device number, including the primary device number and secondary device number. In kernel version 2.6, dev_t is a 32-bit number, where 12 bits are used to indicate the master device number, and the other 20 bits are used to identify the secondary device number. Use the following macro to obtain the master and secondary device numbers through dev_t: MAJOR (dev_t dev); MINOR (dev_t dev); on the contrary, use MKDEV (int major, int minor) to convert the Primary and Secondary device numbers to the dev_t type. 1.2. Assign and release device numbers before building a character device, the driver must first obtain one or more device numbers, which is similar to a business license. With a business license, the driver can work normally in the kernel. The function to complete this task is:
int register_chrdev_region(dev_t first, unsigned int count, const char *name);
First is the start value of the range of device numbers to be allocated. Count is the number of consecutive devices. Name is the name of the device associated with the range of device numbers. It will appear in/proc/devices and sysfs. If this function is successful, 0 is returned. If it fails, a negative error code is returned. This function is used when the master device number is known. When the master device number is unknown, 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 applied for. The first device number to be used by firstminor. These device numbers need to be released when not in use. Other device programs are provided for use:
void unregister_chrdev_region(dev_t dev, unsigned int count);
This function is mostly called in the clear function of the module. After we assigned the device number, we just got the business license. Although we have already prepared a similar number, we just applied for the device number from the kernel, applications still cannot do anything about the device. We need a simple function to connect the device number with the functions that the device can implement so that our module can provide specific functions. this operation is very simple and will be mentioned later. Before that, we will introduce several important data structures. 2. Registering device numbers with important data structures is only the first step to complete a character device driver. The following describes the three important kernel data structures that most drivers will contain. 2.1 file operation file_operationsfile_operations is the first important structure defined in <linux/fs. h>. It is a collection of function pointers, and most of the functions provided by devices are provided in this structure. These operations are also the specific implementation of device-related system calls. The specific implementation of this structure is as follows:
Struct file_operations {// It is a pointer to a module with this structure. this member is used to prevent the module from being detached when its operations are still in use. in almost all time, It is initialized as THIS_MODULE struct 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); struct (* 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); 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) (struct file * file, int mode, loff_t offset, loff_t len ); int (* show_fdinfo) (struct seq_file * m, struct file * f );};


It should be noted that the above functions do not need to be fully implemented in the driver, and the unsupported operation is left NULL.

2.2. the file structure struct filestruct file, defined in <linux/fs. h>, is the second most important data structure in the device driver. The file structure represents an open file. (it is not specific to the device driver; each opened file in the system has an associated struct file in the kernel space ). it is created when the kernel is open and passed to any function operated on the file until it is closed. after all instances in the file are closed, the kernel releases the data structure. For details about the file structure, refer to fs. h. Several important members are listed here.
  • Struct file_operations * f_op: The collection structure of the file operations described above.
  • Mode_t f_mode: The file mode determines whether the file is readable or writable (or both), through the FMODE_READ and FMODE_WRITE. you may want to check the read/write permission of this Member in your open or ioctl function, but you do not need to check the read/write permission because the kernel checks before calling your method. when the file is not opened for that type of access, the attempt to read or write is rejected, and the driver does not even know this situation.
  • Loff_t f_pos: The current read/write location. loff_t is 64-bit on all platforms. The driver can read this value. If it needs to know the current position of the file, it should not be changed 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 it is a request for non-blocking operations.
  • Void * private_data: open System Call sets this pointer to NULL before the open method is called for the driver. you can use this member or ignore it. You can use this member to point to the allocated data, but you must remember that before the kernel destroys the file structure, release the memory in the release method. private_data is a useful resource. status information is retained between system calls, which is used by most of the examples.
2.3 inode Structure

The inode structure is used internally by the kernel to represent files. therefore, it is different from the file structure that represents the open file descriptor. there may be many file structures that represent multiple open descriptors of a single file, but they all point to a single inode structure.

The inode structure contains a large amount of information about files. However, you don't need to worry about writing drivers.

3. The character device registration kernel internally uses the structure of the type struct cdev to represent the character device. Before the kernel calls your device operation, you write the assignment and register one or more of these structures.

There are two ways to allocate and initialize one of these structures. If you want to get an independent cdev structure at runtime, you can use this code:

struct cdev *my_cdev = cdev_alloc();my_cdev->ops = &my_fops;
In more cases, The cdv structure is embedded into your encapsulated device structure. In this case, you need to use the following method to allocate and initialize the structure:
void cdev_init(struct cdev *cdev, struct file_operations *fops);
This is what we will do in the following example. Once the cdev structure is established, the last step is to tell the kernel:
int cdev_add(struct cdev *dev, dev_t num, unsigned int count)
Here, dev is the cdev structure, num is the first device number in the response from this device, and count is the number of device numbers that should be associated with the device. usually count is 1.

Remove a character device from the system and call:

void cdev_del(struct cdev *dev);
4. A simple character device generally describes how to implement a character device. The following is a real example to summarize the content described above. The key points in the source code have been commented out.
# 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/* default master device Number of cdevdemo */static int cdevdemo_major = CDEV DEMO_MAJOR;/* Device struct. This struct can encapsulate information related to devices, semaphores, and so on. In this structure, subsequent device modules generally encapsulate such a struct, however, this struct must contain some members. For character devices, we must include struct cdev */struct cdevdemo_dev {struct cdev;}; struct cdevdemo_dev * cdevdemo_devp; /* Device struct pointer * // * file opening function. When the upper layer calls open for this device, it will execute */int cdevdemo_open (struct inode * inode, struct file * filp) {printk (KERN_NOTICE "======== cdevdemo_open"); return 0 ;}/ * file release, the upper layer will execute */int cdevdemo_rel when calling close for this device Struct (struct inode * inode, struct file * filp) {printk (KERN_NOTICE "======== cdevdemo_release"); return 0 ;}/ * read operations on the file, when the upper layer calls read, it will execute */static ssize_t cdevdemo_read (struct file * filp, char _ user * buf, size_t count, loff_t * ppos) {printk (KERN_NOTICE "======== cdevdemo_read");}/* file operation structure. This structure */static const struct file_operations cdevdemo_fops = {. owner = THIS_MODULE ,. open = cdevdemo_open ,. r Elease = 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");/* initialize a character device, the operations supported by the device are in cdevdemo_fops */cdev_init (& dev-> cdev, & cdevdemo_fops); printk (KERN_NOTICE "======= cdev Demo_setup_cdev 3 "); dev-> cdev. owner = THIS_MODULE; dev-> cdev. ops = & cdevdemo_fops; printk (KERN_NOTICE "======= cdevdemo_setup_cdev 4"); err = cdev_add (& dev-> cdev, devno, 1 ); printk (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 = MKD EV (cdevdemo_major, 0); struct class * cdevdemo_class;/* apply for the device number. if the application fails, 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, "cdevdemo "); cdevdemo_major = MAJOR (devno);} if (ret <0) {printk (KERN_NOTICE "========= cdevdemo_init 3 "); Return ret;}/* dynamically apply for device struct memory */cdevdemo_devp = kmalloc (sizeof (struct cdevdemo_dev), GFP_KERNEL); if (! Cdevdemo_devp)/* application failed */{ret =-ENOMEM; printk (KERN_NOTICE "Error add cdevdemo"); goto fail_malloc;} memset (cdevdemo_devp, 0, sizeof (struct cdevdemo_dev); printk (KERN_NOTICE "======= cdevdemo_init 3"); cdevdemo_setup_cdev (cdevdemo_devp, 0 ); /* the following two rows create a bus type, the cdevdemo directory will be generated under/sys/class. Another main function here is to automatically generate a cdevdemo device node under/dev/after device_create is executed. If you do not call this function, you need to manually create a device node to access the device through the device node before accessing the device. */Cdevdemo_class = class_create (THIS_MODULE, "cdevdemo"); device_create (cdevdemo_class, NULL, MKDEV (cdevdemo_major, 0), NULL, "cdevdemo "); printk (KERN_NOTICE "======= cdevdemo_init 4"); return 0; fail_malloc: unregister_chrdev_region (devno, 1);} void cdevdemo_exit (void) /* uninstall the module */{printk (KERN_NOTICE "End cdevdemo"); cdev_del (& cdevdemo_devp-> cdev);/* log out of cdev */kfree (cdevdemo_devp ); /* release the device struct memory */swap (MKDEV (cdevdemo_major, 0), 1); // release the device number} MODULE_LICENSE ("Dual BSD/GPL"); module_param (cdevdemo_major, int, S_IRUGO); module_init (cdevdemo_init); module_exit (cdevdemo_exit );

 

 

5. Summary This article mainly introduces the compilation and implementation of simple character devices and the key points. The next article will mainly explain some common debugging skills of the driver.

Get a blog update reminder and share more technical information as soon as possible. Welcome to the personal public platform: coder_online)

1. Help you directly answer questions about linux Device Drivers

2. Get technical articles from more than 10 fields in the industry in the first time

3. Ask questions in the article, reply to you immediately, and help you answer questions patiently

4. Let you and the original author become good friends and expand their own network resources

Scan the QR code below or search for the coder_online code. We can contact you online.

 

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.