1、RIL运行流程
2、在init.rc文件里有如下形式的service定义:
3、service ril-daemon /衡痕贤伎system/bin/rild-l reference-ril.so -- -d /dev/tt烤恤鹇灭yUSB0class mainsocket rild stream 660 root radiosocket rild-debug stream 660 radio systemuser rootgroup radio cache inet misc audio log
4、其中,service定义也可写为service ril-daemon /system/bin/rild,此时关于参数的获取可通过设置如下变量处理:
5、rild.libpath=/system/lib/libreference-ril.sorild.libargs=-d/dev/ttyUSB0
6、在Android系统运行时会启动该服务,该服务会创建socket rild stream 660 root radio这样的一个socket,用于与framework里的上层代码进行通信,相应对应处理文件为:frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java,即RIL库与上层交互是通过Socket(socket rild),而与底层驱动是通过串口(/dev/ttyUSB0)。
7、上面涉及到的rild服务源码位于hardware/ril/rild目录,而使用的RIL库对应源码位于hardware/ril/reference-ril目录,而两者交互时会使用到的libril.so源码位于hardware/ril/libril目录。
8、下面是rild服务启动后加载RIL库及AT指令、上报数据的处理流程分析:
9、rild服务的启动流程:
10、其中,“开启eventLoop线程”(libril.so)的流程如下:
11、如果上面线程的循环处理失败而退出循环,訇咀喃谆将会调用kill(0,SIGKILL)杀死自己所在进程后重启。循环中主要是处理timer_list、pending_list、watch忧溲枷茫_table三个队列,其中对pending_list队列的处理将是每个event的最后归宿,即会调用其回调函数并清出队列。
12、“运行RIL库的RIL_Init函数,并返回funcs回调函数指针”部分会调用到reference-ril.so里的RIL_Init()函数,该函数执行流程如下:
13、其中,s_rilenv对应到rild.c文件里s_rilEnv的内容如下:
14、static struct RIL_Env s_rilEnv = {RIL_onRequestComplete,RIL_onUnsolicitedResponse,RIL_requestTimedCallback};
15、而mainLoop()的执行流程如下:
16、上面的at_open(fd,onUnsolicited)处理过程为:初始化s_fd为AT口对应的fd,s_unsolHandler为onUnsolicited,然后再创建线程readerLoop()。
17、而readerLoop()则是循环从AT口(s_fd)或缓存里读一行AT指令数据(read盟敢势袂line()),如为NULL则退出循环,接下来对AT指令数据进行处理,如为SMS非请求数据(以+CMT:、+CDS:或+CBM:开头)则再读一行,如为NULL则退出循环,否则调用s_unsolHandler处理这两次读到的数据,如果不是SMS,则调用processLine()处理读到的AT数据,其最终是调用s_unsolHandler来处理非上层请求数据(即AT口自动返回数据,用于主动向上层上报)或将返回数据加入到sp_response->p_intermediates链头,让RIL库进行处理相应onRequest()上报。当循环退出后会设置s_readerClosed=1并发s_commandcond信号(在断ReaderClosed()中)。
18、上面的RIL_requestTimedCallback(initializeCallback, NULL, 媪青怍牙&TIMEVAL_0)相当于internalRequestTimedCallback()函数,其执行流程为:初始化p_info,配置其回调函数及参数成员p_callback和userParam为initializeCallback和NULL,创建p_info->event,其fd为-1,非persist,该event对应回调函数及参数为userTimerCallback(该回调处理最终调用上面的p_callback和参数userParam)和p_info,并加入timer_list函列(按升序),然后往s_fdWakeupWrite写入一空格,以唤醒队列处理线程,然后返回p_info。
19、而initializeCallback()的处理流程为:setRadioState(RAD诔罨租磊IO_STATE_OFF)先向上层上报RADIO状态为OF僻棍募暖F,然后发AT指令测试AT口能不能握手成功,成功后会再发送一系列AT指令来初始化模块,然后再通过AT指令确认模块是否正常工作,是的话就setRadioState(RADIO_STATE_ON)主动上报RADIO正常ON状态。
20、“注册funcs回调列表”(RIL_register(funcs))部分流程如下:
21、上面的listenCallback()函数对应执行流程如下:
22、上面中的process觊皱筠桡CommandsCallback()函数的执行流程为:从p_rs获取数据至p_record,如果获取成功,则pro艘早祓胂cessCommandBuffer()处理结束后继续从p_rs获取数据和处理这样的循环,如果获取失败或读至结尾,则退出循环,关闭s_fdCommand连接,删除s_commands_event事件,删除p_rs记录流,将s_pendingRequests队列里各元素的cancelled标志位置1,并将s_listen_event加入watch_table重启监听新连接操作。
23、而processCommandBuffer()则将上层新发下来的命令请求里的request和token两数据项封包为RequestInfo类型的pRI中的元素,并将其加入s_pendin爿讥旌护gRequests队列,并执行对应request的s_command中对应的dispatchFunction函数(生成不同request对应的参数再通过s_callbacks.onRequest()去调用RIL库里onRequest()里对应分支的请求)。
24、至此,整稍僚敉视个RIL部分的大致流程已清楚了,其中RILD部分主要是启动整个RIL进程,进行队列的处理(相当于整个队列管理核心),而libril.so中相应函数主要是处理上层请求、AT处理后数据上报,而reference-ril.so中则是处理串口AT指令的获取,利用到libril.so中的AT指令解析等操作。
25、相关文件说明:
26、at罕铞泱殳channel.c:AT口指令收发处理at_tok.c:AT指令返回内容解析处理radiooptio荏鱿胫协ns.c:与RILD对应debug socket通信,调试命令。reference-ril.c:RIL库核心文件,处理AT口指令交互、solicited和unsolicited事件及上报处理结果。ril.cpp:各类回调函数、socket通信处理、上层请求处理。rild.cpp:整个RIL服务入口,处理参数、队列创建和管理等管理相关进程的创建。ril_event.cpp:rild涉及到的相关函数定义。
27、(注:上述流程图中结尾没有再往下的流程则表示该函数执行结束后返回)
28、RIL主要函数
29、.AT指令发送
30、a. at_send_command_singleline
31、该函数在发送AT指令后对返回值进行解析,前提是返回值是只有一行的情况才使用。
32、b. at_send_command_multiline
33、该函数在发送AT指令后对返回值进行解析,前提是返回值是多行的情况下使用,可适用于一条AT指令返回多行数据或多条AT指令同时发送返回多行数据的情况。
34、c. at_send_command
35、该函数仅仅发送AT指令,不对其返回值进行解析。
36、.AT指令返回值解析
37、a. at_tok_start
38、该函数用于将解析的数据指针定位到AT指令头后面位置(即返回值的首个冒号“:”后面)。
39、b. at_tok_nextint
40、该函数用于获取返回的AT数据对应指针位置后的首个Integer数据,并将指针指向数据之后。
41、c. at_tok_nextbool
42、该函数获取的是boolean数据,同样会将指针后移。
43、d. at_tok_nextstr
44、该函数获取的是String数据,同样会将指针后移。
45、e. at_tok_hasmore
46、该函数用于判断还有没有数据,即是不是到达数据尾部,不会进行指针操作。
47、.数据上报
48、a 主动式
49、RIL_onUnsolicitedResponse,当在RIL库中需要向上层主动上报数据时,需用该函数进行处理。
50、b 被动式
51、RIL_onRequestComplete,当上层向RIL库请求处理,并且处理后向上层上报数据时,需用该函数进行处理。
52、.移植涉及修改的函数
53、移植时主要是reference-ril.c文件,相关函数为:
54、static voidonRequest (int request, void *data, size_t datalen, RIL_Token t);stati艘早祓胂c void onUnsolicited (const char *s, const char *sms_pdu);
55、主要把整个RILD服务配置好并正常启动,将对应的Request进行代码完善及匹配处理,其中onRequest()处理上层请求后上报处理的数据,而onUnsolicited()处理AT口未请求返回数据并且处理后上报处理的数据。
56、拔号上网、DHCP及上网状态上报
57、在3G模块时,使用的是PPP协议进行拔号上网及PPPD服务进行DHCP信息的获取,其使用到的是串口进行数据传输,而在LTE模块中,使用串口已无刮茕栓双法满足速率的要求,其使用的是模拟网口的方式进行数据传输,而在拔号上需要向AT口发送配置及连接指令,连接上后需要对模拟网口进行DHCP配置。
58、在3G模块里,一般会映射出/dev/ttyUSB0、1、2共3个USB转串口的设备窑钕仇焱结点,其中这三个串口会对应“AT指令口”、“语音通话口惯墀眚篪”(带语音功能的模块)、“数据上网口”,而在LTE模块,映射出来的有一个AT指令口(如ttyUSB0)和一个虚拟网口(如usb0、wan0)。
59、针对不同LTE模块,其上网的AT指令不同,需根据具体情况处理,但DHCP处理这块,在RK3188平台可使用如下两种方式处理:
60、a 使用busyboxudhcpc命令,在RIL库中使用system函数调用,该方法过多的使用到命令行命令,移植性不佳,故不讨论;
61、b 使用libnetutils库(system/core/libnetutils/)里的dhcp_do_request函数,相应的函数格式如下:
62、dhcp_do_request(PPP_TTY_PATH, ppp_local_ip, ppp_gw, &prefixLength, ppp_dns1, ppp_dns2, server, &lease, vendorInfo)
63、当执行该函数时,会去启动dhcpcd_wan0的service,其中wan0为上面的PPP_TTY_PATH,还有DHCP服务到期后的重新请求服务,故而在init.rk30board.rc里添加如下内容:
64、service dhcpcd_wan0 /system/bin/dhcpcd -ABKLclass maindisabledoneshotservice iprenew_wan0 system/bin/dhcpcd -nclass maindisabledoneshot
65、当请求到相应数据后,我们需要上报到Android上层,此时需要使用如下格式的数据进行上报:
66、sprintf(ppp_dnses, &孥恶膈茯quot;%s %s", ppp_dns1, ppp_dns2);responses = alloca(sizeof(RIL_Data_Call_Response_v6));responses[0].status = 0;responses[0].suggestedRetryTime = -1;responses[0].cid = 1;responses[0].active = 2;responses[0].type = "IP";responses[0].ifname = PPP_TTY_PATH;responses[0].addresses = ppp_local_ip;responses[0].dnses = ppp_dnses;responses[0].gateways = ppp_gw;RIL_onRequestComplete(t, RIL_E_SUCCESS, responses, sizeof(RIL_Data_Call_Response_v6));
67、至此,涉及我们移植过程的细节问题及程序执行流程的分析,就大致说明了一遍,非常棒的一次学习体验。