linux driven learning note platform

Posted by scott_ttocs46 on Tue, 22 Feb 2022 07:57:07 +0100

preface

In the linux kernel, we can't find code similar to the previous demo type. For linux, code reusability is very important, otherwise there will be a lot of junk code in the kernel, resulting in a large number of kernel files. For example, the same module has different drivers on different platforms. In this case, one hardware corresponds to multiple driver files. Obviously, it is not allowed to exist in the linux kernel. Therefore, the concept of bus, driver and device is put forward in linux kernel.

When we register a device (driver) bus with the system, we will see if there is a matching driver (device). If so, we will establish contact with each other.
A large number of drivers in the linux kernel adopt this method, and the drivers get the board in a standard way_ Info. In this way, the original manufacturer of the chip has done the driving work. When we get the chip, we only need to write the device driver according to the original standard. This approach achieves the driven layered isolation idea.

Platform device driver

Based on the above methods, linux provides a virtual bus Platform, and the corresponding device is platform_device, and the corresponding driver is platform_driver

Bus

struct bus_type {
	const char		*name; //Bus name
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	const struct attribute_group **bus_groups;/* Bus properties */
	const struct attribute_group **dev_groups;/* Device properties */
	const struct attribute_group **drv_groups;/* Drive properties */

	int (*match)(struct device *dev, struct device_driver *drv);/*Matching function*/
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);/* Add environment variable*/
	int (*probe)(struct device *dev);/*Drive matching*/
	int (*remove)(struct device *dev);/*Drive unloading*/
	void (*shutdown)(struct device *dev);/*Execute on shutdown*/

	int (*online)(struct device *dev);
	int (*offline)(struct device *dev);
	/*
	Now the general sleep and wake-up are in dev_ pm_ Execute in Ops
	*/
	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;//Power management

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;//Private data
	struct lock_class_key lock_key;
};

The bus is matched according to the registered device or driver through the match function. The two parameters of the match function pointer are device and device respectively_ Driver type, that is, device and driver.

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/*Device tree matching method*/
	if (of_driver_match_device(dev, drv))
		return 1;

	/*ACPI Matching mode*/
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/*id_table Matching mode*/
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/*name Field matching*/
	return (strcmp(pdev->name, drv->name) == 0);
}

device_ Of in driver_ match_ Table member, which holds the compatible matching table of the driver. The compatible attribute of each device node in the device tree will be combined with of_ match_ Compare all members in the table to see if there are the same entries. If there are any, it means that the device matches the driver. After the device and driver match successfully, the probe function will be executed.

drive

struct device_driver {
	const char		*name;
	struct bus_type		*bus;
	...
	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;
	...
};

struct platform_driver {
	int (*probe)(struct platform_device *);//Match successfully loaded
	int (*remove)(struct platform_device *);//uninstall
	void (*shutdown)(struct platform_device *);//Shutdown processing
	/*
		Sleep wake
	*/
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	/*
		device_driver Member of_device_id,acpi_device_id, name matching method
	*/
	struct device_driver driver;
	/*
		id_table Matching mode
	*/
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};
int platform_driver_register (struct platform_driver *driver);//Register platform driver
void platform_driver_unregister(struct platform_driver *driver);//Uninstall platform driver

Template

/*
* platform Driven probe function
* This function will be executed after the driver is successfully matched with the device
*/
static int xxx_probe(struct platform_device *dev){
	return 0;
}
static int xxx_remove(struct platform_device *dev){	
	return 0;
}
/* Device tree matching list */
static const struct of_device_id xxx_of_match[] = {
	{ .compatible = "xxx_xxx"},
	{ /* Sentinel */ }
};
static struct platform_driver xxx_driver = {
	.driver = {
		.name = "xxx",
		.of_match_table = xxx_of_match,
	},
	.probe = xxx_probe,
	.remove = xxx_remove,
};
static int __init xxx_init(void){
	return platform_driver_register(&xxx_driver);
}
static void __exit xxx_exit(void){
	platform_driver_unregister(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin);

equipment

struct platform_device {
	const char	*name;//Device name for name matching
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;//Number of resources
	struct resource	*resource;//Resources, peripheral registers, etc

	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

struct resource {
	resource_size_t start;//Start information
	resource_size_t end;//Termination information
	const char *name;
	unsigned long flags;//Resource type
	struct resource *parent, *sibling, *child;
};
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);

Template

/* Register address definition*/
#define REGI1_BASE (0X20000000) / * first address of peripheral 1 register*/
#define REGI2_ Base (0x0200000) / * first address of peripheral 2 register*/
#define REGISTER_LENGTH 4
/* resources */
static struct resource xxx_resources[] = {
	[0] = {
		.start = REGI1_BASE ,
		.end = (REGI1_BASE + REGISTER_LENGTH - 1),
		.flags = IORESOURCE_MEM,//Memory type
	},
	[1] = {
		.start = REGI2_BASE ,
		.end = (REGI2_BASE + REGISTER_LENGTH - 1),
		.flags = IORESOURCE_MEM,
	},
};
static struct platform_device xxx_device= {
	.name = "xxx",
	.id = -1,
	.num_resources = ARRAY_SIZE(xxx_resources),
	.resource = xxx_resources,
};
static int __init xxx_init(void){
return platform_device_register(&xxx_device);
}
static void __exit xxx_exit(void){
	platform_device_unregister(&xxx_device);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");

demo

platform_device.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
/* Register address definition*/
#define REGI1_BASE (0X20000000) / * first address of peripheral 1 register*/
#define REGI2_BASE (0XC2000000) / * first address of peripheral 2 register*/
#define REGISTER_LENGTH 4
/* resources */
static struct resource demo_device_resources[] = {
	[0] = {
		.start = REGI1_BASE ,
		.end = (REGI1_BASE + REGISTER_LENGTH - 1),
		.flags = IORESOURCE_MEM,//Memory type
	},
	[1] = {
		.start = REGI2_BASE ,
		.end = (REGI2_BASE + REGISTER_LENGTH - 1),
		.flags = IORESOURCE_MEM,
	},
};
static void	demo_release(struct device *dev)
{
	printk("demo device released!\r\n");	
}
static struct platform_device demo_device= {
	.name = "demo-test",
	.id = -1,
    .dev = {
        .release = demo_release,
    },
	.num_resources = ARRAY_SIZE(demo_device_resources),
	.resource = demo_device_resources,
};
static int __init demo_device_init(void){
	return platform_device_register(&demo_device);
}
static void __exit demo_device_exit(void){
	platform_device_unregister(&demo_device);
}
module_init(demo_device_init);
module_exit(demo_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");

platform_driver.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#define DEMO_CNT 			 one 		  	/*  Number of equipment numbers*/
#define DEMO_NAME 			 "demo" 	/*  Name*/
#define MEM_BUFFER_SIZE 1000
/* demo Equipment structure */
struct demo_dev{
	dev_t devid;			/* Equipment number 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class class;		/* class 		*/
	struct device *device;	/* equipment 	 */
	int major;				/* Main equipment No	  */
	int minor;				/* Secondary equipment No   */
	unsigned char mem[MEM_BUFFER_SIZE];
};
struct demo_dev demo;	/* demo equipment */

static struct file_operations demo_fops = {
	.owner = THIS_MODULE,
};
char* strcpy(char* dest, const char* src) {
	char* tmp = dest;
	while ((*dest++ = *src++) != '0');
	return tmp;
}
static ssize_t std_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%s\n",demo.mem);
}
static ssize_t std_store(struct device *dev,  
	struct device_attribute *attr, const char *buf, size_t len)
{
	strcpy(demo.mem,buf);
	return len;
}
static DEVICE_ATTR_RW(std);


static struct attribute *demo_class_attrs[] = {
	&dev_attr_std.attr,
	NULL,
};

static const struct attribute_group demo_group = {
	.attrs = demo_class_attrs,
};

static const struct attribute_group *demo_groups[] = {
	&demo_group,
	NULL,
};
static int demo_probe(struct platform_device *dev)
{
    struct resource *source[2];
	int ressize[2];
    int i = 0;
	printk(" driver match device is success !!!\n");
    
    /* 1,Get resources */
	for (i = 0; i < 2; i++) {
		source[i] = platform_get_resource(dev, IORESOURCE_MEM, i); /* MEM type resources */
		if (!source[i]) {
			dev_err(&dev->dev, "No MEM resource for always on\n");
            /*
                Normal here is to return
            */
			//return -ENXIO;
		}else{
            ressize[i] = resource_size(source[i]);	
         printk(" souce [%#x]  ressize [%d]\n",source[i]->start,ressize[i]);
        }
		
	}
	/* 1,Create device number */
	if (demo.major) {		/*  Defines the equipment number */
		demo.devid = MKDEV(demo.major, 0);
		register_chrdev_region(demo.devid, DEMO_CNT, DEMO_NAME);
	} else {						/* No device number defined */
		alloc_chrdev_region(&demo.devid, 0, DEMO_CNT, DEMO_NAME);	/* Application equipment No */
		demo.major = MAJOR(demo.devid);	/* Get the master device number of the assigned number */
		demo.minor = MINOR(demo.devid);	/* Get the secondary equipment number of the assigned number */
	}
	printk("major=%d,minor=%d\r\n",demo.major, demo.minor);	
	
	/* 2,Initialize cdev */
	demo.cdev.owner = THIS_MODULE;
	cdev_init(&demo.cdev, &demo_fops);
	
	/* 3,Add a cdev */
	cdev_add(&demo.cdev, demo.devid, DEMO_CNT);

	/* 4,Create class */
	demo.class.owner = THIS_MODULE;
	demo.class.name = DEMO_NAME;
	demo.class.dev_groups = demo_groups;
	class_register(&demo.class);
	/* 5,Create device */
	demo.device = device_create(&demo.class, NULL, demo.devid, NULL, DEMO_NAME);
	if (IS_ERR(demo.device)) {
		return PTR_ERR(demo.device);
	}
	
	return 0;
}
static int demo_remove(struct platform_device *dev)
{
	/* Unregister character device driver */
	cdev_del(&demo.cdev);/*  Delete cdev */
	unregister_chrdev_region(demo.devid, DEMO_CNT); /* Logout equipment number */

	device_destroy(&demo.class, demo.devid);
	class_unregister(&demo.class);
    return 0;
}
/* Device tree matching list */
static const struct of_device_id demo_of_match[] = {
	{ .compatible = "demo-trees" },
	{ /* Sentinel */ }
};

/* platform Drive structure */
static struct platform_driver demo_driver= {
	.driver		= {
		.name	= "demo-test",			/* Driver name, used to match the device */
        .of_match_table	= demo_of_match, /* The device tree matching table is used to match the device tree */
	},
	.probe		= demo_probe,
	.remove		= demo_remove,
};
static int __init demo_init(void)
{
	return platform_driver_register(&demo_driver);
}

static void __exit demo_exit(void)
{
	platform_driver_unregister(&demo_driver);
}
module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");

dts file add

demo-tree {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "demo-trees";
		status = "okay";
	};

This instance loads the device and driver modules without changing the device tree, and matches the name s of the device and driver. After successful matching, the probe function can read the device resources
After adding the device tree code, you can only load the driver module, or match it successfully, and run the probe function of the driver module
Without modifying the device tree, only the driver module is loaded, and the probe function does not run.

Topics: Linux