Wednesday, June 19, 2013

Device Driver Diary : Uevent and Netlink Socket for Kernel-Platform Communication

uevent is a kernel state change notification method. So whenever there is any change in hardware, which needs to be conveyed to platform,platform uses uevent to send those. There will be a daemon from platform side which will monitors these events and respond accordingly.
So uevent is nothing but a notification which consists detailed information about this event. For example :
add@/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/host6 ACTION=add DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/host6 SUBSYSTEM=scsi DEVTYPE=scsi_host SEQNUM=1165
This tells many things like what is the device name, device id, subsystem, device type, action(add,remove).
so lets see how it adds the variable info:
to do the same it needs a method add_uevent_var().Here we put the information. for example :
env = kzalloc (sizeof (struct kobj_uevent_env), GFP_KERNEL);
retval = add_uevent_var (env, “ACTION =% s”, action_string);
retval = add_uevent_var (env, “DEVPATH =% s”, devpath);
retval = add_uevent_var (env, “SUBSYSTEM =% s”, subsystem);
now as string is ready, we will call
kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[]);
/*for usb specific*/
kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE,disconnected);
So if you want to send any event to platform, you have to use this method ultimately.
Now lets understand a little bit insight of how kobject_event_env() works. Basically this method uses the netlink socket inside.Netlink sockets are the socket which is used to intiate connection from kernel side. It works like this.
1. We create a socket at kernel side, and associate a buffer with this buffer.
2. It calls netlink_broadcast_filtered()
3. It calls do_one_braodcast()
4. It calls netlink_broadcast_deliver() which puts socket into a socket buffer queue. Also it calls sk->dk_data_ready()
5. finally it calls sock_def_readable, it signals the user side socket that the data at kernel side is ready to be read.
At Platform side we have a file at hardware/libhardware_legacy/uevent/ named as uevent.c. This have a function, uvent_init(), it creates the netlink socket at platform side using socket(), and associate the local buffer to it using bind() function. Then in uevent_next_event() function,it will keep on checking the socket for new data. uevent_next_event() function will be called by application.
Important Files :
1. hardware/libhardware_legacy/uevent/uevent.c
2. kernel/lib/kobject_uevent.c
3. kernel/net/netlink/af_netlink.c
4. kernel/drivers/usb/gadget/android.c [for usb related uevents]
5. frameworks/base/core/java/android/os/UEventObserver.java
6. frameworks/base/services/java/com/android/server/usb/UsbService.java
References :
1. http://blog.csdn.net/dfysy/article/details/7330919 [translate it, it will help you a lot about how uevent flow]
2. http://www.linuxjournal.com/article/7356 [complete tutorial on netlink socket, with example]
3. Man page of socket(), bind() and netlink() @ http://linux.die.net
I hope this will help you. Still I have one doubt that how netlink socket is different from normall polling, if you have clue, please comment below.
EDIT : Mr. Rami Rosen told me that its the generic kernel implementation that when a write operation is done on the queue of the socket, a flag which says that the socket can be awakened is set. So until then poll is in blocking state.

No comments: