In the Linux Device Driver Model excerpt (2), we track the registration process of the driver in amba_pl011.c to demonstrate how a device is unified into the Linux unified device model, amba_pl011 is a generic device. The following uses Android goldfish tty driver as an example to describe this process again (/Drivers/Char/goldfish_tty.c ):
Goldfish_tty_init (void)-> platform_driver_register (& goldfish_tty );
Platform_driver_register () and goldfish_tty are defined as follows:
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 (* suspend_late) (struct platform_device *, pm_message_t State );
INT (* resume_early) (struct platform_device *);
INT (* resume) (struct platform_device *);
Struct device_driver driver;
};
Struct goldfish_tty {
Spinlock_t lock;
Uint32_t base;
Uint32_t IRQ;
Int opencount;
Struct tty_struct * tty;
Struct console;
};
Static struct platform_driver goldfish_tty = {
. Probe = goldfish_tty_probe,
. Remove = goldfish_tty_remove,
. Driver = {
. Name = "goldfish_tty"
}
};
Struct bus_type platform_bus_type = {
. Name = "Platform ",
. Dev_attrs = platform_dev_attrs,
. Match = platform_match,
. Uevent = platform_uevent,
. Suspend = platform_suspend,
. Suspend_late = platform_suspend_late,
. Resume_early = platform_resume_early,
. Resume = platform_resume,
};
Int driver_register (struct device_driver * DRV)
{
Int ret;
If (DRV-> bus-> probe & DRV-> probe) |
(DRV-> bus-> remove & DRV-> remove) |
(DRV-> bus-> shutdown & DRV-> shutdown ))
Printk (kern_warning "Driver '% s' needs updating-Please use"
"Bus_type methods/N", DRV-> name );
Ret = bus_add_driver (DRV );
If (RET)
Return ret;
Ret = driver_add_groups (DRV, DRV-> groups );
If (RET)
Bus_remove_driver (DRV );
Return ret;
}
Int platform_driver_register (struct platform_driver * DRV)
{
DRV-> driver. Bus = & platform_bus_type;
If (DRV-> probe)
DRV-> driver. Probe = platform_drv_probe;
If (DRV-> remove)
DRV-> driver. Remove = platform_drv_remove;
If (DRV-> shutdown)
DRV-> driver. Shutdown = platform_drv_shutdown;
If (DRV-> suspend)
DRV-> driver. Suspend = platform_drv_suspend;
If (DRV-> resume)
DRV-> driver. Resume = platform_drv_resume;
Return driver_register (& DRV-> driver );
}
From the code, we can see that a call path is as follows:
Platform_driver_register-> driver_register-> bus_add_driver-> bus_for_each_dev (DRV-> bus, null, DRV, _ driver_attach)->__ driver_attach-> really_probe
Really_probe-> driver_sysfs_add (Dev)-> sysfs_create_link create a link under/sys/Drivers /.
-> Dev-> bus-> probe (Dev )? No suitable functions can be called.
-> DRV-> probe (Dev)-> platform_drv_probe-> goldfish_tty_probe ();//
From this analysis, we can see that (2) and (3) are still different. (2) use register twice to register the UART protocol first, and then register the AMBA bus, but in (3) in this example, we use platform_driver_register (struct platform_driver) for one-time registration. for bus and Protocol problems, we will use the probe callback function later.
Last question: Regarding the probe process, we can find that in goldfish_tty_probe, the key function is platform_get_resource (). This function can obtain the base_addr and IRQ of the device. However, the platform_get_resource process depends on the request_resource function in the initialization phase, as shown in arch/ARM/kernel/setup. C. Another form is direct resource assignment, for example:
Static struct resource udc_resources [] = {
[0] = {
. Start = at91rm9200_base_udp,
. End = at91rm9200_base_udp + sz_16k-1,
. Flags = ioresource_mem,
},
[1] = {
. Start = at91rm9200_id_udp,
. End = at91rm9200_id_udp,
. Flags = ioresource_irq,
},
};
Static struct platform_device at91rm9200_udc_device = {
. Name = "at91_udc ",
. ID =-1,
. Dev = {
. Platform_data = & udc_data,
},
. Resource = udc_resources,
. Num_resources = array_size (udc_resources ),
};
The ID in platform_device is like this. If Id =-1, the device name is the defined name. As shown above, it is at91_udc, but if it is not-1, the name format will be name. it may indicate that the same name can have multiple devices.
Directly assign a value to platform_device using udc_resources, and then use probe to check the device and find a usable device.
At the end, let's take a look at the relationships between these structs:
Struct platform_device {
Const char * Name;
Int ID;
Struct device dev;
U32 num_resources;
Struct resource * resource;
};
Struct resource {
Resource_size_t start;
Resource_size_t end;
Const char * Name;
Unsigned long flags;
Struct resource * parent, * sibling, * child;
};
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 (* suspend_late) (struct platform_device *, pm_message_t State );
INT (* resume_early) (struct platform_device *);
INT (* resume) (struct platform_device *);
Struct device_driver driver;
};
Therefore, platform_driver is similar to device_driver and platform_device is similar to device. For the conversion between them, there are the following functions for your reference:
Struct platform_driver * DRV = to_platform_driver (struct * Device-> driver );
Struct platform_device * Dev = to_platform_device (struct * device)
# Define to_platform_driver (DRV) (container_of (DRV), struct platform_driver, Driver ))
# Define to_platform_device (x) container_of (x), struct platform_device, Dev)
Looking at these two macros, we will find that (3) is more abstract than (2), and the uniformity should be better. In addition, more and more complicated drivers, such as MMC and USB, use platform_device and platform_driver.