1. Platform bus, device and driver
In the device driver model of Linux 2.6, the bus binds the device and the driver with three entities: Bus, device, and driver. When the system registers a device, it looks for a matching driver. On the contrary, when the system registers a driver, it looks for a matching device, the matching is completed by the bus.
A real Linux Device and driver usually need to be mounted on a bus, and they are attached to PCI, USB, I2
C, SPI and other devices, this is naturally not a problem, but in the embedded system, the independent peripheral controllers integrated in the SoC system and peripherals attached to the SOC memory space are not attached to such
Line. Based on this background, Linux invented a virtual bus called platform bus. The corresponding device is called platform_device, and the driver becomes
Platform_driver.
2. Steps for compiling the platform driver
1. Register a device
1. Write the platform_device device structure for the device
Struct platform_device {
Const char * Name; // device name
Int ID; // device ID
Struct device dev;
U32 num_resources; // number of resources used by devices
Struct resource * resource; // The resource used by the device.
};
Struct resource struct:
Struct resource {
Resource_size_t start; // resource start address
Resource_size_t end; // resource end address
Const char * Name;
Unsigned long flags; // Resource Type
Struct resource * parent, * sibling, * child;
};
Note: struct
In the resource structure, we usually care about the start, end, and flags fields. www.linuxidc.com respectively indicates the start value, end value, and class of the resource.
Flags can be ioresource_io, ioresource_mem, ioresource_irq, ioresource_dma, etc.
The meaning of start and end will change with flags. For example, when flags is ioresource_mem, start and end indicate
The start address and end address of the memory occupied by platform_device. When flags are ioresource_irq, start and end indicate
The start and end values of the interrupt number used by platform_device. If only one interrupt number is used, the start and end values are the same. For resources of the same type, there can be multiple copies, for example
If a device occupies two memory areas, two ioresource_mem resources can be defined.
Note: Device Name and ID
The platform_device.dev.bus_id is the canonical name for the devices. It's built from two components:
* Platform_device.name... which is also used to for driver matching.
* Platform_device.id... the device instance number, or else "-1" to indicate there's only one.
These are concatenated, so name/ID "serial"/0 indicates bus_id
"Serial.0", and "serial/3" indicates bus_id "serial.3"; both wocould use
The platform_driver named "serial". While "my_rtc"/-1 wocould be bus_id
"My_rtc" (no instance id) and use the platform_driver called "my_rtc ".
2. register the structure of the platform_device device.
Int platform_device_register (struct platform_device * pdev); // register a device
Int platform_add_devices (struct platform_device ** pdevs, int ndev); // register multiple devices
Platform device registration example:
Register an instance with the NAND flash device in the linux2.6.26.8 kernel:
Nand controller resources:
Linux2.6.26.8/ARCH/ARM/plat-s3c24xx/Devs. c
Static struct resource initi_nand_resource [] = {
[0] = {
. Start = s3c2410_pa_nand,
. End = s3c2410_pa_nand + s3c24xx_sz_nand-1,
. Flags = ioresource_mem,
}
};
Struct platform_device initi_device_nand = {
. Name = "s3c2410-nand ",
. ID =-1,
. Num_resources = array_size (cloud_nand_resource ),
. Resource = maid,
};
Register NAND Flash as a platform device:
Linux2.6.26.8/ARCH/ARM/plat-s3c24xx/common-smdk.c:
Static struct platform_device _ initdata * smdk_devs [] = {
& Amp; cloud_device_nand,
...
};
Void _ init smdk_machine_init (void)
{
...
Initi_device_nand.dev.platform_data = & smdk_nand_info; // pay attention to the value assignment here. The data assigned here is used in the probe function of the NAND Flash Driver.
Platform_add_devices (smdk_devs, array_size (smdk_devs); // register the device
S3c2410_pm_init ();
}
Note:
In Memory cases, the memory and IRQ resources associated with
Platform device are not enough to let the device's driver work. Board
Setup code will often provide additional information using the device's
Platform_data field to hold additional information.
The platform_data format is customized. For example, we use platform_data: initi_device_nand.dev.platform_data = & smdk_nand_info;
2. register the driver
1. Compile the platform_driver struct for the driver.
Struct platform_driver {
INT (* probe) (struct platform_device *); // Device Detection
INT (* remove) (struct platform_device *); // remove the device
Void (* shutdown) (struct platform_device *); // disable the device
INT (* suspend) (struct platform_device *, pm_message_t State); // pause the device
INT (* suspend_late) (struct platform_device *, pm_message_t State );
INT (* resume_early) (struct platform_device *);
INT (* resume) (struct platform_device *); // device recovery
Struct device_driver driver;
};
2. register the platform_driver struct.
Int platform_driver_register (struct platform_driver *);
Platform device registration example:
The NAND flash device driver registration instance in the linux2.6.26.8 kernel:
Linux/Drivers/MTD/NAND/S3C2410. C:
Static struct platform_driver s3c2410_nand_driver = {
. Probe = s3c2410_nand_probe,
. Remove = s3c2410_nand_remove,
. Suspend = s3c24xx_nand_suspend,
. Resume = s3c24xx_nand_resume,
. Driver = {
. Name = "s3c2410-nand ",
. Owner = this_module,
},
};
Static int _ init s3c2410_nand_init (void)
{
Printk ("s3c24xx NAND driver, (c) 2004 simtec electronics/N ");
Platform_driver_register (& s3c2412_nand_driver );
Platform_driver_register (& s3c2440_nand_driver );
Return platform_driver_register (& s3c2410_nand_driver); // register the device driver
}
Module_init (s3c2410_nand_init );
Note: The driver binding is automatically executed by the kernel. When a driver matches a device, the kernel calls the probe function of the driver to detect and initialize the device.
Device binding ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Driver binding is already med automatically by the driver core,
Invoking driver probe () after finding a match between device and
Driver. If the probe () succeeds, the driver and device are bound
Usual. There are three different ways to find such a match:
-Whenever a device is registered, the drivers for that bus are checked for matches. Platform devices shoshould be registered very
Early during system boot.
-When a driver is registered using platform_driver_register (),
All unbound devices on that bus are checked for matches.
Drivers usually register later during booting, or by module
Loading.
-Registering a driver using platform_driver_probe () works just
Like using platform_driver_register (), register t that the driver
Won't be probed later if another device registers. (which is
OK, since this interface is only for use with non-hotpluggable
Devices .)
Attachment: How does the kernel determine whether the device on the platform bus matches the driver?
A bus_type instance is defined for the platform bus in the system.
Platform_bus_type,
It is defined as follows:
1 struct bus_type platform_bus_type = {
2. Name = "Platform ",
3. dev_attrs = platform_dev_attrs,
4. Match = platform_match,
5. uevent = platform_uevent,
6. PM = platform_pm_ops_ptr,
7 };
8 export_symbol_gpl (platform_bus_type );
Here we need to focus on its match () member function, which shows how the platform_device and platform_driver match each other, as shown in code listing 4.
Code list 4: The match () member function of platform_bus_type
1 static int platform_match (struct device * Dev, struct
Device_driver * DRV)
2 {
3 struct platform_device * pdev;
4
5 pdev = container_of (Dev, struct platform_device, Dev );
6 Return (strncmp (pdev-> name, DRV-> name, bus_id_size) =
0 );
7}
From the 6th line of code listing 4, we can see that matching platform_device and platform_driver mainly depends on whether the name fields of the two are the same.
3. Cancel the device and driver
1. log out of the driver
Void platform_driver_unregister (struct platform_driver *);
Example:
Linux/Drivers/MTD/NAND/S3C2410. C:
Static void _ exit s3c2410_nand_exit (void)
{
Platform_driver_unregister (& s3c2412_nand_driver );
Platform_driver_unregister (& s3c2440_nand_driver );
Platform_driver_unregister (& s3c2410_nand_driver );
}
Module_exit (s3c2410_nand_exit );
2. Cancel the device
Void platform_device_unregister (struct platform_device * pdev );