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. Oracle数据库恢复

    建用户 wf2014 赋权限 grant dba to wf2014; 数据恢复 imp wf2014/wf2014 file=D:\wf2014.dmp full=y 参数设置: datasourc ...

  2. Android ImageView圆形头像

    转载自:http://m.oschina.net/blog/321024 Android ImageView圆形头像 图片完全解析 我们在做项目的时候会用到圆形的图片,比如用户头像,类似QQ.用户在用 ...

  3. Objective-C 语法之 static 关键字

    转:http://www.apkbus.com/android-593-1.html 学习过Java 或者 C 语言的朋友应该很清楚static关键字吧?在某个类中声明一个static 静态变量, 其 ...

  4. mysqld.exe 占了400M内存的问题

    最近遇到了服务器总是停机的问题,虽然它自己只有2G的内存,不过实际部署的应用访问量非常小,也不至于2G就不够用,所以开始了给服务器瘦身的计划. 看着任务管理器里面的各个进程,发现吃内存最厉害的是mys ...

  5. Unity3D Shader入门指南(一)

    动机 自己使用Unity3D也有一段时间了,但是很多时候是流于表面,更多地是把这个引擎简单地用作脚本控制,而对更深入一些的层次几乎没有了解.虽然说Unity引擎设计的初衷就是创建简单的不需要开发者操心 ...

  6. 神舟K650c i7(W350STQ)上成功装好Mac OS X 10.9,兼谈如何安装WinXP、7、8.1、OSX、Ubuntu五系统(Chameleon、MBR)

    作者:zyl910 参考教程——http://bbs.pcbeta.com/viewthread-1432534-1-4.html笔记本SNB和IVY平台Win7/Win8/Win8.1安装OS X ...

  7. 使用Dezender对zend加密后的php文件进行解密

    在开发中需要修改一些php文件,部分是通过zend加密的,记事本打开之后是这样的: 此时需要使用Dezender进行解密,下载链接如下: Dezender.7z 下载后解压到C盘(路径不要带有中文), ...

  8. css/js在线压缩工具

    http://tool.css-js.com/ 在进行前端的时候,可以参考百度性能监控中心给出的意见: http://developer.baidu.com/apm/index

  9. 超棒的 15 款 Bootstrap UI 编辑器

    自从 2011 年 Mark Otto 和 Jacob Thornton 开发了  Bootstrap,我们第一次接触并熟知了 Bootstrap .这些都归功于  Twitter!从那以后,它就非常 ...

  10. Web 开发常备工具

    工欲善其事,必先利其器.如今 Web 开发标准越来越高,Web 开发者也在不断寻找途径提升自己的技能.为使大家的开发工作更顺利进行,本文整理了 10+ 款比较优秀的 Web 开发工具,希望对你有帮助. ...