First, based on linux-3.18.20, Mac driver
Second, the start time:
The so-called "probe" means that in the Linux kernel, if there are device and device_driver with the same name, the kernel executes the probe callback function in Device_driver, which is the entrance to all driver, You can perform actions such as hardware device initialization, character device registration, device file operation, OPS registration, and so on ("remove" is its anti-operation, which occurs when either device or Device_driver logs off from the kernel.)
- Auto-trigger when registering variables of struct device type into the kernel (device_register,device_add,device_create_vargs,device_create)
- Auto-trigger when registering variables of struct device_driver type into the kernel (driver_register)
- Manually find all Device_driver under the same bus, and if there is a driver with the same name as the specified device, perform the probe operation (Device_attach)
- Manually find all the device under the same bus, if there is a device with the same name as the specified driver, perform the probe operation (Driver_attach)
- Call driver's probe interface on its own and bind the driver to a device structure in that interface----that is, set Dev->driver (Device_bind_driver)
Third, the process
3.1 Register Platform Driver
ret = Platform_driver_register (&usrmac_dev_driver);
#define Platform_driver_register (DRV) Platform_driver_register (DRV, This_module)
__platform_driver_register (DRV, This_module)
{
...
Return Driver_register (&drv->driver);
}
int Driver_attach (struct device_driver *drv)
{
...
ret = Bus_add_driver (DRV);
...
}
int bus_add_driver (struct device_driver *drv)
{
...
Return Bus_for_each_dev (Drv->bus, NULL, DRV, __driver_attach);
...
}
This function does not pass parameters to __driver_attach.
int Bus_for_each_dev (struct bus_type *bus, struct device *start, void *data, int (*FN) (struct device *, void *))
{
....
Klist_iter_init_node (&bus->p->klist_devices, &i, (start &start->p->knode_bus:null));
while (dev = next_device (&i)) &&!error)
Error = fn (dev, data);
....
}
Branch One: Assignment I->i_klist, i->i_cur
Because start is null, the third parameter n passed is null
void Klist_iter_init_node (struct klist *k, struct klist_iter *i,
struct Klist_node *n)
{
I->i_klist = k;
I->i_cur = n;
if (n)
Kref_get (&N->N_REF);
}
which
I->i_klist = k = &bus->p->klist_devices
I->i_cur = N = (start &start->p->knode_bus:null) = NULL;
Sub-Two:
static struct device *next_device (struct klist_iter *i)
{
struct Klist_node *n = Klist_next (i);
struct device *dev = NULL;
struct Device_private *dev_prv;
if (n)
{
DEV_PRV = To_device_private_bus (n);
dev = dev_prv->device;
}
return dev;
}
#define To_device_private_bus (obj) container_of (obj, struct device_private, Knode_bus)
Parameters:
I for
struct Klist_iter {
struct klist*i_klist;
struct klist_node*i_cur;
};
is assigned a value of
I->i_klist = k;
I->i_cur = n;
Next_device (&i), because the first node is the head node and needs to start from the next
struct Klist_node *n = Klist_next (i);
N is
struct Klist_node {
void*n_klist;/* never access directly */
struct List_headn_node;
struct krefn_ref;
};
struct Kref {
atomic_t RefCount;
};
Klist_iter_init_node (&bus->p->klist_devices, &i, (start &start->p->knode_bus:null)) The function is to define a klist_iter point to this klist so that it can be used directly later
struct klist_node *klist_next (struct klist_iter *i)
{
...
struct Klist_node *last = i->i_cur;
if (last)
{
Do not execute here
}
Else
Next = To_klist_node (I->i_klist->k_list.next);
I->i_cur = NULL;
while (Next! = To_klist_node (&i->i_klist->k_list))
{
if (Likely (!knode_dead (next)))
{
Kref_get (&NEXT->N_REF);
I->i_cur = Next;
Break
}
Next = To_klist_node (Next->n_node.next);
}
...
}
static struct Klist_node *to_klist_node (struct list_head *n)
{
Return container_of (n, struct klist_node, n_node);
}
The N_node pointer with I->i_klist->k_list.next is removed, but next refers to a head pointer to the N_node address deviation (List_head includes head and next pointers).
The while loop is a loop from the first target To_klist_node (I->i_klist->k_list.next), when looping the head node again To_klist_node (&i->i_klist->k_ List) (This is a circular list that will always be recycled again).
Also an end condition, when looping to Knode_dead (next) is a true break, however, likely shows that next is usually not dead.
Klist_iter find the appropriate stop search and find the device_private device here.
This structure is the parameter passed into the probe function.
After the parameter is found, proceed to return Bus_for_each_dev (Drv->bus, NULL, DRV, __driver_attach); error = fn (dev, data); the __driver_attach function.
The first parameter, Dev, produces the index just while (dev = next_device (&i)) &&!error).
static int __driver_attach (struct device *dev, void *data)
{
...
if (!dev->driver)
Driver_probe_device (DRV, Dev);
...
}
int Driver_probe_device (struct device_driver *drv, struct device *dev)
{
...
ret = Really_probe (dev, DRV);
...
}
static int really_probe (struct device *dev, struct device_driver *drv)
{
...
if (dev->bus->probe)
{
ret = Dev->bus->probe (dev);
if (ret)
Goto probe_failed;
}
else if (drv->probe)
{
ret = Drv->probe (dev);
if (ret)
Goto probe_failed;
}
...
}
static struct Platform_driver Usrmac_dev_driver = {
. Probe = Usrmac_dev_probe,
. remove = Usrmac_dev_remove,
. Suspend = Usrmac_dev_suspend,
. Resume = Usrmac_dev_resume,
. Driver =
{
. Owner = This_module,
. Name = Usrmac_driver_name,
. of_match_table = Usrmac_of_match,
},
};
static int usrmac_dev_probe (struct platform_device *pdev)
{
struct Platform_device *pdev that Klist_iter found.
}
Embeded Linux probe