Linux platform device framework driver

Linux platform device framework driver

  The platform device framework (platform) divides a driver into two parts, the device layer and the driver layer , and binds the device and the driver through the bus model. Every time a device is registered in the system, it will be matched with a driver. Similarly, every registered driver will also be matched with a device.
   Generally, Linux devices and drivers usually need to be connected to a bus. For devices that are attached to PCI, USB, I2C, SPI, etc., this is naturally not a problem, but in embedded systems, SOC systems integrate Independent peripheral controllers, peripherals attached to the SOC memory space, etc. are not attached to this type of bus.
   Based on the model structure of the bus framework, the platform device framework model (platform) is derived under Linux. The platform device bus is a virtual bus called the platform bus . The corresponding device layer is called platform_device ; the driver layer is called platform_driver . The device layer and the driver layer perform matching management through the platform device bus.

1. Platform Device Framework Features

  1. The platform model adopts a layered structure, which divides a device driver into two parts:
      platform device (platform_device) and platform driver (platform_driver) .
  2. The platform device registers the resources of the device itself into the kernel, which can be managed uniformly by the kernel.
  3. Separation of hardware resources and driver interfaces, maintenance and transplantation of compiled codes.
    insert image description here

2. Platform device bus related interface functions

2.1 Device layer interface function

  The system of each device is saved through the device structure struct platform_device . The structure prototype is defined in include/linux/platform_devcie.h .

struct platform_device {
    
    
	const char	* name; //设备名字,驱动层和设备层匹配标志
	int		id;//通常填-1
	struct device	dev;//设备结构体信息
	u32		num_resources;//资源个数
	struct resource	* resource;//资源内容
	const struct platform_device_id	*id_entry;
	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;
	/* arch specific additions */
	struct pdev_archdata	archdata;
};
  • struct device dev structure information

      The struct device dev structure is used to implement the device model. There are many members in this structure, and the structure prototype is defined in include/linux/devcie.h . We often care about two members:
      platform data pointer: void *platform_data;
      resource release function: void (*release)(struct device *dev);
      platform data pointer platform_data is a void * type pointer, which can be sent to the driver layer To pass arbitrary data, the release resource release function interface must be implemented at the device layer, otherwise an error will be reported when releasing resources at the device layer.

  A few of these member structures are listed below:

struct device {
    
    
	const char *init_name; /*逻辑设备的名字*/
	struct device_type *type; /* 设备类型 */
	struct bus_type *bus; /* 设备所属的总线类型 */
	struct device_driver *driver;/* 指向开辟 struct device 结构 driver 指针*/
	void		*platform_data;	/* 平台设备指针 */
	dev_t devt;  /* 存放设备号 dev_t,creates the sysfs"dev" */
	struct class *class;  /* 设备所属类*/
	void	(*release)(struct device *dev);/*设备资源释放函数*/
};
  • struct resource * resource structure information

      struct resource * resource structure is used to store device resource content information. The structure definition location: include/linux/ioport.h
struct resource {
    
    
	resource_size_t start; //资源起始地址
	resource_size_t end; //资源结构地址
	const char *name;//资源名字
	unsigned long flags;//资源类型
	struct resource *parent, *sibling, *child;
};

  In the resource type flags in the resource structure , the relevant macro definition location of the resource type: include/linux/ioport.h , the commonly used resource types are as follows:

#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
#define IORESOURCE_IO		0x00000100 //IO 空间, 一般在 X86 框架中存在, ARM 一般没有
#define IORESOURCE_MEM		0x00000200 //内存空间,占用的是 CPU 4G 统一编址空间
#define IORESOURCE_IRQ		0x00000400 //中断号
#define IORESOURCE_DMA		0x00000800 //DMA
  • Device layer registration function

int platform_device_register(struct platform_device *pdev)
function: register platform device;
formal parameters: pdev --device structure;
return value: return 0 on success, return other values ​​on failure;

  • Device layer logout function

void platform_device_unregister(struct platform_device *pdev)
function: register platform device;
parameter: pdev --device structure;

  • Add multiple devices to the kernel

int platform_add_devices(struct platform_device **devs, int num)
function: register multiple devices to the kernel;
formal parameters: pdev --device structure;
   num - the number of registered devices
return value: return 0 if successful, return other values ​​if failed ;

2.2 Driver layer interface function

  The driver layer saves relevant information through the struct platform_driver structure, and the structure definition location: include/linux/devcie.h
  In this structure, the interface function must be implemented:

  • Resource matching function: int (*probe)(struct platform_device *)
  • Resource release function: int (*remove)(struct platform_device *);
  • Driver resource structure: struct device_driver driver;

  A driver layer can match multiple device layers. If you want to match multiple device layers at the same time, you can complete the matching through the id_table pointer.

struct platform_driver {
    
    
	int (*probe)(struct platform_device *);//资源匹配函数
	int (*remove)(struct platform_device *);//资源释放函数
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;//驱动资源结构体
	const struct platform_device_id *id_table;//匹配多多设备时需要填写
};
  • struct device_driver driver structure information

struct device_driver There is a member name   in the driver structure that must be filled in. When the id_table pointer is not implemented, the device layer and the driver layer complete resource matching through this member.

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;

	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;
};
  • const struct platform_device_id *id_table structure information

  This structure is also used to match device resources, and one driver layer can match multiple device layers at the same time.

struct platform_device_id {
    
    
	char name[PLATFORM_NAME_SIZE]; //资源匹配参数
	kernel_ulong_t driver_data
			__attribute__((aligned(sizeof(kernel_ulong_t)))); //匹配设备层的 void *platform_data数据
};
  • Driver layer registration and deregistration functions

//driver registration function
int platform_driver_register(struct platform_driver *drv)
//driver cancellation function
void platform_driver_unregister(struct platform_driver *drv)

3. Application example of platform device framework

3.1 Example of Device Layer Registration

  Device layer registration steps:

  1. Fill in the struct device structure, and fill in the device resource information struct resource * resource;
  2. Call the device layer registration function platform_device_register();
  3. Call the unregister function platform_device_unregister when unregistering;
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/platform_device.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

static void platform_release(struct device *dev)
{
    
    
	printk("资源释放完成\n");
}
static struct resource	resource[]=
{
    
    
	[0]={
    
    
			.start=EXYNOS4X12_GPM4(0),
			.end=EXYNOS4X12_GPM4(0),
			.name="led1",
			.flags=IORESOURCE_MEM
		},
	[1]={
    
    
			.start=EXYNOS4X12_GPM4(1),
			.end=EXYNOS4X12_GPM4(1),
			.name="led2",
			.flags=IORESOURCE_MEM
		},		
		
};

struct platform_device pdev=
{
    
    
	.name="led_dev",
	.id=-1,
	.dev=
	{
    
    
		.release=platform_release,//资源释放函数
	},
	.num_resources=sizeof(resource)/sizeof(resource[0]),
	.resource=resource,	
};

static int __init wbyq_platform_dev_init(void)
{
    
    
	platform_device_register(&pdev);
    return 0;
}
/*驱动释放*/
static void __exit wbyq_platform_dev_cleanup(void)
{
    
    
	/*注销设备层*/
	platform_device_unregister(&pdev);

}
module_init(wbyq_platform_dev_init);//驱动入口函数
module_exit(wbyq_platform_dev_cleanup);//驱动出口函数

MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 platform_dev Driver");

3.2 Driver layer registration example

  Driver layer registration steps:

  1. Fill in the struct platform_driver structure to implement the resource matching function and resource release function;
  2. Call the device layer registration function platform_driver_register();
  3. When logging out, call the logout function platform_driver_unregister;
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/platform_device.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

static int platform_probe(struct platform_device *dev)
{
    
    
	printk("资源匹配成功\n");
	printk("资源个数:%d\n",dev->num_resources);
	struct resource * resource=platform_get_resource(dev,IORESOURCE_MEM,0);
	if(resource)
	{
    
    
		printk("资源名:%s\tstart=%x\tend=%x\n",resource->name,resource->start,resource->end);
	}
	return 0;
	
}
static int platform_remove(struct platform_device *dev)
{
    
    
	printk("资源释放成功\n");
	return 0;
}
static struct platform_device_id id_table[]=
{
    
    
	[0]=
	{
    
    
		.name="led_dev"
	},
	[1]=
	{
    
    
		.name="tiny4412_dev"
	},
	
};
static struct platform_driver drv=
{
    
    
	.probe=platform_probe,
	.remove=platform_remove,
	.driver=
	{
    
    
		.name="platform_drv",
	},
	.id_table=id_table,
};
static int __init wbyq_platform_drv_init(void)
{
    
    
	platform_driver_register(&drv);
	printk("驱动层平台设备注册成功\n");
    return 0;
}
/*驱动释放*/
static void __exit wbyq_platform_drv_cleanup(void)
{
    
    
	/*注销设备层*/
	platform_driver_unregister(&drv);
	printk("驱动层平台设备注销成功\n");

}
module_init(wbyq_platform_drv_init);//驱动入口函数
module_exit(wbyq_platform_drv_cleanup);//驱动出口函数

MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 platform_drv Driver");

3.3 Makefile

KER_ADD=/home/wbyq/src_pack/linux-3.5
all:
	make -C $(KER_ADD) M=`pwd` modules
	#arm-linux-gcc main.c -o app 
	cp ./*.ko  /home/wbyq/src_pack/rootfs/code 
	make -C $(KER_ADD) M=`pwd` modules clean
	rm app -f
obj-m +=platform_drv.o platform_dev.o platform_dev2.o

4 running effect

insert image description here

Guess you like

Origin blog.csdn.net/weixin_44453694/article/details/126868999