RIL层的作用大体上就是将上层的命令转换成相应的AT指令,控制modem工作。生产modem的厂家有很多:Qualcomm, STE, Infineon... 不同的厂家都有各自的特点,当然也会有各自不同的驱动,但驱动代码的公开多少会涉及到modem厂家的技术细节,所以,Android系统开源了绝大部分代码,对于 部分驱动(Reference-RIL)  允许厂家以二进制Lib的形式成为一套完整Android系统的一部分。

  有Lib就需要有加载的概念,能够加载各种驱动说明驱动们都遵从一个统一的接口。这个接口是什么?RILC又是如何接收并处理RILJ向下传来的请求?


  

  进入hardware\ril\rild\rild.c,一切从main开始。

int main(int argc, char **argv)
{
... ... dlHandle = dlopen(rilLibPath, RTLD_NOW);
if (dlHandle == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
exit(-);
} RIL_startEventLoop(); // ril_event rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
if (rilInit == NULL) {
fprintf(stderr, "RIL_Init not defined or exported in %s\n", rilLibPath);
exit(-); }
... ... funcs = rilInit(&s_rilEnv, argc, rilArgv); // Reference-RIL 获得 LibRIL 的Interface

RIL_register
(funcs); // LibRIL 获得 Reference-RIL 的Interface }

  从dlopen看到了动态加载的痕迹,加载Reference-RIL之后便启动了监听线程,也就在RIL_startEventLoop。每一次从上层传来的请求都是一个event,可见要了解该层的消息传输,关键是要了解  结构体 ril_event

  与其相关的文件是ril_event.h、ril_event.cpp,对于文件的分析还是引用ACE1985兄台的博文为好,抱拳为敬。

 

ril_event.h

// 每次监视的最大的文件描述符句柄数,可以根据需要自行修改
#define MAX_FD_EVENTS 8 // ril_event的回调函数
typedef void (*ril_event_cb)(int fd, short events, void *userdata); struct ril_event {
// 用于将ril_event串成双向链表的前向指针和后向指针
struct ril_event *next;
struct ril_event *prev; //ril事件相关的文件描述符句柄(可以是文件、管道、Socket等)
int fd; //这个事件在监控列表中的索引
int index; //当一个事件处理完后(即从watch_table移到pending_list中等待处理),
//persist参数决定这个事件是否一直存在于监控列表watch_table[]中
bool persist; //事件的超时时间
struct timeval timeout; //回调函数及其传入的参数
ril_event_cb func;
void *param;
}; //以下是ril事件相关的一些操作函数
// 初始化内部数据结构
void ril_event_init(); // 初始化一个ril事件
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param); // 将事件添加到监控列表watch_table[]中
void ril_event_add(struct ril_event * ev); // 增加一个timer事件到timer_list链表中
void ril_timer_add(struct ril_event * ev, struct timeval * tv); // 将指定的事件从监控列表watch_table[]中移除
void ril_event_del(struct ril_event * ev); // 事件循环
void ril_event_loop();

ril_event.c

#define LOG_TAG "RILC"

#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <utils/Log.h>
#include <ril_event.h>
#include <string.h>
#include <sys/time.h>
#include <time.h> #include <pthread.h> // 使用互斥量mutex进行线程同步,可参见《Linux程序设计》相关章节
static pthread_mutex_t listMutex;
#define MUTEX_ACQUIRE() pthread_mutex_lock(&listMutex)
#define MUTEX_RELEASE() pthread_mutex_unlock(&listMutex)
#define MUTEX_INIT() pthread_mutex_init(&listMutex, NULL)
#define MUTEX_DESTROY() pthread_mutex_destroy(&listMutex) // 两个timeval类型的值相加
#ifndef timeradd
#define timeradd(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
if ((vvp)->tv_usec >= ) { \
(vvp)->tv_sec++; \
(vvp)->tv_usec -= ; \
} \
} while ()
#endif // 两个timeval类型的值进行比较
#ifndef timercmp
#define timercmp(a, b, op) \
((a)->tv_sec == (b)->tv_sec \
? (a)->tv_usec op (b)->tv_usec \
: (a)->tv_sec op (b)->tv_sec)
#endif // 两个timeval类型的值相减
#ifndef timersub
#define timersub(a, b, res) \
do { \
(res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((res)->tv_usec < ) { \
(res)->tv_usec += ; \
(res)->tv_sec -= ; \
} \
} while();
#endi // 保存Rild中所有设备文件句柄,便于使用select函数完成事件的监听
static fd_set readFds;
// 记录readFds中最大fd值+1
static int nfds = ; // 为了统一管理ril事件,Android提供如下三个队列:
// 监控事件列表,需要检测的事件都需要先存入该列表中
static struct ril_event * watch_table[MAX_FD_EVENTS]; // timer事件队列,事件超时后即移入pending_list队列中
static struct ril_event timer_list; // 待处理的事件队列,即事件已经触发,后续需要调用事件的回调函数
static struct ril_event pending_list; #define DEBUG 0 #if DEBUG
#define dlog(x...) LOGD( x )
static void dump_event(struct ril_event * ev)
{
dlog("~~~~ Event %x ~~~~", (unsigned int)ev);
dlog(" next = %x", (unsigned int)ev->next);
dlog(" prev = %x", (unsigned int)ev->prev);
dlog(" fd = %d", ev->fd);
dlog(" pers = %d", ev->persist);
dlog(" timeout = %ds + %dus", (int)ev->timeout.tv_sec, (int)ev->timeout.tv_usec);
dlog(" func = %x", (unsigned int)ev->func);
dlog(" param = %x", (unsigned int)ev->param);
dlog("~~~~~~~~~~~~~~~~~~");
}
#else
#define dlog(x...) do {} while(0)
#define dump_event(x) do {} while(0)
#endif // 获取此刻timeval值
static void getNow(struct timeval * tv)
{
#ifdef HAVE_POSIX_CLOCKS
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec/;
#else
gettimeofday(tv, NULL);
#endif
} // 初始化指定的ril_event链表
static void init_list(struct ril_event * list)
{
memset(list, , sizeof(struct ril_event));
list->next = list;
list->prev = list;
list->fd = -;
} // 增加一个ril_event事件到ril_event队列头
static void addToList(struct ril_event * ev, struct ril_event * list)
{
ev->next = list;
ev->prev = list->prev;
ev->prev->next = ev;
list->prev = ev;
dump_event(ev);
} // 从ril_event队列中移除指定的ril_event
static void removeFromList(struct ril_event * ev)
{
dlog("~~~~ Removing event ~~~~");
dump_event(ev); ev->next->prev = ev->prev;
ev->prev->next = ev->next;
ev->next = NULL;
ev->prev = NULL;
} // 从watch_table[]中移除指定索引的事件
static void removeWatch(struct ril_event * ev, int index)
{
// 索引index对应的事件置为空,同时事件ev的索引设为无效值-1
watch_table[index] = NULL;
ev->index = -; // 将该事件对应的文件描述符句柄从readFds中清除
FD_CLR(ev->fd, &readFds); if (ev->fd+ == nfds) {
int n = ; for (int i = ; i < MAX_FD_EVENTS; i++) {
struct ril_event * rev = watch_table[i]; if ((rev != NULL) && (rev->fd > n)) {
n = rev->fd;
}
}
nfds = n + ;
dlog("~~~~ nfds = %d ~~~~", nfds);
}
} // 遍历timer_list队列中的事件,当事件超时时间到时
// 将事件移除,并添加到pending_list队列中
static void processTimeouts()
{
dlog("~~~~ +processTimeouts ~~~~");
MUTEX_ACQUIRE();
struct timeval now;
struct ril_event * tev = timer_list.next;
struct ril_event * next; getNow(&now);
// walk list, see if now >= ev->timeout for any events dlog("~~~~ Looking for timers <= %ds + %dus ~~~~", (int)now.tv_sec, (int)now.tv_usec);
while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {
// Timer expired
dlog("~~~~ firing timer ~~~~");
next = tev->next;
removeFromList(tev);
addToList(tev, &pending_list);
tev = next;
}
MUTEX_RELEASE();
dlog("~~~~ -processTimeouts ~~~~");
} // 遍历监控列表watch_table[]中的事件,并将有数据可读的事件
// 添加到pending_list链表中,同时如果事件的persist不为true
// 则将该事件从watch_table[]中移除
static void processReadReadies(fd_set * rfds, int n)
{
dlog("~~~~ +processReadReadies (%d) ~~~~", n);
MUTEX_ACQUIRE(); for (int i = ; (i < MAX_FD_EVENTS) && (n > ); i++) {
struct ril_event * rev = watch_table[i];
if (rev != NULL && FD_ISSET(rev->fd, rfds)) {
addToList(rev, &pending_list);
if (rev->persist == false) {
removeWatch(rev, i);
}
n--;
}
} MUTEX_RELEASE();
dlog("~~~~ -processReadReadies (%d) ~~~~", n);
} // 依次调用待处理队列pending_list中的事件的回调函数
static void firePending()
{
dlog("~~~~ +firePending ~~~~");
struct ril_event * ev = pending_list.next;
while (ev != &pending_list) {
struct ril_event * next = ev->next;
removeFromList(ev);
ev->func(ev->fd, , ev->param);
ev = next;
}
dlog("~~~~ -firePending ~~~~");
} // 计算timer_list链表中下一个事件的新的超时时间
static int calcNextTimeout(struct timeval * tv)
{
struct ril_event * tev = timer_list.next;
struct timeval now; getNow(&now); // Sorted list, so calc based on first node
if (tev == &timer_list) {
// no pending timers
return -;
} dlog("~~~~ now = %ds + %dus ~~~~", (int)now.tv_sec, (int)now.tv_usec);
dlog("~~~~ next = %ds + %dus ~~~~",
(int)tev->timeout.tv_sec, (int)tev->timeout.tv_usec);
if (timercmp(&tev->timeout, &now, >)) {
timersub(&tev->timeout, &now, tv);
} else {
// timer already expired.
tv->tv_sec = tv->tv_usec = ;
}
return ;
} // 初始化内部数据结构(互斥量、FD集合、三个事件队列)
void ril_event_init()
{
MUTEX_INIT(); FD_ZERO(&readFds);
init_list(&timer_list);
init_list(&pending_list);
memset(watch_table, , sizeof(watch_table));
} // 初始化一个ril事件
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
{
dlog("~~~~ ril_event_set %x ~~~~", (unsigned int)ev);
memset(ev, , sizeof(struct ril_event));
ev->fd = fd;
ev->index = -;
ev->persist = persist;
ev->func = func;
ev->param = param; //linux的文件上锁函数,给文件描述符fd上非阻塞的文件锁
fcntl(fd, F_SETFL, O_NONBLOCK);
} // 将事件添加到监控列表watch_table[]中
void ril_event_add(struct ril_event * ev)
{
dlog("~~~~ +ril_event_add ~~~~");
MUTEX_ACQUIRE();
for (int i = ; i < MAX_FD_EVENTS; i++) {
if (watch_table[i] == NULL) {
watch_table[i] = ev;
ev->index = i;
dlog("~~~~ added at %d ~~~~", i);
dump_event(ev);
FD_SET(ev->fd, &readFds);
if (ev->fd >= nfds) nfds = ev->fd+;
dlog("~~~~ nfds = %d ~~~~", nfds);
break;
}
}
MUTEX_RELEASE();
dlog("~~~~ -ril_event_add ~~~~");
} // 增加一个timer事件到timer_list链表中
void ril_timer_add(struct ril_event * ev, struct timeval * tv)
{
dlog("~~~~ +ril_timer_add ~~~~");
MUTEX_ACQUIRE(); struct ril_event * list;
if (tv != NULL) {
// add to timer list
list = timer_list.next;
ev->fd = -; // make sure fd is invalid struct timeval now;
getNow(&now);
timeradd(&now, tv, &ev->timeout); // 根据timeout值从小到大在链表中排序
while (timercmp(&list->timeout, &ev->timeout, < )
&& (list != &timer_list)) {
list = list->next;
}
// 循环结束后,list指向链表中第一个timeout值大于ev的事件
// 将新加入的事件ev加到list此刻指向的事件前面
addToList(ev, list);
} MUTEX_RELEASE();
dlog("~~~~ -ril_timer_add ~~~~");
} // 将事件从watch_table[]中移除
void ril_event_del(struct ril_event * ev)
{
dlog("~~~~ +ril_event_del ~~~~");
MUTEX_ACQUIRE(); if (ev->index < || ev->index >= MAX_FD_EVENTS) {
MUTEX_RELEASE();
return;
} removeWatch(ev, ev->index); MUTEX_RELEASE();
dlog("~~~~ -ril_event_del ~~~~");
} #if DEBUG
// 打印监控列表中可用的事件
static void printReadies(fd_set * rfds)
{
for (int i = ; (i < MAX_FD_EVENTS); i++) {
struct ril_event * rev = watch_table[i];
if (rev != NULL && FD_ISSET(rev->fd, rfds)) {
dlog("DON: fd=%d is ready", rev->fd);
}
}
}
#else
#define printReadies(rfds) do {} while(0)
#endif void ril_event_loop()
{
int n;
fd_set rfds;
struct timeval tv;
struct timeval * ptv; for (;;) {
// make local copy of read fd_set
memcpy(&rfds, &readFds, sizeof(fd_set));
// 根据timer_list来计算select函数的等待时间
// timer_list之前已按事件的超时时间排好序了
if (- == calcNextTimeout(&tv)) {
// no pending timers; block indefinitely
dlog("~~~~ no timers; blocking indefinitely ~~~~");
ptv = NULL;
} else {
dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);
ptv = &tv;
}
printReadies(&rfds);
// 使用select函数实现多路IO复用
n = select(nfds, &rfds, NULL, NULL, ptv);
printReadies(&rfds);
dlog("~~~~ %d events fired ~~~~", n);
if (n < ) {
if (errno == EINTR) continue; LOGE("ril_event: select error (%d)", errno);
// bail?
return;
} // Check for timeouts
processTimeouts();
// Check for read-ready
processReadReadies(&rfds, n);
// Fire away
firePending();
}
}

若干ril_event构成watch_table数组,同时也被两个双向链表timer_list、pending_list串起来,不禁想起了内核链表。select对watch_table数组上的ril_event们进行监听。

RILJ与RILC通过socket连接,前者为client,后者为server。

  server通过select监听对外开放的socket端口fd,若RILJ请求连接,则回调listenCallback(),accept()出一个s_fdCommand,加入select监听数组,这个s_fdCommand便成为了上层传入请求的通道,RILC通过这个通道接收具体的command,而后转化为AT指令。

static struct ril_event s_commands_event;
static struct ril_event s_wakeupfd_event;
static struct ril_event s_listen_event;
static struct ril_event s_wake_timeout_event;
static struct ril_event s_debug_event;

  以上便是大致的思路,select+socket连接的经典模式。通道打通后,从s_fdCommand中到底会接收到什么?

ril_event_set (&s_commands_event, s_fdCommand, ,
processCommandsCallback, p_rs);

  

  函数层层嵌套,终会有一个办实事的命令。

static int
processCommandBuffer
(void *buffer, size_t buflen) {
Parcel p;
status_t status;
int32_t request;
int32_t token;
RequestInfo *pRI; //构造该结构体,尤其是其中的pCI
int ret; p.setData((uint8_t *) buffer, buflen); //获得有效p // status checked at end
status = p.readInt32(&request); //取得request值
status = p.readInt32 (&token); if (status != NO_ERROR) {
LOGE("invalid request block");
return ;
} if (request < || request >= (int32_t)NUM_ELEMS(s_commands)) {
LOGE("unsupported request code %d token %d", request, token);
// FIXME this should perhaps return a response
return ;
} pRI = (RequestInfo *)calloc(, sizeof(RequestInfo)); pRI->token = token;
pRI->pCI = &(s_commands[request]); //确定早已待命的command号 ret = pthread_mutex_lock(&s_pendingRequestsMutex);
assert (ret == ); pRI->p_next = s_pendingRequests;
s_pendingRequests = pRI; ret = pthread_mutex_unlock(&s_pendingRequestsMutex);
assert (ret == ); /* sLastDispatchedToken = token; */ pRI->pCI->dispatchFunction(p, pRI); //命令,发射! return ;
}

  Ok,这个办实事的命令就是s_comands数组第request个结构体中的dispatchFunction().

  s_comands数组是个啥?

static CommandInfo s_commands[] = {
#include "ril_commands.h"
}; typedef struct {
    int requestNumber;
    void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
    int (*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;

  

  Ref: http://blog.csdn.net/ace1985/article/details/7051522

     {, NULL, NULL},                   //none
{RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus},
{RIL_REQUEST_ENTER_SIM_PIN, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PUK, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PIN2, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_SIM_PUK2, dispatchStrings, responseInts},
{RIL_REQUEST_CHANGE_SIM_PIN, dispatchStrings, responseInts},
{RIL_REQUEST_CHANGE_SIM_PIN2, dispatchStrings, responseInts},
{RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, dispatchStrings, responseInts},
{RIL_REQUEST_GET_CURRENT_CALLS, dispatchVoid, responseCallList},
{RIL_REQUEST_DIAL, dispatchDial, responseVoid},
{RIL_REQUEST_GET_IMSI, dispatchVoid, responseString},
{RIL_REQUEST_HANGUP, dispatchInts, responseVoid},
{RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, dispatchVoid, responseVoid},
{RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, dispatchVoid, responseVoid},
{RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, dispatchVoid, responseVoid},
{RIL_REQUEST_CONFERENCE, dispatchVoid, responseVoid},
{RIL_REQUEST_UDUB, dispatchVoid, responseVoid},
{RIL_REQUEST_LAST_CALL_FAIL_CAUSE, dispatchVoid, responseInts},
{RIL_REQUEST_SIGNAL_STRENGTH, dispatchVoid, responseRilSignalStrength},
{RIL_REQUEST_VOICE_REGISTRATION_STATE, dispatchVoid, responseStrings},
{RIL_REQUEST_DATA_REGISTRATION_STATE, dispatchVoid, responseStrings},
{RIL_REQUEST_OPERATOR, dispatchVoid, responseStrings},
{RIL_REQUEST_RADIO_POWER, dispatchInts, responseVoid},
{RIL_REQUEST_DTMF, dispatchString, responseVoid},
{RIL_REQUEST_SEND_SMS, dispatchStrings, responseSMS},
{RIL_REQUEST_SEND_SMS_EXPECT_MORE, dispatchStrings, responseSMS},
{RIL_REQUEST_SETUP_DATA_CALL, dispatchDataCall, responseSetupDataCall},
{RIL_REQUEST_SIM_IO, dispatchSIM_IO, responseSIM_IO},
{RIL_REQUEST_SEND_USSD, dispatchString, responseVoid},
{RIL_REQUEST_CANCEL_USSD, dispatchVoid, responseVoid},
{RIL_REQUEST_GET_CLIR, dispatchVoid, responseInts},
{RIL_REQUEST_SET_CLIR, dispatchInts, responseVoid},
{RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, dispatchCallForward, responseCallForwards},
{RIL_REQUEST_SET_CALL_FORWARD, dispatchCallForward, responseVoid},
{RIL_REQUEST_QUERY_CALL_WAITING, dispatchInts, responseInts},
{RIL_REQUEST_SET_CALL_WAITING, dispatchInts, responseVoid},
{RIL_REQUEST_SMS_ACKNOWLEDGE, dispatchInts, responseVoid},
{RIL_REQUEST_GET_IMEI, dispatchVoid, responseString},
{RIL_REQUEST_GET_IMEISV, dispatchVoid, responseString},
{RIL_REQUEST_ANSWER,dispatchVoid, responseVoid},
{RIL_REQUEST_DEACTIVATE_DATA_CALL, dispatchStrings, responseVoid},
{RIL_REQUEST_QUERY_FACILITY_LOCK, dispatchStrings, responseInts},
{RIL_REQUEST_SET_FACILITY_LOCK, dispatchStrings, responseInts},
{RIL_REQUEST_CHANGE_BARRING_PASSWORD, dispatchStrings, responseVoid},
{RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, dispatchVoid, responseInts},
{RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, dispatchVoid, responseVoid},
{RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, dispatchString, responseVoid},
{RIL_REQUEST_QUERY_AVAILABLE_NETWORKS , dispatchVoid, responseStrings},
{RIL_REQUEST_DTMF_START, dispatchString, responseVoid},
{RIL_REQUEST_DTMF_STOP, dispatchVoid, responseVoid},
{RIL_REQUEST_BASEBAND_VERSION, dispatchVoid, responseString},
{RIL_REQUEST_SEPARATE_CONNECTION, dispatchInts, responseVoid},
{RIL_REQUEST_SET_MUTE, dispatchInts, responseVoid},
{RIL_REQUEST_GET_MUTE, dispatchVoid, responseInts},
{RIL_REQUEST_QUERY_CLIP, dispatchVoid, responseInts},
{RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE, dispatchVoid, responseInts},
{RIL_REQUEST_DATA_CALL_LIST, dispatchVoid, responseDataCallList},
{RIL_REQUEST_RESET_RADIO, dispatchVoid, responseVoid},
{RIL_REQUEST_OEM_HOOK_RAW, dispatchRaw, responseRaw},
{RIL_REQUEST_OEM_HOOK_STRINGS, dispatchStrings, responseStrings},
{RIL_REQUEST_SCREEN_STATE, dispatchInts, responseVoid},
{RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, dispatchInts, responseVoid},
{RIL_REQUEST_WRITE_SMS_TO_SIM, dispatchSmsWrite, responseInts},
{RIL_REQUEST_DELETE_SMS_ON_SIM, dispatchInts, responseVoid},
{RIL_REQUEST_SET_BAND_MODE, dispatchInts, responseVoid},
{RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, dispatchVoid, responseInts},
{RIL_REQUEST_STK_GET_PROFILE, dispatchVoid, responseString},
{RIL_REQUEST_STK_SET_PROFILE, dispatchString, responseVoid},
{RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, dispatchString, responseString},
{RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, dispatchString, responseVoid},
{RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM, dispatchInts, responseVoid},
{RIL_REQUEST_EXPLICIT_CALL_TRANSFER, dispatchVoid, responseVoid},
{RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, dispatchInts, responseVoid},
{RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, dispatchVoid, responseInts},
{RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, dispatchVoid, responseCellList},
{RIL_REQUEST_SET_LOCATION_UPDATES, dispatchInts, responseVoid},
{RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, dispatchInts, responseVoid},
{RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, dispatchInts, responseVoid},
{RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, dispatchVoid, responseInts},
{RIL_REQUEST_SET_TTY_MODE, dispatchInts, responseVoid},
{RIL_REQUEST_QUERY_TTY_MODE, dispatchVoid, responseInts},
{RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE, dispatchInts, responseVoid},
{RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE, dispatchVoid, responseInts},
{RIL_REQUEST_CDMA_FLASH, dispatchString, responseVoid},
{RIL_REQUEST_CDMA_BURST_DTMF, dispatchStrings, responseVoid},
{RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY, dispatchString, responseVoid},
{RIL_REQUEST_CDMA_SEND_SMS, dispatchCdmaSms, responseSMS},
{RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, dispatchCdmaSmsAck, responseVoid},
{RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG, dispatchVoid, responseGsmBrSmsCnf},
{RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG, dispatchGsmBrSmsCnf, responseVoid},
{RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION, dispatchInts, responseVoid},
{RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG, dispatchVoid, responseCdmaBrSmsCnf},
{RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG, dispatchCdmaBrSmsCnf, responseVoid},
{RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION, dispatchInts, responseVoid},
{RIL_REQUEST_CDMA_SUBSCRIPTION, dispatchVoid, responseStrings},
{RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM, dispatchRilCdmaSmsWriteArgs, responseInts},
{RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM, dispatchInts, responseVoid},
{RIL_REQUEST_DEVICE_IDENTITY, dispatchVoid, responseStrings},
{RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, dispatchVoid, responseVoid},
{RIL_REQUEST_GET_SMSC_ADDRESS, dispatchVoid, responseString},
{RIL_REQUEST_SET_SMSC_ADDRESS, dispatchString, responseVoid},
{RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, dispatchInts, responseVoid},
{RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, dispatchVoid, responseVoid},
{RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, dispatchVoid, responseInts},
{RIL_REQUEST_ISIM_AUTHENTICATION, dispatchString, responseString}

RIL中有两种Response类型:

一是Solicited Response(经过请求的回复),应用的场景是AP主动向BP发送一个AT指令,请求BP进行相应处理并在处理结束时回复一个AT指令通知AP执行的结果。源码中对应的文件是ril_commands.h。

一是Unsolicited Response(未经请求的回复),应用场景是BP主动向AP发送AT指令,用于通知AP当前系统发生的与Telephony相关的事件,例如网络信号变化,有电话呼入等。源码中对应的文件是ril_unsol_commands.h。

static UnsolResponseInfo s_unsolResponses[] = {
#include "ril_unsol_commands.h"
}; typedef struct {
int requestNumber;
int (*responseFunction) (Parcel &p, void *response, size_t responselen);
WakeType wakeType;
} UnsolResponseInfo;

  面对这上百的s_command元素们,顿觉代码的流程并非难点,难在对每一个s_command的理解。

  Ref:hardware\ril\include\telephony\Ril.h

     {RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_NEW_SMS, responseString, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT, responseString, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM, responseInts, WAKE_PARTIAL},
{RIL_UNSOL_ON_USSD, responseStrings, WAKE_PARTIAL},
{RIL_UNSOL_ON_USSD_REQUEST, responseVoid, DONT_WAKE},
{RIL_UNSOL_NITZ_TIME_RECEIVED, responseString, WAKE_PARTIAL},
{RIL_UNSOL_SIGNAL_STRENGTH, responseRilSignalStrength, DONT_WAKE},
{RIL_UNSOL_DATA_CALL_LIST_CHANGED, responseDataCallList, WAKE_PARTIAL},
{RIL_UNSOL_SUPP_SVC_NOTIFICATION, responseSsn, WAKE_PARTIAL},
{RIL_UNSOL_STK_SESSION_END, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_STK_PROACTIVE_COMMAND, responseString, WAKE_PARTIAL},
{RIL_UNSOL_STK_EVENT_NOTIFY, responseString, WAKE_PARTIAL},
{RIL_UNSOL_STK_CALL_SETUP, responseInts, WAKE_PARTIAL},
{RIL_UNSOL_SIM_SMS_STORAGE_FULL, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_SIM_REFRESH, responseInts, WAKE_PARTIAL},
{RIL_UNSOL_CALL_RING, responseCallRing, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_CDMA_NEW_SMS, responseCdmaSms, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, responseRaw, WAKE_PARTIAL},
{RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_RESTRICTED_STATE_CHANGED, responseInts, WAKE_PARTIAL},
{RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_CDMA_CALL_WAITING, responseCdmaCallWaiting, WAKE_PARTIAL},
{RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, responseInts, WAKE_PARTIAL},
{RIL_UNSOL_CDMA_INFO_REC, responseCdmaInformationRecords, WAKE_PARTIAL},
{RIL_UNSOL_OEM_HOOK_RAW, responseRaw, WAKE_PARTIAL},
{RIL_UNSOL_RINGBACK_TONE, responseInts, WAKE_PARTIAL},
{RIL_UNSOL_RESEND_INCALL_MUTE, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, responseInts, WAKE_PARTIAL},
{RIL_UNSOL_CDMA_PRL_CHANGED, responseInts, WAKE_PARTIAL},
{RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE, responseVoid, WAKE_PARTIAL},
{RIL_UNSOL_RIL_CONNECTED, responseInts, WAKE_PARTIAL}

打电话,则调用的是:

{RIL_REQUEST_DIAL, dispatchDial, responseVoid},

看来dispatchDial才是办实事的好同志,而dispatchDial中最终调用了s_callbacks,即之前通过 RIL_register(funcs),LibRIL 获得 Reference-RIL 的Interface 。

s_callbacks.onRequest(pRI->pCI->requestNumber, &dial, sizeOfDial, pRI);

至此,终于进入了Reference-RIL。

中场休息,做个简单的回顾:

1. 我们构造了RequestInfo,pCI指向了对应的s_commands:

typedef struct RequestInfo {
int32_t token; //this is not RIL_Token
CommandInfo *pCI;
struct RequestInfo *p_next;
char cancelled;
char local; // responses to local commands do not go back to command process
} RequestInfo;

  2. CommandInfo中的dispatchFunction最终调用了Reference-RIL提供的接口。

typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;

3. RIL_RadioFunctions 便是RIL对Reference-RIL的实现要求。

typedef struct {
int version; /* set to RIL_VERSION */
RIL_RequestFunc onRequest;
RIL_RadioStateRequest onStateRequest;
RIL_Supports supports;
RIL_Cancel onCancel;
RIL_GetVersion getVersion;
} RIL_RadioFunctions;

4. onRequest 根据request号做出对应的处理,也就是ril_commands.h。

/**
* RIL_Request Function pointer
*
* @param request is one of RIL_REQUEST_*
* @param data is pointer to data defined for that RIL_REQUEST_*
* data is owned by caller, and should not be modified or freed by callee
* @param t should be used in subsequent call to RIL_onResponse
* @param datalen the length of data
*
*/
typedef void (*RIL_RequestFunc) (int request, void *data,
size_t datalen, RIL_Token t);

RIL_RadioFunctions需要实现ril_commands.h中定义的request,当然,不一定全部支持。

  

  OK,继续 dialing...

case RIL_REQUEST_DIAL:
requestDial(data, datalen, t);

终于要见到AT的影子:

static void requestDial(void *data, size_t datalen, RIL_Token t)
{
RIL_Dial *p_dial;
char *cmd;
const char *clir;
int ret; p_dial = (RIL_Dial *)data; switch (p_dial->clir) {
case : clir = "I"; break; /*invocation*/
case : clir = "i"; break; /*suppression*/
default:
case : clir = ""; break; /*subscription default*/
} asprintf(&cmd, "ATD%s%s;", p_dial->address, clir); ret = at_send_command(cmd, NULL); free(cmd); /* success or failure is ignored by the upper layer here.
it will call GET_CURRENT_CALLS and determine success that way */
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, );
}

  之后的事情便是将AT string通过某种通道发送给BP。至于这个通道的建立,可能是串口也可能是其他,但最终都会表现为一个文件描述符,这就是 rilInit 的事儿了。

以上便是基于Dial的流程浏览,到这一层,重点还是对ril_commands.h, ril_unsol_commands.h的理解,"得此二物者得RIL"!

NEXT, LET'S GO INTO BP.


进入BP,简略一聊。先不谈从AP发来的AT指令做何处理,这是电信的范畴,涉及到众多协议以及核心网的概念,先就BP本身RF的一些基本功能聊些简单的话题。

RF (Radio Frequency),就是中学物理所讲的无线电波,这样说来也不觉有什么陌生,其实就是如此。

射频(RF)是Radio Frequency的缩写,表示可以辐射到空间的电磁频率,频率范围从300KHz~30GHz之间。(>10KHZ,高频电流)

在电子学理论中,电流流过导体,导体周围会形成磁场;交变电流通过导体,导体周围会形成交变的电磁场,称为电磁波。
在电磁波频率低于100khz时,电磁波会被地表吸收,不能形成有效的传输,但电磁波频率高于100khz时,电磁波可以在空气中传播,并经大气层外缘的电离层反射,形成远距离传输能力,我们把具有远距离传输能力的高频电磁波称为射频

我们的最终目的就是:用一定的功率发射出一定频率的无线电波

疑问来了,一定的频率到底是多少?

<工作频段>
中国陆地公用蜂窝数字移动通信网GSM通信系统采用900MHz频段:
890~915(移动台发、基站收)
935~960(基站发、移动台收)
双工间隔为45MHz,工作带宽为25 MHz,载频间隔为200 kHz。即25×1000/200-1=124个频点。
随着业务的发展,可视需要向下扩展,或向1.8GHz频段的GSM1800过渡,即1800MHz频段:
1710~1785(移动台发、基站收)
1805~1880(基站发、移动台收)
双工间隔为95MHz,工作带宽为75 MHz,载频间隔为200 kHz。即75×1000/2000-1=374个频点。

<频道间隔>
相邻两频道间隔为200kHz。 每个频道采用时分多址接入(TDMA)方式,分为8个时隙,即8个信道(全速率)。每信道占用带宽200 kHz/8=25 kHz。
将来GSM采用半速率话音编码后,每个频道可容纳16个半速率信道。

以上便是基于GSM的例子。频率越高,穿透性是强,但是衰减很大,覆盖不了多远。CDMA使用的800MHz频段和GSM使用的900MHz频段是公认的通话“黄金频段”。

RF的东西研究的深,则深不见底;若只考虑到应用,就目前的就业市场和国情对大部分人而言则比较现实。好运!

(补充:本人已跳出火坑^_^)

Communication - 03.RILC的更多相关文章

  1. [Code::Blocks] Install wxWidgets & openCV

    The open source, cross platform, free C++ IDE. Code::Blocks is a free C++ IDE built to meet the most ...

  2. 本人SW知识体系导航 - Programming menu

    将感悟心得记于此,重启程序员模式. js, py, c++, java, php 融汇之全栈系列 [Full-stack] 快速上手开发 - React [Full-stack] 状态管理技巧 - R ...

  3. Linux Communication Mechanism Summarize

    目录 . Linux通信机制分类简介 . 控制机制 0x1: 竞态条件 0x2: 临界区 . Inter-Process Communication (IPC) mechanisms: 进程间通信机制 ...

  4. [转]A Faster UIWebView Communication Mechanism

    ref:http://blog.persistent.info/2013/10/a-faster-uiwebview-communication.html Use location.hash or t ...

  5. RILC

    RILC RIL层的作用大体上就是将上层的命令转换成相应的AT指令,控制modem工作.生产modem的厂家有很多:Qualcomm, STE, Infineon... 不同的厂家都有各自的特点,当然 ...

  6. 2690036 - SAP HANA 2.0 SPS 03 Database Revision 034

    Symptom This is the SAP Release Note for SAP HANA 2.0 Database Revision 034 (2.00.034.00) of the SAP ...

  7. 一步步Cobol 400 上手自学入门教程03 - 数据部

    数据部的作用 程序中涉及到的全部数据(输入.输出.中间)都要在此定义,对它们的属性进行说明.主要描述以下属性: 数据类型(数值/字符)和存储形式(长度) 数据项之间的关系(层次和层号) 文件与记录的关 ...

  8. Communication with each role instance in Azure

    Use WCF  Communication with role instance in azure 1)In worker role build WCF Service public overrid ...

  9. Android游戏开发实践(1)之NDK与JNI开发03

    Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...

随机推荐

  1. css解决谷歌,360浏览器默认最小字体为12px问题

    当我们设置前台html页面样式问题字体小于12px;时,会发现不管怎么设置小于12px字体,在谷歌.360浏览器上都不生效. 但在火狐等浏览器上却可以正常设置,当你打开谷歌360的设置后会发现,它们设 ...

  2. nginx 负载均衡集群解决方案 healthcheck_nginx_upstreams (一)

    该文章来源于互联网,目前找不到原作者,放在这里的目的是记录healthcheck_nginx_upstreams 的安装过程和相关配置,在起初安装成功后不能够正常运行healthcheck_nginx ...

  3. Libjingle库简介

    原文链接 国内现在很多语音聊天工具都是基于TURN方式实现的,包括YY.AK等等,这种方式对于服务器的性能要求很高,而且在用户量增大的时候,服务器压力也会越来越大,用户的语音质量也会受到很大影响.而基 ...

  4. VS2015 C#6.0 中的那些新特性

    本人个人博客原文链接地址为http://aehyok.com/Blog/Detail/66.html. 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok ...

  5. windows下用golang连接mssql

    版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+] 安装Microsoft SQL Server Native Client 安装golang的mssql驱动 写测试代码   ...

  6. Android 设置VPN(pptp连接方式)

    本教程以小米手机的MIUI系统为例子,教大家如何设置VPN 先找到“设置”,打开设置菜单,如下图: 在设置菜单里面找“其它连接方式” 然后找到“VPN”,点击进入: 进入VPN设置界面后,如果VPN未 ...

  7. 微信小程序实例-获取当前的地理位置、速度

    微信小程序官方文档 https://mp.weixin.qq.com/debug/wxadoc/dev/api/location.html JS代码 //index.js //获取应用实例 var a ...

  8. Android软件安全开发实践(下)

    Android开发是当前最火的话题之一,但很少有人讨论这个领域的安全问题.本系列将分两期,探讨Android开发中常见的安全隐患和解决方案.第一期将从数据存储.网络通信.密码和认证策略这三个角度,带你 ...

  9. 关于typedef的使用方法

    在计算机编程语言中用来为复杂的声明定义简单的别名.与宏定义有些差异.它本身是一种存储类的keyword,与auto.extern.mutable.static.register等keyword不能出如 ...

  10. [原]shader实现矩形圆角

    哎!竭力想说清楚这个实现原理,并解释清楚shader里面的算法,结果发现越解释越不好理解,见谅! 一.实现目标:矩形四角是圆弧效果 二.实现的原理:通过每个角绘制1/4圆弧,剔除掉圆弧以外的部分. 原 ...