Xunwei Embedded Linux Driver Development Notes (3) - Platform Bus Model

1. Introduction to the platform bus model

1. What is the platform bus model?
The platform bus model is also called the platform bus model. It is a bus virtualized by the Linux kernel, not a real wire.
The platform bus model is to divide the original driver C file into two C files, one is device.c and the other is driver.c. Put the stable and unchanging in driver.c, and put it in device if necessary. c inside. Put the common ones in dirver.c, and change them in device.c.

2. Why is there a platform bus model?
(1) Can improve code reusability
(2) Reduce repetitive code.
Device bus driver
device.c driver.c

3. The advantages of the platform bus model.
(1) The driver and device codes are distinguished, and the reusability is strong, which can reduce duplication!

(2) Mounted on the platform bus.

4. How to write a driver designed with the platform bus model?
One is device.c and the other is driver.c. Then register device.c and driver.c respectively.

Platform buses are matched by name, which is actually a string comparison.

2. Register the Platform device

1. The platform bus registers a device
device.c The hardware resources are written in it. The hardware resources here refer to the address of the register, the interrupt number, the clock and other hardware resources. In the Linux kernel, we use a structure to describe hardware resources.

struct platform_device {
    
     
	const char *name; 
	平台总线进行匹配的时候用到的 name,/sys/bus......
	int id 
	设备id,一般写-1 
	struct device dev;
	内嵌的 device 结构体
	u32 num_resources; 
	资源的个数
	struct resource *resource; 
	device里面的硬件资源(寄存器地址和中断时钟等)};
 struct resource {
    
     
 resource_size_t start; 
 资源的起始 
 resource_size_t end; 
 资源的结束 
 const char *name; 
 资源的名字 
 unsigned long flags; 
 资源的类型 }; 
#define IORESOURCE_IO 	IO 的内存
#define IORESOURCE_MEM 	表述一段物理内存
#define IORESOURCE_IRQ 	表示中断

Device part of the driver code:

#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_res[] = 
{
    
    
    [0] = {
    
    
        .start  = 0x020AC000,//开始  蜂鸣器地址
        .end    = 0x020AC003,//结束
        .flags  = IORESOURCE_MEM,//描述的是一段物理地址
        .name   = "GPIO5_DR"
    }
};

struct platform_device beep_device = {
    
    
    .name = "beep_test",//匹配名称
    .id = -1,
    .resource = beep_res,
    .num_resources = ARRAY_SIZE(beep_res),
    .dev = {
    
    
        .release = beep_release
    }
};

static int device_init(void)
{
    
    
    printk("hello world");
    //加载到内核
    return platform_device_register(&beep_device);
}

static void device_exit(void)
{
    
    
    printk("bye bye");
    platform_device_unregister(&beep_device);
}

module_init(device_init);

module_exit(device_exit);

MODULE_LICENSE("GPL");

Experimental phenomena

insert image description hereCheck the /sys/bus/platform/devices directory, you can see that the
insert image description heredriver has been loaded and uninstalled successfully
insert image description here

3. Register the platform driver

To write driver.c
, first define a structure variable of platform_driver, and then implement each member variable in the structure, then when the driver and device match successfully, the probe function will be executed, so the key point after the successful match is the writing of the probe function .

 struct platform_driver {
    
    
			int (*probe)(struct platform_device *);//函数指针
			匹配函数:当driver和device匹配成功之后,执行probe函数
			int (*remove)(struct platform_device *);
			当driver和device任意一个remove的时候,执行这个函数
			void (*shutdown)(struct platform_device *);
			当设备收到shutdown,会执行这个函数
			int (*suspend)(struct platform_device *, pm_message_t state);
			当设备收到suspend,会执行这个函数休眠
			int (*resume)(struct platform_device *);
			当设备收到resume,会执行这个函数唤醒
			struct device_driver driver;
			const struct platform_device_id *id_table;
			//优先匹配id_table 的.name成员
			bool prevent_deferred_probe;
 };

 struct device_driver {
    
    
			const char      *name;
			//这个是我们匹配设备时候要用到的名字
			struct bus_type     *bus;
			struct module       *owner;
			const char      *mod_name;  /* used for built-in modules */
			bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
			const struct of_device_id   *of_match_table;
			const struct acpi_device_id *acpi_match_table;
			int (*probe) (struct device *dev);
			int (*remove) (struct device *dev);
			void (*shutdown) (struct device *dev);
			int (*suspend) (struct device *dev, pm_message_t state);
			int (*resume) (struct device *dev);
			const struct attribute_group **groups;
			const struct dev_pm_ops *pm;
			struct driver_private *p;
};

Device driver code writing:

#include<linux/init.h>
#include<linux/module.h>
#include <linux/platform_device.h>

int beep_probe(struct platform_device *pdev){
    
    
    printk("beep_probe");
    return 0;
}

int beep_remove(struct platform_device *pdev){
    
    
    printk("beep_remove");
    return 0;
}

//id_table 的优先级比较高
const struct platform_device_id beepid_table = 
{
    
    
    .name = "123"
};

struct platform_driver beep_driver = {
    
    
     .probe = beep_probe,
     .remove = beep_remove,
     .driver = {
    
    
         .owner = THIS_MODULE,
         .name = "beep_test"
     },
     .id_table = &beepid_table
 };

static int beep_driver_init(void)
{
    
    
    int ret =0;
    ret = platform_driver_register(&beep_driver);
    if (ret<0)
    {
    
    
        printk("platform_driver_register is error");
        return ret;
    }
    
    printk("platform_driver_register is error");
    return 0;
}

static void beep_driver_exit(void)
{
    
    
    platform_driver_unregister(&beep_driver);
    printk("bye bye");
}

module_init(beep_driver_init);

module_exit(beep_driver_exit);

MODULE_LICENSE("GPL");

Experimental phenomena

insert image description here
Device and driver comprehensive test:
The results are as follows: Enter the probe function~~~! ! !
insert image description here

Fourth, the platform bus probe function writing

Probe function writing ideas:
(1) Obtain resources from device.c
Method 1. Obtain directly, not recommended.

 printk("beep_res is %s\n",pdev->resource[0].name);//直接获取,打印成员变量

Method 2, use the function to get

struct resource *platform_get_resource_byname(struct platform_device *,unsigned int,const char *);

(2) Register miscellaneous/character devices, improve the file_operation structure, and generate device nodes.
Register before registering.

 #define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n    ), (name), 0)

Register to use


int beep_probe(struct platform_device *pdev){
    
    

    printk("beep_probe");

    printk("beep_res is %s\n",pdev->resource[0].name);//方法1、直接获取,打印成员变量

    //方法2 结构体成员 资源类型,同类中第几个
    beep_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);
    if (beep_mem == NULL)
    {
    
    
        printk("platform_get_resource is error\n");
        return -EBUSY;
    }
    printk("beep_res start is 0x%x\n",beep_mem->start);
    
    printk("beep_res end is 0x%x\n",beep_mem->end);



    //登记使用
    beep_mem_temp = requset_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1,"beep");

    if (beep_mem_temp == NULL) 
    {
    
    
        printk("requset_mem_region is error\n");
        goto err_region;
    }

    return 0;
err_region:
    release_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1);

}

The difference from the original is the use of miscellaneous devices and character devices in the probe function, which separates the device and the driver. Misc devices were previously registered in misc_init.

#include<linux/init.h>
#include<linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#define GPIO5_DR 0x020AC000

unsigned int *vir_gpio_dr;//存放映射完成的虚拟地址的首地址

int misc_open(struct inode *inode,struct file *file)
{
    
    
    printk("hello misc_open\n");
    
    return 0;
}

int misc_close(struct inode *inode,struct file *file)
{
    
    
    printk("bye bye\n");
    
    return 0;
}

int misc_read(struct file *file,char __user *ubuf,size_t size,loff_t *loff_t)
{
    
    
    char kbuf[64] = "hello";

    if(copy_to_user(ubuf,kbuf,sizeof(kbuf))!=0)
    {
    
    
        printk("copy to user error\n");
        return -1;
    }  
    return 0;
}

int misc_write(struct file *file,const char __user *ubuf,size_t size,loff_t *loff_t)
{
    
    
   char kbuf[64] = {
    
    0};

    if(copy_from_user(kbuf,ubuf,size)!=0)  //ubuf  传送进来 保存到 kbuf中
    {
    
    
        printk("copy_from_user\n");
        return -1;
    }  
    printk("kbuf is %s\n",kbuf);

    if (kbuf[0] == 1)
        *vir_gpio_dr |= (1 <<1);//open
    else if (kbuf[0] == 0)
        *vir_gpio_dr &= ~(1 <<1);//close
    
    return 0;

}

struct file_operations  misc_fops ={
    
    
    .owner      = THIS_MODULE,
    .open       = misc_open,
    .release    = misc_close,
    .read       = misc_read,
    .write      = misc_write
};

struct miscdevice misc_dev = {
    
    
    .minor = MISC_DYNAMIC_MINOR,
    .name = "hello_misc",
    .fops = &misc_fops
};

struct resource *beep_mem;

struct resource *beep_mem_temp;

int beep_probe(struct platform_device *pdev){
    
    

     int ret = 0;

    printk("beep_probe");

    printk("beep_res is %s\n",pdev->resource[0].name);//方法1、直接获取,打印成员变量

    //方法2 结构体成员 资源类型,同类中第几个
    beep_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);
    if (beep_mem == NULL)
    {
    
    
        printk("platform_get_resource is error\n");
        return -EBUSY;
    }
    printk("beep_res start is 0x%x\n",beep_mem->start);
    
    printk("beep_res end is 0x%x\n",beep_mem->end);

#if 0
    //登记使用
    beep_mem_temp = requset_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1,"beep");

    if (beep_mem_temp == NULL) 
    {
    
    
        printk("requset_mem_region is error\n");
        goto err_region;
    }
#endif

/************************************************************/

    vir_gpio_dr = ioremap(beep_mem->start,4);

    if(vir_gpio_dr == NULL) 
    {
    
    
        printk("GPIO5_DR ioremap error\n");
        return -EBUSY;
    }
    printk("GPIO5_DR ioremap ok\n");

/************************************************************/

    ret = misc_register(&misc_dev);//注册
    if (ret<0)
    {
    
    
        printk("misc_register is error\n");
        return -1;
    }
    printk("misc_register is successful\n");

    return 0;

// err_region:
//     release_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1);

}

int beep_remove(struct platform_device *pdev){
    
    

    printk("beep_remove");
    return 0;
}

//id_table 的优先级比较高
const struct platform_device_id beepid_table = 
{
    
    
    .name = "123"
};

struct platform_driver beep_driver = {
    
    
     .probe = beep_probe,
     .remove = beep_remove,
     .driver = {
    
    
         .owner = THIS_MODULE,
         .name = "beep_test"
     },
     .id_table = &beepid_table
 };

static int beep_driver_init(void)
{
    
    
    int ret =0;
    ret = platform_driver_register(&beep_driver);
    if (ret<0)
    {
    
    
        printk("platform_driver_register is error");
        return ret;
    }
    
    printk("platform_driver_register is error");
    
    return 0;
}

static void beep_driver_exit(void)
{
    
    
    platform_driver_unregister(&beep_driver);

    misc_deregister(&misc_dev);//卸载

    iounmap(vir_gpio_dr);

    printk("bye bye");

}

module_init(beep_driver_init);

module_exit(beep_driver_exit);

MODULE_LICENSE("GPL");


Experimental results

mount first
insert image description here

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324093300&siteId=291194637