Communication - 03.RILC
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的更多相关文章
- [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 ...
- 本人SW知识体系导航 - Programming menu
将感悟心得记于此,重启程序员模式. js, py, c++, java, php 融汇之全栈系列 [Full-stack] 快速上手开发 - React [Full-stack] 状态管理技巧 - R ...
- Linux Communication Mechanism Summarize
目录 . Linux通信机制分类简介 . 控制机制 0x1: 竞态条件 0x2: 临界区 . Inter-Process Communication (IPC) mechanisms: 进程间通信机制 ...
- [转]A Faster UIWebView Communication Mechanism
ref:http://blog.persistent.info/2013/10/a-faster-uiwebview-communication.html Use location.hash or t ...
- RILC
RILC RIL层的作用大体上就是将上层的命令转换成相应的AT指令,控制modem工作.生产modem的厂家有很多:Qualcomm, STE, Infineon... 不同的厂家都有各自的特点,当然 ...
- 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 ...
- 一步步Cobol 400 上手自学入门教程03 - 数据部
数据部的作用 程序中涉及到的全部数据(输入.输出.中间)都要在此定义,对它们的属性进行说明.主要描述以下属性: 数据类型(数值/字符)和存储形式(长度) 数据项之间的关系(层次和层号) 文件与记录的关 ...
- Communication with each role instance in Azure
Use WCF Communication with role instance in azure 1)In worker role build WCF Service public overrid ...
- Android游戏开发实践(1)之NDK与JNI开发03
Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...
随机推荐
- css解决谷歌,360浏览器默认最小字体为12px问题
当我们设置前台html页面样式问题字体小于12px;时,会发现不管怎么设置小于12px字体,在谷歌.360浏览器上都不生效. 但在火狐等浏览器上却可以正常设置,当你打开谷歌360的设置后会发现,它们设 ...
- nginx 负载均衡集群解决方案 healthcheck_nginx_upstreams (一)
该文章来源于互联网,目前找不到原作者,放在这里的目的是记录healthcheck_nginx_upstreams 的安装过程和相关配置,在起初安装成功后不能够正常运行healthcheck_nginx ...
- Libjingle库简介
原文链接 国内现在很多语音聊天工具都是基于TURN方式实现的,包括YY.AK等等,这种方式对于服务器的性能要求很高,而且在用户量增大的时候,服务器压力也会越来越大,用户的语音质量也会受到很大影响.而基 ...
- VS2015 C#6.0 中的那些新特性
本人个人博客原文链接地址为http://aehyok.com/Blog/Detail/66.html. 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok ...
- windows下用golang连接mssql
版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 安装Microsoft SQL Server Native Client 安装golang的mssql驱动 写测试代码 ...
- Android 设置VPN(pptp连接方式)
本教程以小米手机的MIUI系统为例子,教大家如何设置VPN 先找到“设置”,打开设置菜单,如下图: 在设置菜单里面找“其它连接方式” 然后找到“VPN”,点击进入: 进入VPN设置界面后,如果VPN未 ...
- 微信小程序实例-获取当前的地理位置、速度
微信小程序官方文档 https://mp.weixin.qq.com/debug/wxadoc/dev/api/location.html JS代码 //index.js //获取应用实例 var a ...
- Android软件安全开发实践(下)
Android开发是当前最火的话题之一,但很少有人讨论这个领域的安全问题.本系列将分两期,探讨Android开发中常见的安全隐患和解决方案.第一期将从数据存储.网络通信.密码和认证策略这三个角度,带你 ...
- 关于typedef的使用方法
在计算机编程语言中用来为复杂的声明定义简单的别名.与宏定义有些差异.它本身是一种存储类的keyword,与auto.extern.mutable.static.register等keyword不能出如 ...
- [原]shader实现矩形圆角
哎!竭力想说清楚这个实现原理,并解释清楚shader里面的算法,结果发现越解释越不好理解,见谅! 一.实现目标:矩形四角是圆弧效果 二.实现的原理:通过每个角绘制1/4圆弧,剔除掉圆弧以外的部分. 原 ...