catalogue
1 miscellaneous character device
Miscellaneous character device registration application
Classic device registration application
Linux2.6 device registration application
Linux system draws lessons from the idea of object-oriented to manage device drivers. Each type of device will define a specific structure to describe it. This structure contains the basic information of the device and the method of operating the device (function pointer). Therefore, writing a program is actually to implement the core structure, and then register this structure into the kernel.
The driver is to control the hardware downward and provide the interface upward. The interface provided upward here finally corresponds to the application layer in three ways: device file, / proc, / sys. The most commonly used is to use device file, which has three forms:
- Character device: a device that is accessed sequentially in bytes. (such as serial port, RTC, LCD screen...)
- Block device: a device that reads and writes in blocks. (emmc, flash, sd and other storage devices)
- Network equipment: for sending and receiving data packets. (wired / wireless network card)
The most commonly used Linux devices are character devices. In the current kernel version, there are three popular character device programming modules:
1. Miscellaneous equipment drive model
2. Early classic standard character device driver model
3,Linux2.6 standard character device driver model
Note: installing miscellaneous devices will automatically generate device files in the / dev directory, while the early classic character device driver model and Linux 2 6 the standard character device driver model will not generate device files by itself
1 miscellaneous character device
Before learning miscellaneous character equipment, first understand the concept of equipment number, which is divided into primary equipment number and secondary equipment number. Main equipment number: it is used to identify class I equipment (similar to class number); Secondary equipment number: it is used to identify the equipment in a class of equipment (student number of students in the class).
Miscellaneous character devices mainly learn one structure and two functions.
Core structure
Parameter miscdevice header file path: #include < Linux / miscdevice h>
struct miscdevice { int minor; //Secondary equipment No const char *name; //Device name, / dev / const struct file_operations *fops;//File operation method set pointer (linux-3.2.2\include\linux\ fs.h) //The following is the kernel usage, which users do not need to pay attention to struct list_head list; struct device *parent; struct device *this_device; const char *nodename; mode_t mode; };
Operation function
int misc_register(struct miscdevice *misc); //Register function //Function: register a miscellaneous character device with the kernel //Parameter: misc has implemented the address of struct miscdevice structure variable of minor,name,fop and three members //Return: 0: Registration succeeded, < 0: failed, return failure error code int misc_deregister(struct miscdevice *misc);//Logoff function //Function: unregister a miscellaneous character device that has been registered in the kernel //Parameter: the address of the struct miscdevice structure variable that misc has registered //Return: 0: Registration succeeded, < 0: failed, return failure error code
Miscellaneous character device registration application
Write 3 files, 1 is the kernel layer driver file hello c. 2 is the user layer file app c. 3 is Makefile file, which realizes the function of LED flashing. Note: call misc once_ The register registration function will only occupy one secondary device number. The primary device number of miscellaneous devices is fixed to 10, and the change of secondary device number is returned to 0-254. Writing 255 represents the automatic allocation of secondary device number. The device file is automatically generated.
1 kernel layer: Hello c
//**************Kernel layer code hello c************** #include <linux/module.h> #include <linux/kernel.h> #include <linux/miscdevice.h> #include <linux/fs.h> unsigned int *base = NULL; //Base address of LED lamp control register #define GPM4CON *((volatile unsigned int *)(base)) //LED data register base address #define GPM4DAT *((volatile unsigned int *)(base +1)) int led_open (struct inode *inode, struct file *fp) { printk("hello open is ok\n"); GPM4DAT &=~(0XF<<0); //Turn on the LED return 0; } int led_close (struct inode *inode, struct file *fp) { printk("hello close is ok\n"); GPM4DAT |=(0XF<<0); //Turn off the small lights return 0; } struct file_operations hello_fops = {//Collection of file operation methods .owner = THIS_MODULE,/*Module reference, which is assigned at any time_ MODULE */ .open = led_open, .release = led_close, }; //Initialize miscellaneous character device control structure struct miscdevice misc = { .minor = 255,/*It can be specified directly. Generally, it is 255, indicating that it is automatically allocated by the system*/ .name = "led", .fops = &hello_fops, }; static int __init led_init(void) { int ret; ret = misc_register(&misc);//Register device if(ret <0) { printk("misc register error!\n"); return -1; } printk("misc register sucess!\n"); //Address mapping base =(unsigned int *) ioremap(0x110002e0, 0x16); GPM4CON &=~(0XFFFF<<0);//configure port GPM4CON |= (0X1111<<0); GPM4DAT |= (0XF<<0);//LED off by default return 0; } static void __exit led_exit(void) { iounmap(base);//Unmap address misc_deregister(&misc);//Logout device printk("misc register exit\n"); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
2. User layer: app c
#include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> int fd; int main() { while(1) { fd=open("/dev/led",O_RDWR); sleep(2); close(fd); sleep(2); } return 0; }
3. Prepare Makefile file
obj-m += led.o KDIR:=/root/work/linux-3.5 all: make -C $(KDIR) M=$(PWD) modules arm-linux-gcc -o app app.c clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.markers *.order app
4 load the module in the super terminal of the development board and run the user program
insmod led.ko //Load LED module lsmod //View mounted modules ./app //Run user program //Here the LED flashes
2 early classic registration
Earlier versions of device registration used the function register_chrdev(), the operation is successful, and the return value is 0. When closing the device, you usually need to unregister the original device and use the function unregister_chrdev(). The primary equipment number and secondary equipment number cannot be greater than 255. The core structure of miscellaneous equipment does not have a core structure;
Equipment number: primary equipment number: 0 ~ 255 (10 is for miscellaneous equipment). Secondary equipment number: 0 ~ 255. When 255 is passed, it means that the secondary equipment number is automatically assigned. Feature: after installation, the / dev / device file node will not be created automatically, but needs to be created manually with mknod command.
mknod /dev/xxx c primary device number secondary device number
Call a register_ After chrdev is registered, 256 secondary device numbers are occupied. That is, a master device number can only use register_ The chrdev function is registered once.
Operation function
Header file: #include < Linux / Fs h>
int register_chrdev(unsigned int major,const char *name,const struct file_operations *fops); /*Function: register an early classic standard character device Parameter: major master device number, 0 ~ 255 (except 10). The master device number registered by this kind of driver cannot be the same as that of other drivers, otherwise it will fail. Therefore, when you are not sure which number is OK, it can only be automatically allocated by the kernel. When major passes 0, it means that the kernel will automatically allocate an available master device number. name:The device name does not need to be the same as the corresponding node name under / dev. after registration, this name will appear in the / proc/device file. (you can view it with cat /proc/device). fops:File operation method pointer Return value: when major =0: Success: return the assigned primary device number; failure: return a negative number. When major > 0: Success: return 0 Failed: negative number returned.*/ void unregister_chrdev(unsigned int major,const char *name ); /*Function: log off an existing standard character device Parameter: major: main device number: Name: device name, using register_chrdev registered device name Return: None*/
Classic device registration application
1. Kernel layer: register_chrdev.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> static int major = 0; int hello_open(struct inode *inode, struct file *fp) { printk("hello_open ok\n"); return 0; } ssize_t hello_read(struct file *fp, char __user *buf, size_t size, loff_t *offset) { printk("hello_read ok\n"); return 0; } ssize_t hello_write(struct file *fp, const char __user *buf, size_t size, loff_t *offset) { printk("hello_write ok\n"); return 0; } int hello_close(struct inode *inode, struct file *fp) { printk("hello_close ok\n"); return 0; } struct file_operations my_fops = { .owner=THIS_MODULE, .open = hello_open, .read = hello_read, .write = hello_write, .release = hello_close, }; static int __init hello_register_init(void) { major = register_chrdev(0, "hello_chrdev",&my_fops); if(major < 0) { printk("register chrdev error\n"); return -1; } printk("register chrdev ok\n"); printk("major = %d\n",major); return 0; } static void __exit hello_registet_exit(void) { unregister_chrdev(major,"hello_chrdev"); } module_init(hello_register_init); module_exit(hello_registet_exit); MODULE_LICENSE("GPL");
2. User layer: app c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() { int fp; fp =open(argv[1], O_RDWR); if(fp < 0) { printf("open error\n"); return -1; } write(fp,NULL,0); read(fp,NULL,0); close(fp); return 0; }
3 Makefile file
obj-m += register_chrdev.o KDIR:=/root/work/linux-3.5 all: make -C $(KDIR) M=$(PWD) modules arm-linux-gcc -o app app.c clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.markers *.order app
4. Manually create the device node file, load the module and run the user program
//Execute in the terminal serial port of the development board mknod /dev/led c 250 0 //Create device node insmod led.ko //Loading module ./app led //Run user program
3 Linux2.6 character device
Using dev_t represents a device number, dev_t is actually a u32 type. The upper 12 digits are the main equipment number and the lower 20 digits are the secondary equipment number. Main equipment No.: dev_t high 12 digits, 2^12=4K (10 is for miscellaneous equipment), secondary equipment No.: dev_t lower 20 bits, 2^20=1M
Core structure
//Linux2.6. Core structure of standard character equipment: cdev #include <linux/cdev.h> struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; //Operation method of equipment file struct list_head list; dev_t dev; //32-bit device number: including primary and secondary unsigned int count; //How many connected equipment numbers are used }; //Members that must be implemented: // ops: file operation method pointer // Dev: starting device number (including primary device number and secondary device number), dev_t is actually a u32 type // count: the number of secondary device numbers to be occupied by this device (continuous), starting from the secondary device number in dev.
Note: after installation, the / dev / device file node will not be created automatically, but needs to be created manually with mkond command
mknod /dev/xxx c primary device number secondary device number, call CDEV once_ After add is registered, the specified number of secondary numbers is occupied. The number can be specified by yourself. A master device can use CDEV_ The add function is registered more than once
Synthetic equipment No.: MKDEV(ma,mi):ma: main equipment No; mi: secondary equipment number
Decompose equipment number: MAJOR(dev): decompose the main equipment number from the equipment number dev
MINOR(dev): decompose the secondary device number from the device number dev
Operation function
Including functions related to automatic creation of equipment nodes.
//1. Static device number application function header file: #include <linux/fs.h> int register_chrdev_region(dev_t from, unsigned count, const char *name); //Function: apply for an equipment number range //Parameter: from: starting equipment number (primary and secondary); count: number of consecutive secondary equipment numbers; //Name: device name, which does not need to be the same as the device file name of / dev / //Return value: 0: success; Failed: negative number returned //1. Dynamic device number application function header file: #include <linux/fs.h> int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name); //Function: apply for an equipment number range //Parameter: dev: store the first assigned device (including primary and secondary device numbers); baseminor: to assign the starting secondary equipment number; count: number of consecutive secondary equipment numbers; Name: device name, which does not need to be the same as the device file name of / dev / //Return value: 0: success; Failed: negative number returned //2. Device number release function: header file: #include <linux/fs.h> void unregister_chrdev_region(dev_t from,unsigned int count); //Function: release a device number range //Parameter: from: starting equipment number (primary and secondary) (including primary and secondary equipment numbers); count: number of consecutive secondary equipment numbers //Return value: None //3. Core structure allocation function: header file: #include <linux/cdev.h> struct cdev *cdev_alloc(void); //Function: allocate a core structure in the heap space. Note that when not in use, release it with kfree function; //Parameter: None //Return value: returns the first address allocated to the struct cdev structure space //Note: remember to release after use, otherwise it will cause memory leakage. //4. Core structure initialization function: header file: #include <linux/cdev.h> void cdev_init(struct cdev *cdev,const struct file_operations *fops); //Function: initialize the core structure, specifically clearing the core structure and initializing the list, kobj and OPS members of the core structure //Parameter: cdev: pointer to the core structure to be initialized; fops: file operation method structure pointer //Return value: None //Note: when writing this driving model, it is not necessary to define the struct cdev structure variable initialization, because CDEV is called_ The init function will clear it to 0 when it is defined, and the initial is invalid when it is defined //5. Device registration function: header file: #include <linux/cdev.h> int cdev_add(struct cdev *p,dev_t dev,unsigned count); //Function: register a cdev structure //Parameter: p: initialized core structure pointer; dev: starting equipment number (including primary and secondary equipment numbers); //count: number of consecutive equipment numbers //Return value: Success: 0; Failed: negative number returned //6. Device logoff function: header file: #include <linux/cdev.h> void cdev_del(struct cdev *p); //Function: log off a cdev structure //Parameter: p: struct cdev structure pointer registered earlier //Value: None //**********Automatically create device node related functions********** //1. Automatic node function creation: header file #include<linux/device.h> struct device *device_create( struct class *cls, // The class pointer of the device is managed by that class struct device *parent, // NULL dev_t devt, // The combination of primary equipment number and secondary equipment number is 32 bits, primary 12 bits and secondary 20 bits void *drvdata, // NULL, device private data const char *fmt, ... /*Node name under fmt:/dev / that can be formatted*/ ); //2. Create class: header file #include<linux/device.h> class_create(ower,name); //This function returns a pointer //Owner: the owner of the class. The fixed is THIS_MODULE //nameļ¼ Class name, whatever, can have meaning. It's best not to be the name of the device under / dev /. This name determines / sys/class/name. When a class is created, a directory named name will be generated in the / sys/class / directory
Linux2.6 device registration application
Kernel layer: linux26_class.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/ioport.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <asm/uaccess.h> #include <linux/io.h> static int major = 250; static int minor = 0; static dev_t devno; static struct cdev cdev; static struct class *cls; static struct device *tst_device; static int hello_open(struct inode *inode, struct file *file)//open { printk("hello_open\n"); return 0; } static int hello_release(struct inode *inode, struct file *file)//close { printk("hello_release\n"); return 0; } struct file_operations hello_ops ={ .owner = THIS_MODULE, .open = hello_open, //open(); .release = hello_release, //close(); }; static int hello_init(void)//initialization { int ret; devno = MKDEV(major,minor); ret = register_chrdev_region(devno, 1, "hello");//Static application if(ret<0) { if(alloc_chrdev_region(&devno, 0, 1, "hello"))//Dynamic application { printk("fail \n"); return ret; } } cls = class_create(THIS_MODULE, "mycls"); tst_device = device_create(cls, NULL, devno, NULL, "my_device"); printk("hello_init()\n"); printk("major = %d \n",MAJOR(devno)); cdev_init(&cdev, &hello_ops);//initialization ret = cdev_add(&cdev, devno, 1);//register if(ret<0) { unregister_chrdev_region(devno, 1);//Logout equipment number return ret; } return 0; } static void hello_exit(void) { printk("hello_exit()\n"); device_destroy(cls, devno); class_destroy(cls); cdev_del(&cdev);//Unregister cdev structure space unregister_chrdev_region(devno,1);//Release device number } MODULE_LICENSE("GPL"); module_init(hello_init); module_exit(hello_exit);
User layer: test c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> int main() { int fd; int ret; int red; char buf[1024]={0}; fd = open("/dev/my_device",O_RDWR); if(fd < 0) { printf("open error\n"); return -1; } #if 0 ret = read(fd,buf,sizeof(buf)); if(ret < 0) { printf("read error\n"); return -1; } red = write(fd,buf,sizeof(buf)); if(red < 0) { printf("write error\n"); return -1; } #endif close(fd); return 0; }
Makefile
KERN_DIR = /root/tiny4412/linux-3.5 all: make -C $(KERN_DIR) M=`pwd` modules arm-linux-gcc test.c -o test clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order *o test obj-m += linux26_class.o