摘要
oFono支持动态的检测modem设备的插拔。本文主要讲解oFono如何实现对modem的插拔进行检测的。这篇文章也解释了plugin是如何扩展oFono功能而不是仅仅作为modem/atom驱动的容器。
1.modem设备检测分析
1.1初始化
当设备检测plugin被oFono载入到系统后,系统会对plugin进行初始化。
1 static int detect_init(void) 2 { 3 udev_ctx = udev_new(); 4 if (udev_ctx == NULL) { 5 ofono_error("Failed to create udev context"); 6 return -EIO; 7 } 8 9 udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev"); 10 if (udev_mon == NULL) { 11 ofono_error("Failed to create udev monitor"); 12 udev_unref(udev_ctx); 13 udev_ctx = NULL; 14 return -EIO; 15 } 16 /*创建hash table*/ 17 modem_list = g_hash_table_new_full(g_str_hash, g_str_equal, 18 NULL, destroy_modem); 19 20 udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL); 21 udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL); 22 23 udev_monitor_filter_update(udev_mon); 24 25 udev_start(); 26 27 return 0; 28 }
plugin进行初始化时会创建udev_monitor【1】,并初始化一个g_hash_table【2】,然后使能设备发现。
注:创建的hash table有一下的特点,modem节点被删除时,会自动调用destory_modem对modem进行析构。关于udev_monitor和g_hash_table的细节可以在最后的reference中找到。
1 static void udev_start(void) 2 { 3 GIOChannel *channel; 4 int fd; 5 6 DBG(""); 7 8 if (udev_monitor_enable_receiving(udev_mon) < 0) { 9 ofono_error("Failed to enable udev monitor"); 10 return; 11 } 12 /*枚举当前系统下的所有USB modem*/ 13 enumerate_devices(udev_ctx); 14 15 fd = udev_monitor_get_fd(udev_mon); 16 /*创建一个设备监听通道*/ 17 channel = g_io_channel_unix_new(fd); 18 if (channel == NULL) 19 return; 20 /*将设备监听通道读事件加入到时间循环当中*/ 21 udev_watch = g_io_add_watch(channel, 22 G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, 23 udev_event, NULL); 24 25 g_io_channel_unref(channel); 26 }
从代码来看设备发现的初始化主要做了一下三件事情:
1. 枚举出当前系统中已经存在的USB modem设备,并在oFono中创建对应的modem
2. 监听udev_monitor
3. 将udev_monitor读事件加入到主事件循环中去。
1.2 设备的插入/拔出
这一节将介绍当设备插入/拔出时,设备发现plugin所做的动作。我们知道在初始化之后,系统就会去检测udev_monitor的状态,一旦状态发生改变,就会出发udev_event函数来处理所发生的udev事件。
1 static gboolean udev_event(GIOChannel *channel, GIOCondition cond, 2 gpointer user_data) 3 { 4 struct udev_device *device; 5 const char *action; 6 7 if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { 8 ofono_warn("Error with udev monitor channel"); 9 udev_watch = 0; 10 return FALSE; 11 } 12 13 device = udev_monitor_receive_device(udev_mon); 14 if (device == NULL) 15 return TRUE; 16 17 action = udev_device_get_action(device); 18 if (action == NULL) 19 return TRUE; 20 /*设备插入*/ 21 if (g_str_equal(action, "add") == TRUE) { 22 if (udev_delay > 0) 23 g_source_remove(udev_delay); 24 /*检查设备合法性*/ 25 check_device(device); 26 /*创建并注册modem到oFono中*/ 27 udev_delay = g_timeout_add_seconds(1, check_modem_list, NULL); 28 } 29 /*设备拔出*/ 30 else if (g_str_equal(action, "remove") == TRUE) 31 remove_device(device); 32 33 udev_device_unref(device); 34 35 return TRUE; 36 }
1.2.1设备拔出
当检测到modem被拔出后,plugin会将这个modem device从系统中删除。
1.2.2设备插入
当检测到modem插入时,plugin会检查设备的合法性,然后调用check_modem_list将创建并注册合法的modem到oFono当中去。
需要注意的是,check_modem_list并不是马上执行的,而是会被推迟到1s之内没有新设备被发现之后才执行。这样做的目的是,很多USB modem都会虚拟出若干个串口分别适应不同的工作,这样如果不等所有串口初始化完毕就立马执行check_modem_list可能就会导致modem初始化失败。
2.最后
这就是我对oFono设备发现过程的理解,也许存在偏差和不对的地方,欢迎大家指正。
References:
【1】https://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/libudev-udev-monitor.html
【2】https://developer.gnome.org/glib/2.30/glib-Hash-Tables.html#g-hash-table-new-full