preface
The main content of this paper is to register platform bus devices and platform bus drivers under Linux.
1, platform bus
Platform bus model: a bus virtualized by the Linux kernel is not a real wire.
The platform bus model divides the original driver c file into two C files, one is device.c and the other is driver.c, in which the stable ones are placed in driver.c and the ones that need to be changed are placed in device.c. This can improve the reusability of code and reduce the repeatability of code, which is the role of platform bus model.
So how to write a driver designed based on the platform bus model? In fact, driver.c and device.c are registered respectively. There is a structure member in these two files to match each other. When the two drivers are loaded, the structure member used to store the matching name in the two files has the same content, the matching is successful, otherwise the matching fails.
2, Registering platform devices
1. Introduction to structure
device.c contains hardware resources, which refer to register address, interrupt number, clock and other hardware resources.
In the Linux kernel, a structure is used to describe hardware resources.
platform_ The device structure is defined in / linux-4.1.15/include/linux/platform_device.h.
struct platform_device { const char *name; //If the name used in platform bus matching is consistent with that in driver, the matching is successful. Otherwise, the matching fails, and the name will be generated under / sys/bus/platform/devices int id; //Device id, generally write - 1 bool id_auto; struct device dev; //Embedded device structure u32 num_resources; //Number of resources struct resource *resource; //Hardware resources in device const struct platform_device_id *id_entry; char *driver_override; struct mfd_cell *mfd_cell; struct pdev_archdata archdata; };
Register function:
extern int platform_device_register(struct platform_device *);
Logoff function:
extern void platform_device_unregister(struct platform_device *);
The resource structure is defined under / linux-4.1.15/include/linux/ioport.h.
struct resource { resource_size_t start; //Resource start resource_size_t end; //End of resource const char *name; //Resource name unsigned long flags; //Resource type struct resource *parent, *sibling, *child; };
The macro definitions used are also in / linux-4.1.15/include/linux/ioport.h.
#define IORESOURCE_ IO memory #define IORESOURCE_MEM represents a piece of physical memory #define IORESOURCE_IRQ indicates interrupt
After the device is loaded successfully, you can see the name defined by yourself to match the driver in the / sys/bus/platform/devices path.
2.device.c file
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> void beep_release(struct device *dev){ printk("beep_release!\n"); } struct resource beep_resource[] = { [0] = { .start = 0x20AC000, .end = 0x20AC003, .flags = IORESOURCE_MEM, //Register type .name = "GPIO5_DR" } }; struct platform_device beep_device = { .name = "beep_test", //If the name used in platform bus matching is consistent with that in driver, the matching is successful. Otherwise, the matching fails, and the name will be generated under / sys/bus/platform/devices .id = -1, //Device id, generally write - 1 .resource = beep_resource, //Hardware resources in device .num_resources = ARRAY_SIZE(beep_resource), //Number of resources .dev = { .release = beep_release //Release or delete device } }; static int device_init(void) { printk("hello device_init!\n"); return platform_device_register(&beep_device); //register } static int device_exit(void) { printk("device_exit!\n"); platform_device_unregister(&beep_device); //cancellation } module_init(device_init); module_exit(device_exit); MODULE_LICENSE("GPL");
3.Makefile file
obj-m += device.o KDIR:=/linux/linux-4.1.15 PWD?=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean
3, Register platform driver
1. Introduction to structure
The idea of writing driver.c: first define a platform_driver structure variable, and then implement each member variable in the structure. When the driver and device match successfully, the probe function will be executed. After the match is successful, the focus is on the probe function.
platform_ The driver structure is defined in / linux-4.1.15/include/linux/platform_device.h.
struct platform_driver { int (*probe)(struct platform_device *); //When the driver and device match successfully, the probe function will be executed int (*remove)(struct platform_device *); //This function will be executed when either driver or device is remove d void (*shutdown)(struct platform_device *); //This function is executed when the device receives the shutdown command int (*suspend)(struct platform_device *, pm_message_t state); //This function is executed when the device receives the suspend command int (*resume)(struct platform_device *); //This function is executed when the device receives the resume command struct device_driver driver; const struct platform_device_id *id_table; //Priority is higher than name under driver bool prevent_deferred_probe; };
device_ The driver structure is defined under / linux-4.1.15/include/linux/device.h.
struct device_driver { const char *name; //The name used when matching with device, with priority lower than ID_ Name in table struct module *owner; };
2.driver.c file
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> int beep_probe(struct platform_device *pdev) { printk("beep_probe matching ok!\n"); return 0; } int beep_remove(struct platform_device *pdev) { printk("beep_remove!\n"); return 0; } const struct platform_device_id beep_idtable = { .name = "beep_test" //The name priority here is higher than the name in the driver, and the priority matches the name in the device }; struct platform_driver beep_device = { .probe = beep_probe, .remove = beep_remove, .driver = { .owner = THIS_MODULE, .name = "beep123" //It needs to be the same as the name in the device, otherwise the matching is unsuccessful. If there is no idtable, the device matches the name }, .id_table = &beep_idtable }; static int beep_driver_init(void) { int ret = 0; ret = platform_driver_register(&beep_device); if(ret < 0) { printk("platform_driver_register error!\n"); return ret; } printk("platform_driver_register ok!\n"); return 0; } static int beep_driver_exit(void) { printk("beep_driver_exit!\n"); platform_driver_unregister(&beep_device); } module_init(beep_driver_init); module_exit(beep_driver_exit); MODULE_LICENSE("GPL");
3.Makefile file
obj-m += driver.o KDIR:=/linux/linux-4.1.15 PWD?=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean
4, Operation results
After compiling the above code, send the drivers to the development board respectively, and then load the drivers. The loading sequence does not affect the matching results.
First, load the device driver and print the information of successful registration.
Use the following command to view the devices generated under the platform bus.
ls /sys/bus/platform/devices
Then load the driver driver and print the information of successful registration and matching.
In order to verify that the matching results are independent of the loading order, we uninstall the two drivers first, and you can see that they both print the unloading information.
Then load the driver first.
It can be seen that loading the driver first does not print the information of successful matching. The information of successful matching is written in driver.c. at the same time, the "beep_test" defined in the code does not appear in the device.
We load the device driver again.
Not only the information of successful matching is printed, but also the device appears. This is the same as the result of loading the device driver first and then the driver driver. Therefore, the loading sequence of device and driver drivers will not affect the matching result.
summary
The above is all about registering platform bus devices and platform bus drivers under Linux. We should pay attention to the idea of platform bus model, which will help us simplify problems and improve efficiency.