Android 与linux一样使用设备驱动来访问硬件设备,设备节点文件是设备驱动的逻辑文件,应用程序使用设备节点文件来访问设备驱动程序,linux使用mknod来创建设备节点文件,Android 有自己法子。
Android 使用Init 进程来创建设备节点文件,分两种情况:静态节点文件和动态节点文件,以应对已经定义好的冷插拔和系统运行起来后插入的热插拔设备。
若要添加新的用户定义的新设备需要在此结构体中添加相应信息。
init 首先调用device_init() 函数,创建一个socket 来接收uevent,再通过cold_boot() 调用do_coldboot()对内核启动时注册到/sys下的驱动程序进行冷插拔处理,do_coldboot会启动uevent,在handler_device_fd()中接收uevent信息,并写入到uevent struct 中,调用handle_device_event()创建节点文件,先创建所有的子目录,然后调用make_device()创建节点文件。
init 对于热插拔的动态设备,使用事件处理循环来完成,使用poll()监听来自驱动程序的uevent, 然后调用handle_device_fd()创建设备节点。
我们可以在system/core/init/下的init.c和devices.c中找到答案:
init.c中
1. int main(int argc, char
2. {
3. ...
4.
5. /* Get the basic filesystem setup we need put
6. * together in the initramdisk on / and then we'll
7. * let the rc file figure out the rest.
8. */
9. "/dev", 0755);
10. "/proc", 0755);
11. "/sys", 0755);
12.
13. "tmpfs", "/dev", "tmpfs", 0, "mode=0755");
14. "/dev/pts", 0755);
15. "/dev/socket", 0755);
16. "devpts", "/dev/pts", "devpts", 0, NULL);
17. "proc", "/proc", "proc", 0, NULL);
18. "sysfs", "/sys", "sysfs", 0, NULL);
19.
20. for(;;) {
21. ...
22. if
23. handle_device_fd(device_fd);
24.
25. if
26. handle_property_set_fd(property_set_fd);
27. if
28. handle_keychord(keychord_fd);
29. }
30.
31. return
32. }
我们再来看看handle_device_fd(),该函数定义在devices.c中
1. void handle_device_fd(int
2. {
3. ...
4. handle_device_event(&uevent);
5. handle_firmware_event(&uevent);
6. }
7. }
而handle_device_event定义如下:
1. static void handle_device_event(struct
2. {
3. ...
4. if(!strcmp(uevent->action, "add")) {
5. make_device(devpath, block, uevent->major, uevent->minor);
6. return;
7. }
8. ...
9. }
make_device定义如下:
1. static void make_device(const char *path, int block, int major, int
2. {
3. ...
4. mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
5. dev = (major << 8) | minor;
6. ...
7. setegid(gid);
8. mknod(path, mode, dev);
9. chown(path, uid, -1);
10. setegid(AID_ROOT);
11. }
我们看看get_device_perm如下实现:
1. static mode_t get_device_perm(const char
2. {
3. mode_t perm;
4.
5. if
6. return
7. else if
8. return
9. else
10. struct
11. struct
12. struct
13.
14. /* Check partners list. */
15. list_for_each(node, &devperms_partners) {
16. struct
17. dp = &perm_node->dp;
18.
19. if
20. if
21. continue;
22. else
23. if
24. continue;
25. }
26. /* Found perm in partner list. */
27. *uid = dp->uid;
28. *gid = dp->gid;
29. return
30. }
31. /* Default if nothing found. */
32. *uid = 0;
33. *gid = 0;
34. return
35. }
36. }
我们最后可以看到在devperms中定义了要生成的设备节点:
1. static struct
2. "/dev/null", 0666, AID_ROOT, AID_ROOT, 0 },
3. "/dev/zero", 0666, AID_ROOT, AID_ROOT, 0 },
4. "/dev/full", 0666, AID_ROOT, AID_ROOT, 0 },
5. "/dev/ptmx", 0666, AID_ROOT, AID_ROOT, 0 },
6. "/dev/tty", 0666, AID_ROOT, AID_ROOT, 0 },
7. "/dev/random", 0666, AID_ROOT, AID_ROOT, 0 },
8. "/dev/urandom", 0666, AID_ROOT, AID_ROOT, 0 },
9. "/dev/ashmem", 0666, AID_ROOT, AID_ROOT, 0 },
10. "/dev/binder", 0666, AID_ROOT, AID_ROOT, 0 },
11.
12. /* logger should be world writable (for logging) but not readable */
13. "/dev/log/", 0662, AID_ROOT, AID_LOG, 1 },
14.
15. /* the msm hw3d client device node is world writable/readable. */
16. "/dev/msm_hw3dc", 0666, AID_ROOT, AID_ROOT, 0 },
17.
18. /* gpu driver for adreno200 is globally accessible */
19. "/dev/kgsl", 0666, AID_ROOT, AID_ROOT, 0 },
20.
21. /* these should not be world writable */
22. "/dev/diag", 0660, AID_RADIO, AID_RADIO, 0 },
23. "/dev/diag_arm9", 0660, AID_RADIO, AID_RADIO, 0 },
24. "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 },
25. "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 },
26. "/dev/ttyMSM0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 },
27. "/dev/ttyHS0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 },
28. "/dev/uinput", 0660, AID_SYSTEM, AID_BLUETOOTH, 0 },
29. "/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 },
30. "/dev/tty0", 0660, AID_ROOT, AID_SYSTEM, 0 },
31. "/dev/graphics/", 0660, AID_ROOT, AID_GRAPHICS, 1 },
32. "/dev/msm_hw3dm", 0660, AID_SYSTEM, AID_GRAPHICS, 0 },
33. "/dev/input/", 0660, AID_ROOT, AID_INPUT, 1 },
34. "/dev/eac", 0660, AID_ROOT, AID_AUDIO, 0 },
35. "/dev/cam", 0660, AID_ROOT, AID_CAMERA, 0 },
36. "/dev/pmem", 0660, AID_SYSTEM, AID_GRAPHICS, 0 },
37. "/dev/pmem_adsp", 0660, AID_SYSTEM, AID_AUDIO, 1 },
38. "/dev/pmem_camera", 0660, AID_SYSTEM, AID_CAMERA, 1 },
39. "/dev/oncrpc/", 0660, AID_ROOT, AID_SYSTEM, 1 },
40. "/dev/adsp/", 0660, AID_SYSTEM, AID_AUDIO, 1 },
41. "/dev/snd/", 0660, AID_SYSTEM, AID_AUDIO, 1 },
42. "/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 },
43. "/dev/msm_camera/", 0660, AID_SYSTEM, AID_SYSTEM, 1 },
44. "/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 },
45. "/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 },
46. "/dev/akm8973_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 },
47. "/dev/akm8973_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 },
48. "/dev/bma150", 0640, AID_COMPASS, AID_SYSTEM, 0 },
49. "/dev/cm3602", 0640, AID_COMPASS, AID_SYSTEM, 0 },
50. "/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 },
51. "/dev/lightsensor", 0640, AID_SYSTEM, AID_SYSTEM, 0 },
52. "/dev/msm_pcm_out", 0660, AID_SYSTEM, AID_AUDIO, 1 },
53. "/dev/msm_pcm_in", 0660, AID_SYSTEM, AID_AUDIO, 1 },
54. "/dev/msm_pcm_ctl", 0660, AID_SYSTEM, AID_AUDIO, 1 },
55. "/dev/msm_snd", 0660, AID_SYSTEM, AID_AUDIO, 1 },
56. "/dev/msm_mp3", 0660, AID_SYSTEM, AID_AUDIO, 1 },
57. "/dev/audience_a1026", 0660, AID_SYSTEM, AID_AUDIO, 1 },
58. "/dev/tpa2018d1", 0660, AID_SYSTEM, AID_AUDIO, 1 },
59. "/dev/msm_audpre", 0660, AID_SYSTEM, AID_AUDIO, 0 },
60. "/dev/msm_audio_ctl", 0660, AID_SYSTEM, AID_AUDIO, 0 },
61. "/dev/htc-acoustic", 0660, AID_SYSTEM, AID_AUDIO, 0 },
62. "/dev/vdec", 0660, AID_SYSTEM, AID_AUDIO, 0 },
63. "/dev/q6venc", 0660, AID_SYSTEM, AID_AUDIO, 0 },
64. "/dev/snd/dsp", 0660, AID_SYSTEM, AID_AUDIO, 0 },
65. "/dev/snd/dsp1", 0660, AID_SYSTEM, AID_AUDIO, 0 },
66. "/dev/snd/mixer", 0660, AID_SYSTEM, AID_AUDIO, 0 },
67. "/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 },
68. "/dev/qemu_trace", 0666, AID_SYSTEM, AID_SYSTEM, 0 },
69. "/dev/qmi", 0640, AID_RADIO, AID_RADIO, 0 },
70. "/dev/qmi0", 0640, AID_RADIO, AID_RADIO, 0 },
71. "/dev/qmi1", 0640, AID_RADIO, AID_RADIO, 0 },
72. "/dev/qmi2", 0640, AID_RADIO, AID_RADIO, 0 },
73. /* CDMA radio interface MUX */
74. "/dev/ts0710mux", 0640, AID_RADIO, AID_RADIO, 1 },
75. "/dev/ppp", 0660, AID_RADIO, AID_VPN, 0 },
76. "/dev/tun", 0640, AID_VPN, AID_VPN, 0 },
77. { NULL, 0, 0, 0, 0 },
78. };
在Android中,没有独立的类似于udev或者mdev的用户程序,这个功能集成到了init中做了。代码见:system/core/init/init.c文件,如下:
if (ufds[0].revents == POLLIN)
handle_device_fd(device_fd);
其中handle_device_fd(device_fd)函数在system/core/init/devices.c中实现,参数device_fd 由函数device_init()->open_uevent_socket()->socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)函数调用返回。
函数handle_device_fd(device_fd)中,根据传进来的device_fd参数,调用recv(fd, msg, UEVENT_MSG_LEN, 0)函数,将内核探测到的设备并通过NETLINK机制传过来的socket描述符转化成消息。接着调用parse_event(msg, &uevent);函数将消息翻译成uevent事件,并将改事件传递给handle_device_event(&uevent)函数。
handle_device_event(&uevent)函数中,依据参数uevent->subsystem类型创建dev下的相应目录,如:/dev/graphics。紧接着根据uevent->action是"add"还是"remove"来实现设备节点的创建与删除。如果uevent->action是"add",则调用make_device(devpath, block, uevent->major, uevent->minor)函数生成设备节点。如果uevent->action是"remove",则调用unlink(devpath)对设备节点进行删除。