3, New interface for character device driver registration

1, Register_ Register character device driver in chrdev() register_chrdev(unsigned int major, const char *name,const str...
1, Register_ Register character device driver in chrdev()
register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);

But in fact, this function is registered before linux version 2.4. Its principle is:

(1) Identify a master device number

(2) Construct a file_ The operations structure is then placed in the chrdevs array

(3) Registration: register_chrdev

Then, when reading and writing character devices, the corresponding structure will be taken from the chrdevs array according to the main device number, and the corresponding processing function will be called.

It has a big disadvantage:

For each character device registered, 0 ~ 255 secondary device numbers will be registered continuously to bind them to the same file_ On the operations operation method structure, in most cases, only a few secondary device numbers are used, so a lot of resources will be wasted

2, New way to register character device drivers

After the 2.6 kernel, a register is added_ chrdev_ Region function, which supports segmenting the secondary device number under the same primary device number. Each segment is supplied to a character device driver, which greatly improves the resource utilization. At the same time, the 2.6 kernel retains the original register_chrdev method. In the 2.6 kernel, both methods are called to__ register_chrdev_region function.

At the same time, it is divided into static registration (specify the device number to register) register_chrdev_region, dynamic allocation (register without specifying device number)_ chrdev_ Region, as well as the range of secondary equipment number with continuous registration, avoiding register_chrdev() is a waste of resources

  1. register_chrdev_region()
/** * register_chrdev_region() - Specifies the device number to statically register a character device* * @from: The registered specified starting equipment number, such as MKDEV(100, 0), indicates that the starting primary equipment number is 100 and the starting secondary equipment number is 0 * @count: The number of secondary equipment numbers that need to be registered continuously. For example, the starting secondary equipment number is 0,count=100, which means that the secondary equipment numbers from 0 to 99 must be bound to the same file_operations is on the operation method structure * @name: Character device name * * return:When the return value is less than 0, the registration fails */ int register_chrdev_region(dev_t from, unsigned count, const char *name) { struct char_device_struct *cd; dev_t to = from + count; dev_t n, next; for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n)+1, 0); if (next > to) next = to; cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name); } return 0; }

register_chrdev_region is applied according to the required scope, and we need to manually cdev_init cdev_add .

  1. alloc_chrdev_region: register a series of device numbers, and the kernel allocates the device number
/** * alloc_chrdev_region() - Dynamically allocate a character device, register successfully, and put the allocated primary and secondary device numbers into * dev * @*dev:Store the pointer of the starting device number. When the registration is successful, * dev will be equal to the assigned starting device number. You can extract the primary and secondary device numbers through the macro() and MINNOR() macros * @baseminor: The base address of the secondary equipment number, that is, the starting secondary equipment number * @count:The number of secondary equipment numbers that need to be registered continuously. For example, the starting secondary equipment number (baseminor) is 0 and baseminor = 2, which means that the equipment numbers from 0 to 1 should be bound in the same file_operations is on the operation method structure * @name:Character device name * * return: When the return value is less than 0, the registration fails */ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) { struct char_device_struct *cd; cd = __register_chrdev_region(0, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd); *dev = MKDEV(cd->major, cd->baseminor); return 0; }
  1. cdev_alloc()

cdev_ The alloc function is used for operations that require space application, and cdev_init refers to operations that do not require space application; So if you define a pointer, you only need to use cdev_alloc function and then do an ops assignment operation; If you define a structure instead of a pointer, you only need to use CDEV_ The init function is OK.

  1. cdev_init()
/*Initialize the cdev structure and set the file_ Put the operations structure into cdev - > ops*/ void cdev_init(struct cdev *cdev, const struct file_operations *fops);

The members of cdev structure are as follows:

struct cdev { struct kobject kobj; // Embedded kobject object struct module *owner; //Module const struct file_operations *ops; //Operation method structure struct list_head list; //Inode - > I of the character device file corresponding to cdev_ Chain header for devices dev_t dev; //The starting equipment number can be extracted by MAJOR(),MINOR() unsigned int count; //Number of secondary equipment numbers registered continuously };
  1. cdev_add()
/*Add the cdev structure to the system, put dev (registered device number) into cdev - > dev, and count (number of secondary device numbers) into cdev - > count*/ int cdev_add(struct cdev *p, dev_t dev, unsigned count);
  1. cdev_del()
/*Delete the cdev structure in the system*/ void cdev_del(struct cdev *p);
  1. unregister_chrdev_region()
/*Unregister character device*/ void unregister_chrdev_region(dev_t from, unsigned count);

from: the specified starting equipment number of logout, for example: MKDEV(100, 0), which means the starting primary equipment number is 100 and the starting secondary equipment number is 0

count: the number of secondary equipment numbers that need to be logged off continuously. For example, if the starting secondary equipment number is 0 and baseminor = 100, it means that 0 ~ 99 secondary equipment numbers are logged off

  1. Several related macros

MKDEV, MAJOR and MINOR macros

You can use the major and minor macros from dev_t gets major and minor. Conversely, MKDEV macro punch major and minor can be used to get dev_t. Using these macro codes can make the driver more portable.

Drive instance
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h> #include <asm/uaccess.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <linux/string.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/cdev.h> #include <linux/device.h> #define MYCNT 1 #define MYNAME "testchar" #define GPJ0CON S5PV210_GPJ0CON #define GPJ0DAT S5PV210_GPJ0DAT #define rGPJ0CON *((volatile unsigned int *)GPJ0CON) #define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT) #define GPJ0CON_PA 0xe0200240 #define GPJ0DAT_PA 0xe0200244 unsigned int *pGPJ0CON; unsigned int *pGPJ0DAT; //int mymajor; static dev_t mydev; //static struct cdev test_cdev; // The CDEV is a directly defined variable, and register is used later_ chrdev_ Region() registers the driver directly. static struct cdev *pcdev; static struct class *test_class; char kbuf[100]; // Kernel space buf static int test_chrdev_open(struct inode *inode, struct file *file) { // What should really be placed in this function is to open the hardware operation code part of the device // But now we can't write so much, so we use a printk to print a message as a representative. printk(KERN_INFO "test_chrdev_open\n"); rGPJ0CON = 0x11111111; rGPJ0DAT = ((0 << 3) | (0 << 4) | (0 << 5)); // bright return 0; } static int test_chrdev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "test_chrdev_release\n"); rGPJ0DAT = ((1 << 3) | (1 << 4) | (1 << 5)); return 0; } ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { int ret = -1; printk(KERN_INFO "test_chrdev_read\n"); ret = copy_to_user(ubuf, kbuf, count); if (ret) { printk(KERN_ERR "copy_to_user fail\n"); return -EINVAL; } printk(KERN_INFO "copy_to_user success..\n"); return 0; } // The essence of write function is to copy the data passed from the application layer to the kernel, and then write it to the hardware in a correct way to complete the operation. static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { int ret = -1; printk(KERN_INFO "test_chrdev_write\n"); // Use this function to copy the content in the ubuf transmitted from the application layer to a buf in the driver space //memcpy(kbuf, ubuf); // No, because two are not in the same address space memset(kbuf, 0, sizeof(kbuf)); ret = copy_from_user(kbuf, ubuf, count); if (ret) { printk(KERN_ERR "copy_from_user fail\n"); return -EINVAL; } printk(KERN_INFO "copy_from_user success..\n"); if (kbuf[0] == '1') { rGPJ0DAT = ((0 << 3) | (0 << 4) | (0 << 5)); } else if (kbuf[0] == '0') { rGPJ0DAT = ((1 << 3) | (1 << 4) | (1 << 5)); } return 0; } // Customize a file_operations struct variable, and de populate static const struct file_operations test_fops = { .owner = THIS_MODULE, // Convention, just write it directly .open = test_chrdev_open, // The actual call when the application open s this device in the future .release = test_chrdev_release, // This is the function corresponding to. open .write = test_chrdev_write, .read = test_chrdev_read, }; // Module installation function static int __init chrdev_init(void) { int retval; printk(KERN_INFO "chrdev_init helloworld init\n"); // Use the new cdev interface to register character device drivers // The new interface needs 2 steps to register the character device driver // Step 1: assign primary and secondary equipment numbers retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME); if (retval < 0) { printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME); goto flag1; } printk(KERN_INFO "alloc_chrdev_region success\n"); printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev)); // Step 2: register the character device driver, where the dynamic application memory is used pcdev = cdev_alloc(); // Allocate memory to pcdev and instantiate pointers //cdev_init(pcdev, &test_fops); pcdev->owner = THIS_MODULE; pcdev->ops = &test_fops; retval = cdev_add(pcdev, mydev, MYCNT); if (retval) { printk(KERN_ERR "Unable to cdev_add\n"); goto flag2; } printk(KERN_INFO "cdev_add success\n"); // After registering the character device driver, add the operation of device class to let the kernel send messages for us // To udev, let udev automatically create and delete device files test_class = class_create(THIS_MODULE, "aston_class"); if (IS_ERR(test_class)) return -EINVAL; // The last parameter string is the name of the device file we will create in the / dev directory in the future // So the file name we want here is / dev/test device_create(test_class, NULL, mydev, NULL, "test111"); // Use dynamic mapping to operate registers if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON")) // return -EINVAL; goto flag3; if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON")) // return -EINVAL; goto flag3; pGPJ0CON = ioremap(GPJ0CON_PA, 4); pGPJ0DAT = ioremap(GPJ0DAT_PA, 4); *pGPJ0CON = 0x11111111; *pGPJ0DAT = ((0 << 3) | (0 << 4) | (0 << 5)); // bright //goto flag0: return 0; // If step 4 goes wrong, jump here release_mem_region(GPJ0CON_PA, 4); release_mem_region(GPJ0DAT_PA, 4); // If step 3 goes wrong, jump here flag3: cdev_del(pcdev); // If step 2 goes wrong, jump here flag2: // Write off the success of step 1 here unregister_chrdev_region(mydev, MYCNT); // If step 1 goes wrong, jump here flag1: return -EINVAL; //flag0: // return 0; } // Module download function static void __exit chrdev_exit(void) { printk(KERN_INFO "chrdev_exit helloworld exit\n"); *pGPJ0DAT = ((1 << 3) | (1 << 4) | (1 << 5)); // Unmap iounmap(pGPJ0CON); iounmap(pGPJ0DAT); release_mem_region(GPJ0CON_PA, 4); release_mem_region(GPJ0DAT_PA, 4); /* // In module_ Unregister the character device driver in the function called by the exit macro unregister_chrdev(mymajor, MYNAME); */ device_destroy(test_class, mydev); class_destroy(test_class); // Use the new interface to unregister the character device driver // There are two steps for cancellation: // The first step is to really log off CDEV for character device driver_ del cdev_del(pcdev); // The second step is to cancel the primary and secondary equipment number of the application unregister_chrdev_region(mydev, MYCNT); } module_init(chrdev_init); module_exit(chrdev_exit); // MODULE_xxx macro is used to add module description information MODULE_LICENSE("GPL"); // Describe the license for the module MODULE_AUTHOR("aston"); // Describe the author of the module MODULE_DESCRIPTION("module test"); // Describe the introduction of the module MODULE_ALIAS("alias xxx"); // Describes the alias information for the module

11 November 2021, 17:20 | Views: 3082

Add new comment

For adding a comment, please log in
or create account

0 comments