基于迅为4412精英版连接SIM7100C模块Android下调试——RIL库分析(二)

来源:互联网 发布:comic studio mac 编辑:程序博客网 时间:2024/05/22 08:15

基于迅为4412精英版连接SIM7100C模块Android下调试——RIL库分析(一)

   

1.概念介绍

Android电话系统围绕底层使用Modem硬件来搭建,提供呼叫、短信和网络连接功能,其中modem也称为基带。Modem驱动程序包含在Linux内核层中,所以3G/4G模块的搭建其实就是电话系统的搭建 。主流3G/4G模块:主要分有 内置(集成在处理器中如高通等,内置模块主要通过共享内存与处理器通信);  外置(较常见,大多通过usb转串口接口并利用AT命令与处理器通信,usb串口一般使用标准的驱动,AT命令由Hayes公司发明,一种调制解调器命令语言。

2.基本架构概述

   Android RIL (Radio Interface Layer)提供了Telephony服务和Radio硬件之间的抽象层。RIL负责数据的可靠传输、AT命令的发送以及response(响应)的解析。一般的,应用处理器(AP)通过AT命令集与无线通讯模块(基带/BP)通信。通信的方式又分为主动请求的request(诸如拨号、发短信……),以及Modem主动上报的例如信号强度、基站信息、来电、来短信等,称之为unsolicitedresponse(未经请求的响应)。   电话系统大体框架):分三大层,硬件(连接modem设备),Android系统(主要包括了Linux内核层,运行库层和Framework层),平台API(即再往上为应用层)。

电话系统大体框架

具体实现自下到上:Modem驱动,RIL库 、RIL守护进程,电话JAVA框架和电话应用(其中电话系统部分无JNI) 。 
Kernel Space 属于驱动层在内核中实现,一般分为AT命令通道和数据通道两路接口。 
RIL层:贯穿Android的内核层至应用层,由三部分组成:RIL守护进程(本质是rild可执行程序,开机通过init启动,与framework层主要通过socket来进行), libril库和ril实现库(电话层硬件抽象层)。主要负责负责数据的可靠传输、AT命令的发送以及response(来自modem)的解析 。 
这里写图片描述

RIL接口下层结构:ril实现库的接口非常复杂,需要处理的命令,相关结构体比较多,其接口主要定义在 hardware/ril/include/telephony 目录的ri.h中。 rild socket:与框架层进行通信 
RIL_Env: 主要用于请求完成函数、上报消息响应函数和周期行处理函数三个功能。 
ril硬件抽象层主要实现 ril命令与AT命令的转换。 
这里写图片描述


3.Modem与AP通讯

   在Android系统中rild运行在AP上,AP上的应用通过rild发送AT指令给BP,BP接收到信息后又通过rild传送给AP。AP与BP之间有两种通信方式:

1.Solicited Response:Ap向Bp发送请求,Bp给Ap发送回复,该类型的AT指令及其回调函数以数组的形式存放在Ril_commands.h文件中; 
2.unSolicited Response:Bp主动给Ap发送事件,该类型的AT指令及其回调函数以数组的形式存放在ril_unsol_commands.h文件中。

不同手机厂商使用的AT命令不完全相同,AP与BP之间通过各厂商自己的相关动态库来通信。 
这里写图片描述
目前市面上的3G/4G modem和主机的连接方式主要有串口、USB和mini-PCIE,串口模块比较传统且使用比较简单。加载驱动后,3G/4G模块的usb口会映射为ttyUSB*的形式。


4.Android RIL组成

    Android RIL可以分成2个模块,一个部分RIL Demon(RILD),用于通过socket和framework通讯;另一部分是第三方自己客制化的部分,暂时称之为vendor RIL。这样设计是因为不同的厂商使用的Modem不一样,而RIL又和Modem紧密联系,所以Android有把和Modem联系紧密的部分和公共部分剥离开,让不同的厂商可以客制化vendor RIL以适应厂商自己的Modem。Vendor RIL专门负责通过AT和Modem进行通讯。可以细化称为:

这里写图片描述 
RIL模块由rild守护进程、libril.so、librefrence.so三部分组成:

1.rild模块:

被编译为一个可执行文件,RIL守护进程,开机时被init守护进程调用启动,实现一个main函数作为整个ril模块的入口点。 
在初始化时使用dlopen打开librefrence-ril.so,从中取出并执行RIL_Init函数,得到RIL_RadioFunctions指针,通过RIL_register()函数注册到libril.so库中,其源码结构如下: 
这里写图片描述 
在rild.c文件中,将完成ril的加载过程,它会执行如下操作: 
a. 动态加载Vendor RIL的.so文件; 
b.执行RIL_startEventLoop()开启消息队列以进行事件监听; 
c. 通过执行Vendor RIL的rilInit()方法来进行Vendor RIL与libril的关系建立。 
d.在rild文件夹中还包括一个radiooptions.c文件,它的作用是通过串口将一些radio相关的参数直接传给rild来对radio进行配置。radiooptiongs通过获取启动参数, 利用socket与rild通信,可供调试时配置Modem参数。


2. libril.so:

主要负责同上层的通信工作,接收ril的请求,并传递给librefrence_ril.so,同时将librefrence_ril.so返回的消息送给调用进程。与 rild结合相当紧密,是其共享库,编译时就已经建立了这一关系。源码结构如下所示: 
这里写图片描述 
a.在编译时libril.so被链入rild,它为rild提供了event处理功能,还提供了在rild与Vendor RIL之间传递请求和响应消息的能力。 
b.libril.so提供的主要功能分布在两个主要方法内,一个RIL_startEventLoop()方法,另一个是RIL_register()方法。 
c.RIL_startEventLoop()方法所提供的功能就是启用eventLoop线程,开始执行RIL消息队列。 
d.RIL_register()方法的主要功能是启动名为 rild 的监听端口,等待java 端通过socket进行连接。


3.librefrence_ril.so:

是由各手机厂商自己实现,在rild进程运行中通过dlopen方式加载,主要负责跟modem硬件通信,转换来自libril.so的请求为AT命令,并且将AT指令写入radio中。同时监听Modem的反馈信息给libril.so。在初始化时, rild通过符号RIL_Init获取一组函数指针并以此与之建立联系。源码结构如下所示: 
这里写图片描述 
Android自带的Vendor RIL的参考实现。被编译成.so文件,由于本部分是厂商定制的重点所在。所以被设计为松散耦合,且可灵活配置的。reference-ril会接收调用者传来的参数,参数内容为与radio的通信方式。如通过串口连接radio,那么参数为这种形式:-d /dev/ttyUSB*。


5.参考:

http://blog.csdn.net/yangwen123/article/details/8914291

http://blog.csdn.net/hanbo622/article/details/42520007

http://blog.csdn.net/tronteng/article/details/39031615

http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/column/details/ril-3g.html

http://blog.csdn.net/jscese/article/details/40046493

http://www.cnitblog.com/luofuchong/archive/2011/05/28/74190.html

今天先开个头,继续加油! 
以上内容多参考网上的诸位前辈的博客,谢谢你们!


基于迅为4412精英版连接SIM7100C模块Android下调试——RIL库分析(二)

 

1.Android电话系统设计框架图:

      Android的智能机架构是应用处理器+基带芯片,也就是AP+Modem的模式,AP部分相当于CPU,Modem相当于网卡,而且每个厂商使用的Modem都有可能不一样,故Android 开发者使用的Modem 是不一样的,各种指令格式,初始化序列都可能不一样,所以为了消除这些差别,Android 设计者将ril 做了一个抽象,使用一个虚拟电话的概念,不同modem相关的AT指令或者通信协议编译成相应的动态链接库.so文件,Rild 是具体的AT 指令合成者和应答解析者。

这里写图片描述


Android电话系统代码结构: 
这里写图片描述


2.互相之间的通信

    RILC与上层的RILJ沟通方式是通过Socket传输数据与命令,而与底层Modem的信号传输是通过串口用AT命令来实现。

这里写图片描述


3.ril-daemon的启动

    ril-daemon进程是由init进程在系统开机时负责启动的,该进程在我们系统启动之后就一直存在在系统里面了。

在init.rc(…/out/target/product/sabresd_6dq/root/init.rc对应源码 …/system/core/rootdir/init.rc)中可以看到如下代码: 
这里写图片描述
ril-daemon守护进程指的是system/bin/下的可执行程序rild,而rild是由…/hardware/ril/rild/目录下的rild.c文件编译生成的。


4.RILD框架设计

这里写图片描述


5.rild启动流程分析

1.rild(hardware/ril/rild/rild.c):仅实现main函数作为整个ril层的入口点,负责完成初始化。
2.libril.so(hardware/ril/libril/*):与rild结合相当紧密,是其共享库,编译时就已经建立了这一关系.libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递给libreference-ril.so,同时把libreference-ril.so的反馈传给调用进程。
3.libreference-ril.so(hardware/ril/libreference-ril/):rild通过dlopen方式加载,主要负责跟Modem硬件通信。它转换来自librild.so的请求为AT命令,通过串口连接radio,那么参数为这种形式:-d /dev/ttySx(ttyUSB*),同时监控Modem的反馈信息,并传递回libril.so。在初始化时,rild通过符号RIL_Init获取一组函数指针并以此与之建立联系。

RILD进程启动的时序图: 
这里写图片描述


6.rild.c 代码分析

#include <stdio.h>#include <stdlib.h>#include <dlfcn.h>#include <string.h>#include <stdint.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <telephony/ril.h>#define LOG_TAG "RILD"#include <utils/Log.h>#include <cutils/properties.h>#include <cutils/sockets.h>#include <sys/capability.h>#include <linux/prctl.h>#include <private/android_filesystem_config.h>#include "hardware/qemu_pipe.h"#define LIB_PATH_PROPERTY   "rild.libpath"#define LIB_ARGS_PROPERTY   "rild.libargs"#define MAX_LIB_ARGS        16static void usage(const char *argv0){    fprintf(stderr, "Usage: %s -l <ril impl library> [-- <args for impl library>]\n", argv0);    exit(-1);}extern void RIL_register (const RIL_RadioFunctions *callbacks);extern void RIL_onRequestComplete(RIL_Token t, RIL_Errno e,                           void *response, size_t responselen);extern void RIL_onUnsolicitedResponse(int unsolResponse, const void *data,                                size_t datalen);extern void RIL_requestTimedCallback (RIL_TimedCallback callback,                               void *param, const struct timeval *relativeTime);static struct RIL_Env s_rilEnv = {    RIL_onRequestComplete,    RIL_onUnsolicitedResponse,    RIL_requestTimedCallback};extern void RIL_startEventLoop();static int make_argv(char * args, char ** argv){    // Note: reserve argv[0]    int count = 1;    char * tok;    char * s = args;    while ((tok = strtok(s, " \0"))) {        argv[count] = tok;        s = NULL;        count++;    }    return count;}/* * switchUser - Switches UID to radio, preserving CAP_NET_ADMIN capabilities. * Our group, cache, was set by init. //切换UID为AID_RADIO   */void switchUser() {    prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);    setuid(AID_RADIO);    struct __user_cap_header_struct header;    struct __user_cap_data_struct cap;    header.version = _LINUX_CAPABILITY_VERSION;    header.pid = 0;    cap.effective = cap.permitted = (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);    cap.inheritable = 0;    capset(&header, &cap);}/*rild.c仅实现一main函数作为整个ril层的入口点,负责完成初始化*/int main(int argc, char **argv){    const char * rilLibPath = NULL;    char **rilArgv;    void *dlHandle;    const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);    const RIL_RadioFunctions *funcs;    char libPath[PROPERTY_VALUE_MAX];    unsigned char hasLibArgs = 0;    int i;    umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);    for (i = 1; i < argc ;) {        if (0 == strcmp(argv[i], "-l") && (argc - i > 1)) {            rilLibPath = argv[i + 1];            i += 2;        } else if (0 == strcmp(argv[i], "--")) {            i++;            hasLibArgs = 1;            break;        } else {            usage(argv[0]);        }    }    if (rilLibPath == NULL) {        if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) {            // No lib sepcified on the command line, and nothing set in props.            // Assume "no-ril" case.            goto done;        } else {            rilLibPath = libPath;        }    }    /* special override when in the emulator */    /*判断是否为模拟器 */#if 1    {        static char*  arg_overrides[3];        static char   arg_device[32];        int           done = 0;#define  REFERENCE_RIL_PATH  "/system/lib/libreference-ril.so"        /* first, read /proc/cmdline into memory */        char          buffer[1024], *p, *q;        int           len;        int           fd = open("/proc/cmdline",O_RDONLY);        if (fd < 0) {            RLOGD("could not open /proc/cmdline:%s", strerror(errno));            goto OpenLib;        }    /*读取/proc/cmdline文件中的内容*/        do {            len = read(fd,buffer,sizeof(buffer)); }        while (len == -1 && errno == EINTR);        if (len < 0) {            RLOGD("could not read /proc/cmdline:%s", strerror(errno));            close(fd);            goto OpenLib;        }        close(fd);    /*判断是否为模拟器,对于真机,此处条件为false */        if (strstr(buffer, "android.qemud=") != NULL)        {            /* the qemud daemon is launched after rild, so            * give it some time to create its GSM socket            */            int  tries = 5;#define  QEMUD_SOCKET_NAME    "qemud"            while (1) {                int  fd;                sleep(1);                fd = qemu_pipe_open("qemud:gsm");                if (fd < 0) {                    fd = socket_local_client(                                QEMUD_SOCKET_NAME,                                ANDROID_SOCKET_NAMESPACE_RESERVED,                                SOCK_STREAM );                }                if (fd >= 0) {                    close(fd);                    snprintf( arg_device, sizeof(arg_device), "%s/%s",                                ANDROID_SOCKET_DIR, QEMUD_SOCKET_NAME );                    arg_overrides[1] = "-s";                    arg_overrides[2] = arg_device;                    done = 1;                    break;                }                RLOGD("could not connect to %s socket: %s",                    QEMUD_SOCKET_NAME, strerror(errno));                if (--tries == 0)                    break;            }            if (!done) {                RLOGE("could not connect to %s socket (giving up): %s",                    QEMUD_SOCKET_NAME, strerror(errno));                while(1)                    sleep(0x00ffffff);            }        }        /* otherwise, try to see if we passed a device name from the kernel */        if (!done) do {#define  KERNEL_OPTION  "android.ril="#define  DEV_PREFIX     "/dev/"    /*判断/proc/cmdline中的内容是否包含"android.ril="  */            p = strstr( buffer, KERNEL_OPTION );            if (p == NULL)                break;            p += sizeof(KERNEL_OPTION)-1;            q  = strpbrk( p, " \t\n\r" );            if (q != NULL)                *q = 0;            snprintf( arg_device, sizeof(arg_device), DEV_PREFIX "%s", p );            arg_device[sizeof(arg_device)-1] = 0;            arg_overrides[1] = "-d";            arg_overrides[2] = arg_device;            done = 1;        } while (0);        if (done) {            argv = arg_overrides;            argc = 3;            i    = 1;            hasLibArgs = 1;            rilLibPath = REFERENCE_RIL_PATH; //连接库地址:/system/lib/libreference-ril.so              RLOGD("overriding with %s %s", arg_overrides[1], arg_overrides[2]);        }    }OpenLib:#endif/* 动态库装载 */   // switchUser(); /*设置Rild进程的组用户为radio  */ /********************************************************************************** 打开链接库,librefrence -ril.so(厂商自定义的库  ),从中取出并执行RIL_Init函数, 得到RIL_RadioFunctions指针,通过RIL_register()函数注册到libril.so库中***********************************************************************************/     dlHandle = dlopen(rilLibPath, RTLD_NOW);    if (dlHandle == NULL) {        RLOGE("dlopen failed: %s", dlerror());        exit(-1);    } /************************************************************************************************* *RIL_startEventLoop():第一个任务 开启EventLoop循环,调用 ril.cpp 中的 RIL_startEventLoop 函数,libril.so 开始循环监听 socket 事件。   **************************************************************************************************/    RIL_startEventLoop();/**************************************************************************************************第二个任务:RIL_Init*硬件访问从链接库中(也就是reference-ril.c)寻找RIL_Init函数地址,强制转换为RIL_RadioFunctions的函数指针  RIL_Init首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket. 接下来便新开一个线程继续初始化, 即mainLoop*****************************************************************************************************/    rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");    if (rilInit == NULL) {        RLOGE("RIL_Init not defined or exported in %s\n", rilLibPath);        exit(-1);    }    if (hasLibArgs) {        rilArgv = argv + i - 1;        argc = argc -i + 1;    } else {        static char * newArgv[MAX_LIB_ARGS];        static char args[PROPERTY_VALUE_MAX];        rilArgv = newArgv;        property_get(LIB_ARGS_PROPERTY, args, "");/*过属性系统获取参数:rild.libargs*/        argc = make_argv(args, rilArgv);    }    // Make sure there's a reasonable argv[0]    rilArgv[0] = argv[0];/**************************************************************************************调用reference-ril.c中的RIL_Init函数进行初始化INIT,同时得到reference-ril的回调函数调用RIL_Init函数来初始化rild,传入参数s_rilEnv,返回RIL_RadioFunctions地址  **************************************************************************************/    funcs = rilInit(&s_rilEnv, argc, rilArgv);/*******************************************************************************************注册得到的reference的回调函数rild通过RIL_register注册这一(onRequest)指针。RIL_register中要完成的另外一个任务,就是打开前面提到的跟上层通信的socket接口(s_fdListen是主接口,s_fdDebug供调试时使用)。*******************************************************************************************/     RIL_register(funcs);done:    while(1) {        // sleep(UINT32_MAX) seems to return immediately on bionic        sleep(0x00ffffff);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293

主入口:rild.c中的main函数,主要完成三个任务:

1. 开启libril.so中的event机制, 在RIL_startEventLoop中,是最核心的由多路I/O驱动的消息循环。
2. 初始化librefrence_ril.so,也就是跟硬件或模拟硬件modem通信的部分(后面统一称硬件), 通过RIL_Init函数完成。从libreference-ril.so库中取得RIL_Init函数地址,并使用该函数将libril.so库中的RIL_Env接口注册到libreference-ril.so库,同时将libreference-ril.so库中的RIL_RadioFunctions接口注册到到libril.so库中,建立起libril.so库与libreference-ril.so库通信桥梁。
3. 通过RIL_Init获取一组函数指针RIL_RadioFunctions, 并通过RIL_register完成注册,并打开接受上层命令的socket通道。
原创粉丝点击