Principle of platform Platform Bus Driven by linux Device (3)



Equipment for data, drive for processing


1. Take led-s3c24xx.c as an example to analyze the registration process of platform devices and drivers.


The driving data structure for led is as follows:

static struct platform_driver s3c24xx_led_driver = {
	.probe		= s3c24xx_led_probe,
	.remove		= s3c24xx_led_remove,
	.driver		= {
		.name		= "s3c24xx_led",
		.owner		= THIS_MODULE,
	},
};

struct platform_driver The data structure is
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;
};


The device data structure for led is

static struct platform_device mini2440_led1 = {
	.name		= "s3c24xx_led",
	.id		= 1,
	.dev		= {
		.platform_data	= &mini2440_led1_pdata,
	},
};

The name of the device is the same as the name of the driver. The id value in the device data structure is used to distinguish different led devices, because there may be multiple LEDs on a board. This id value can be used to distinguish different led lights. This id value can be arranged at will by itself. For example, there are four leds, and the id value can be set to a number from 1 to 4. Another way is that we do not specify the id value. The id value is set to - 1. It represents the id of LED on the board, which is allocated by the kernel. platform_data in member dev is the data part of the device.

The data structure of struct platform_device is as follows

struct platform_device {
	const char	* name;
	int		id;
	struct device	dev;
	u32		num_resources;
	struct resource	* resource;

	const struct platform_device_id	*id_entry;

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

You can see that the dev member variable is indeed in the struct platform_device structure. Take a look at the data structure content of the struct device type of the dev variable in the struct platform_device structure.

struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	struct device_type	*type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	struct dev_pm_info	power;

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
	/* arch specific additions */
	struct dev_archdata	archdata;
#ifdef CONFIG_OF
	struct device_node	*of_node;
#endif

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
};


You can see that there is a void *platform_data in the struct device structure type; the member is a pointer that can point to any type, which is the data part of the device. Back to the data structure of led devices

static struct platform_device mini2440_led1 = {
	.name		= "s3c24xx_led",
	.id		= 1,
	.dev		= {
		.platform_data	= &mini2440_led1_pdata,
	},
};

platform_data, a member of struct device type dev, is a pointer that can point to any type. This position is a blank position, and we can write one by ourselves.

Things, binding the finished thing to the members of the platform_data, the void * type platform_data member variable refers to things left to us, because it is void * type, so we can define a type ourselves and then bind to it, this place can be said to be a unique data part of the device, because the kernel device and the device. Drivers are not designed to define everything for all devices, so with such a void * type of member variable, let's define the device-specific data part.

The platform_data member variable in the device data structure of the LED points to a data structure such as min2440_led1_pdata, which is a data structure defined by the person writing the LED device. Look at the data structure of this mini2440_led 1_pdata variable

static struct s3c24xx_led_platdata mini2440_led1_pdata = {
	.name		= "led1",
	.gpio		= S3C2410_GPB(5),
	.flags		= S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
	.def_trigger	= "heartbeat",
};

Look at the structure type struct s3c24xx_led_platdata of this mini2440_led1_pdata variable

struct s3c24xx_led_platdata {
	unsigned int		 gpio;
	unsigned int		 flags;

	char			*name;
	char			*def_trigger;
};

This is a self-defined data structure type, not a pre-defined data structure type in the kernel, because what platform_data members in the device point to is determined by us. This struct s3c24xx_led_platdata defines some data parts peculiar to led. Platform_data of different devices of platforms platform bus is definitely different. It is up to us to define this different place. The data structure that platforms_data members point to is the key to distinguish what devices platforms platform bus is, and the unique part of this device. .

For the unique data structure struct s3c24xx_led_platdata, which is defined by platform_data members in the LED device, gpio represents the quotation number used by the LED and the attributes of the LED corresponding to flags. The name indicates the name of the led, and def_trigger may indicate that the LED lamp is bound to other hardware.

Next, we continue to divide the min2440_led1_pdata variable, the data part specific to the LED device, and the porter's own definition.

static struct s3c24xx_led_platdata mini2440_led1_pdata = {
	.name		= "led1",
	.gpio		= S3C2410_GPB(5),
	.flags		= S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
	.def_trigger	= "heartbeat",
};

The name is led 1, the gpio offset used is S3C2410_GPB(5), the attribute flags is... And the def_trgiger value is heatbeat, indicating that the LED 1 will be used as a heartbeat lamp in the future.

After we have written the unique data part of the led, we bind it to the plotform_data member, and let this member point to the unique device data part of the LED we have written. Let's see how to bind it. It's a bit around.

static struct platform_device mini2440_led1 = {
	.name		= "s3c24xx_led",
	.id		= 1,
	.dev		= {
		.platform_data	= &mini2440_led1_pdata,
	},
};

The data structure of mini2440_led1_pdata device is bound to the platform_data member. The unique data part is realized as the data part of LED device. Finally, the data structure of mini2440_led1 device of struct plat_device type is filled in. Then, the mini2440_led1 variable is registered with register function, which represents one variable. One led device.


Summary: platform_data is actually the device-related data (such as gpio, interrupt number, device name, etc.) provided when the device is registered. When registering a platform device, we first provide the device data for the device. After our device and driver match, the match function will be called to identify the device and driver. Actively, matching the device and driver will be transferred from the device side to the driver side. The data provided is to transfer these data parts to the driver in the device data structure after matching the device and driver in the future. After the driver gets these data, the driver can know the specific information of the device through these data, and then operate the device. The advantage of this is that our driver source code does not carry data, only responsible for the operation of the hardware, but does not know which hardware we are specifically operating on. After the driver matches the device with the driver, the driver will know which device to operate after the device transfers the data we provide to the driver, because the data includes the device. The resources used (such as the GPIO corresponding to the device, the interrupt number used, etc.) and the driver has the operation method for this type of device, so after the driver gets the data of the device, it can operate the device directly with the operation method. The separation of data and operation logic is a good design logic, because this driver has generality. When the hardware is changed, that is, when the equipment is replaced with gpio, we do not need to change the source code of the driver, but when transplanting, we can change the resource of the equipment after replacing the hardware, and the source code of the driver is unchanged.


Since after the device and driver match, the device will transfer the unique data part of the device we provide to the driver, and then the driver can know which hardware to operate in his own way, then the device will transfer the data part to the driver. In the probe function, the device will transfer the data part to the driver.

static int s3c24xx_led_probe(struct platform_device *dev)
{
	struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;
	struct s3c24xx_gpio_led *led;
	int ret;

	led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);
	if (led == NULL) {
		dev_err(&dev->dev, "No memory for device\n");
		return -ENOMEM;
	}

	platform_set_drvdata(dev, led);

	led->cdev.brightness_set = s3c24xx_led_set;
	led->cdev.default_trigger = pdata->def_trigger;
	led->cdev.name = pdata->name;
	led->cdev.flags |= LED_CORE_SUSPENDRESUME;

	led->pdata = pdata;

	/* no point in having a pull-up if we are always driving */

	if (pdata->flags & S3C24XX_LEDF_TRISTATE) {
		s3c2410_gpio_setpin(pdata->gpio, 0);
		s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);
	} else {
		s3c2410_gpio_pullup(pdata->gpio, 0);
		s3c2410_gpio_setpin(pdata->gpio, 0);
		s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);
	}

	/* register our new led device */

	ret = led_classdev_register(&dev->dev, &led->cdev);
	if (ret < 0) {
		dev_err(&dev->dev, "led_classdev_register failed\n");
		kfree(led);
		return ret;
	}

	return 0;
}

We can see that there is a line of code starting in the led driver code probe function.

struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;

This line of code refers the data part of the device to the driver, because the driver's probe function defines a struct s3c24xx_led_platdata type pdata structure pointer, and the parameter of the probe function is

From the above analysis, we know that the data structure of this device has a member variable dev, which is a struct device type structure. The struct device type structure has a platform_data member variable, which is used to point to the device we write. The data part, in this led, the data type of this unique data part is s3c24xx_led_platdata, so we can see from the above code that the probe function of the driver code points to the unique data part platform_data of the LED device through the pointer of pdata, which is the process that the device transfers the data part we provide to the driver.


Keywords: Attribute

Added by alvinkoh on Wed, 26 Jun 2019 22:15:01 +0300