一.整体架构综述
ril用于实现打电话,发短信等通信业务功能,它主要通过AT命令与硬件模块交互实现功能。
![](https://app.yinxiang.com/shard/s14/res/08b64ec9-0531-464e-927d-b316f727ab99.png?resizeSmall&width=721)
这里一共涉及两个进程,一个是Phone process,一个是ril进程,Phone显然是上层进程,ril是负责与硬件打交道的进程。在ril中有四个线程,rild启动了一个主线程和一个eventLoop线程,主线程一直处于休眠状态,eventLoop线程在接收到Phone进程的命令之后,会调用ril.so的api函数发送request,这个request实际上就是AT command,也就是说eventLoop线程是发送AT command的线程。ril.so也启动了两个线程,一个是mainLoop线程,一个是readerLoop,mainLoop线程主要用于启动和恢复readerLoop线程,readerLoop线程则用于读取AT command的响应。下面将详细分析ril的实现。
二.rild
hardware/ril/rild/rild.c
main函数
-----> dlHandle = dlopen(rilLibPath, RTLD_NOW);
-----> RIL_startEventLoop();
-----> rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
-----> funcs = rilInit(&s_rilEnv, argc, rilArgv);
-----> RIL_register(funcs);
该函数主要是载入so文件,然后调用so文件的RIL_Init函数,该函数的作用是使得rild和so文件互相注册回调函数,它把RIL_Env注册给so,同时返回RIL_RadioFunctions,使得rild得到一组函数指针,RIL_RadioFunctions和RIL_Env的结构如下:
//so文件交给ril调用的api
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;
//ril交给so文件调用的callback函数,用于通知某个事件
struct RIL_Env {
void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,
void *response, size_t responselen);
void (*OnUnsolicitedResponse)(int unsolResponse, const void *data,
size_t datalen);
void (*RequestTimedCallback) (RIL_TimedCallback callback,
void *param, const struct timeval *relativeTime);
};
static struct RIL_Env s_rilEnv = {
RIL_onRequestComplete,
RIL_onUnsolicitedResponse,
RIL_requestTimedCallback
};
RIL_startEventLoop函数
hardware/ril/libril/ril.cpp
RIL_startEventLoop(void)该函数启动eventLoop进程
--->eventLoop(void *param)
-----> ril_event_init();
-----> ret = pipe(filedes);
-----> s_fdWakeupRead = filedes[0];
s_fdWakeupWrite = filedes[1];
----->ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);
rilEventAddWakeup (&s_wakeupfd_event);
-------->void ril_event_add(struct ril_event * ev) //add event to watch_table and rfds read set
-------->static void triggerEvLoop()
----------->void ril_event_loop()
for (;;) {
----->calcNextTimeout//获取timer_list第一个节点的timeout减去当前时间的差值作为select的timeout时间
-----> n = select(nfds, &rfds, NULL, NULL, ptv);//等待读事件或者超时
----->processTimeouts//把timer_list中的超时节点取出来放到pending_list中
----->processReadReadies//把watch_table中所有读事件置位的节点取出来放到pending_list中
----->firePending//触发pending_list中的事件
}
首先要说明的是ril_event结构,它的定义如下,该结构定义了rild的基本事件,其中fd表示一个读事件,timeout表示一个超时时间,在触发读事件或者超时到期后,将会调用func回调函数,这就是ril_event_loop循环所做的事情。
struct ril_event {
struct ril_event *next;
struct ril_event *prev;
int fd;
int index;
bool persist;
struct timeval timeout;
ril_event_cb func;
void *param;
};
ril_event_init
对几个重要的结构进行了初始化,readFds读事件FD_SET,timer_list超时列表,pending_list是一个临时列表,watch_table是所有ril_event的列表
void ril_event_init()
{
MUTEX_INIT();
FD_ZERO(&readFds);
init_list(&timer_list);
init_list(&pending_list);
memset(watch_table, 0, sizeof(watch_table));
}
RIL_register (const RIL_RadioFunctions *callbacks)
-----> s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);设备路径是//dev/socket/rild,属性名是ANDROID_SOCKET_rild
ret = listen(s_fdListen, 4);
ril_event_set (&s_listen_event, s_fdListen, false,//这个事件在触发回调函数后,会被删除,如果需要再次触发,需要重新加入事件
listenCallback, NULL);
rilEventAddWakeup (&s_listen_event);
//把rild-debug加入到读事件列表中
s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG);
ret = listen(s_fdDebug, 4);
ril_event_set (&s_debug_event, s_fdDebug, true,
debugCallback, NULL);
rilEventAddWakeup (&s_debug_event);
该函数主要就是加入监听事件,用于监听Phone Process的连接
listenCallback
在发生listen的callback的时候,连接会被加入到读事件列表中,同时监听事件会从读事件列表中删除,这样就不会有新的连接进来,在连接退出的时候,监听事件会被重新加入进来
-----> s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
------> err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);//这里所做的检查是只接受来自PHONE_PROCESS("radio")进程的连接
------> p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
------> ril_event_set (&s_commands_event, s_fdCommand, 1,//这个事件会一直存在于读事件列表中
processCommandsCallback, p_rs);//
rilEventAddWakeup (&s_commands_event);//把得到的连接加入到read列表中
processCommandsCallback
void processCommandsCallback(int fd, short flags, void *param)
这个fd即是连接对应的fd,s_fdCommand,param即为listenCallback中生成的p_rs,p_rs的类型是struct RecordStream,它有一段8k的内存用于从fd中读取数据,数据的格式是长度加内容
for (;;) {
ret = record_stream_get_next(p_rs, &p_record, &recordlen);每次从RecordStream中读取到的内容称为一个record,其格式为request+参数
processCommandBuffer(p_record, recordlen);
-----> 读取record中的request和token
p.setData((uint8_t *) buffer, buflen);
status = p.readInt32(&request);
status = p.readInt32 (&token);
//建立RequestInfo,并把它加入到s_pendingRequests链表中
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
pRI->token = token;
pRI->pCI = &(s_commands[request]);//s_commands是一个列表,定义在ril_commands.h中
pRI->p_next = s_pendingRequests;
s_pendingRequests = pRI;
//dispatch函数会根据不同的request类型,在调用s_callbacks.onRequest函数时传入不同的参数,该函数就是向so文件发送请求.
pRI->pCI->dispatchFunction(p, pRI);
}
我们返回去再去看main函数,在调用RIL_startEventLoop函数启动了eventLoop线程后,该线程由于没有事件发生,由select函数导致处于休眠状态。在调用RIL_register函数之后,加入了监听事件,在Phone进程连接进来后,eventLoop将处于等待Phone进程的命令状态,在接收到命令之后,会调用dispatchFunction,向so文件发送请求,在后面的分析中,我们知道,实际上就是发送AT命令,也就是说发送AT命令是运行在eventLoop线程上的。
综上所述,Phone进程和Rild进程通过socket进行通信,它们之间的数据被称为RecordStream,rild和so互相注册回调函 数,rild调用so的RIL_RadioFunctions函数来发送请求,so通过RIL_Env中的函数指针通知rild,请求的响应 (solicit),或者异步事件的发生(unsolicit)。
hardware/ril/rild/rild.c
main函数
-----> dlHandle = dlopen(rilLibPath, RTLD_NOW);
-----> RIL_startEventLoop();
-----> rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
-----> funcs = rilInit(&s_rilEnv, argc, rilArgv);
-----> RIL_register(funcs);
该函数主要是载入so文件,然后调用so文件的RIL_Init函数,该函数的作用是使得rild和so文件互相注册回调函数,它把RIL_Env注册给so,同时返回RIL_RadioFunctions,使得rild得到一组函数指针,RIL_RadioFunctions和RIL_Env的结构如下:
//so文件交给ril调用的api
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;
//ril交给so文件调用的callback函数,用于通知某个事件
struct RIL_Env {
void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,
void *response, size_t responselen);
void (*OnUnsolicitedResponse)(int unsolResponse, const void *data,
size_t datalen);
void (*RequestTimedCallback) (RIL_TimedCallback callback,
void *param, const struct timeval *relativeTime);
};
static struct RIL_Env s_rilEnv = {
RIL_onRequestComplete,
RIL_onUnsolicitedResponse,
RIL_requestTimedCallback
};
RIL_startEventLoop函数
hardware/ril/libril/ril.cpp
RIL_startEventLoop(void)该函数启动eventLoop进程
--->eventLoop(void *param)
-----> ril_event_init();
-----> ret = pipe(filedes);
-----> s_fdWakeupRead = filedes[0];
s_fdWakeupWrite = filedes[1];
----->ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);
rilEventAddWakeup (&s_wakeupfd_event);
-------->void ril_event_add(struct ril_event * ev) //add event to watch_table and rfds read set
-------->static void triggerEvLoop()
----------->void ril_event_loop()
for (;;) {
----->calcNextTimeout//获取timer_list第一个节点的timeout减去当前时间的差值作为select的timeout时间
-----> n = select(nfds, &rfds, NULL, NULL, ptv);//等待读事件或者超时
----->processTimeouts//把timer_list中的超时节点取出来放到pending_list中
----->processReadReadies//把watch_table中所有读事件置位的节点取出来放到pending_list中
----->firePending//触发pending_list中的事件
}
首先要说明的是ril_event结构,它的定义如下,该结构定义了rild的基本事件,其中fd表示一个读事件,timeout表示一个超时时间,在触发读事件或者超时到期后,将会调用func回调函数,这就是ril_event_loop循环所做的事情。
struct ril_event {
struct ril_event *next;
struct ril_event *prev;
int fd;
int index;
bool persist;
struct timeval timeout;
ril_event_cb func;
void *param;
};
ril_event_init
对几个重要的结构进行了初始化,readFds读事件FD_SET,timer_list超时列表,pending_list是一个临时列表,watch_table是所有ril_event的列表
void ril_event_init()
{
MUTEX_INIT();
FD_ZERO(&readFds);
init_list(&timer_list);
init_list(&pending_list);
memset(watch_table, 0, sizeof(watch_table));
}
RIL_register (const RIL_RadioFunctions *callbacks)
-----> s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);设备路径是//dev/socket/rild,属性名是ANDROID_SOCKET_rild
ret = listen(s_fdListen, 4);
ril_event_set (&s_listen_event, s_fdListen, false,//这个事件在触发回调函数后,会被删除,如果需要再次触发,需要重新加入事件
listenCallback, NULL);
rilEventAddWakeup (&s_listen_event);
//把rild-debug加入到读事件列表中
s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG);
ret = listen(s_fdDebug, 4);
ril_event_set (&s_debug_event, s_fdDebug, true,
debugCallback, NULL);
rilEventAddWakeup (&s_debug_event);
该函数主要就是加入监听事件,用于监听Phone Process的连接
listenCallback
在发生listen的callback的时候,连接会被加入到读事件列表中,同时监听事件会从读事件列表中删除,这样就不会有新的连接进来,在连接退出的时候,监听事件会被重新加入进来
-----> s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
------> err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);//这里所做的检查是只接受来自PHONE_PROCESS("radio")进程的连接
------> p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
------> ril_event_set (&s_commands_event, s_fdCommand, 1,//这个事件会一直存在于读事件列表中
processCommandsCallback, p_rs);//
rilEventAddWakeup (&s_commands_event);//把得到的连接加入到read列表中
processCommandsCallback
void processCommandsCallback(int fd, short flags, void *param)
这个fd即是连接对应的fd,s_fdCommand,param即为listenCallback中生成的p_rs,p_rs的类型是struct RecordStream,它有一段8k的内存用于从fd中读取数据,数据的格式是长度加内容
for (;;) {
ret = record_stream_get_next(p_rs, &p_record, &recordlen);每次从RecordStream中读取到的内容称为一个record,其格式为request+参数
processCommandBuffer(p_record, recordlen);
-----> 读取record中的request和token
p.setData((uint8_t *) buffer, buflen);
status = p.readInt32(&request);
status = p.readInt32 (&token);
//建立RequestInfo,并把它加入到s_pendingRequests链表中
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
pRI->token = token;
pRI->pCI = &(s_commands[request]);//s_commands是一个列表,定义在ril_commands.h中
pRI->p_next = s_pendingRequests;
s_pendingRequests = pRI;
//dispatch函数会根据不同的request类型,在调用s_callbacks.onRequest函数时传入不同的参数,该函数就是向so文件发送请求.
pRI->pCI->dispatchFunction(p, pRI);
}
我们返回去再去看main函数,在调用RIL_startEventLoop函数启动了eventLoop线程后,该线程由于没有事件发生,由select函数导致处于休眠状态。在调用RIL_register函数之后,加入了监听事件,在Phone进程连接进来后,eventLoop将处于等待Phone进程的命令状态,在接收到命令之后,会调用dispatchFunction,向so文件发送请求,在后面的分析中,我们知道,实际上就是发送AT命令,也就是说发送AT命令是运行在eventLoop线程上的。
综上所述,Phone进程和Rild进程通过socket进行通信,它们之间的数据被称为RecordStream,rild和so互相注册回调函 数,rild调用so的RIL_RadioFunctions函数来发送请求,so通过RIL_Env中的函数指针通知rild,请求的响应 (solicit),或者异步事件的发生(unsolicit)。
rild在收到Phone process的command后,使用RIL_RadioFunctions的onRequest发送请求,so通过env的 RIL_onRequestComplete发送响应,该函数会继续通过socket把响应发送给Phone process。
在异步事件发生的时候,so通过env的RIL_onUnsolicitedResponse把事件发送给Phone process
so也会通过env的RIL_requestTimedCallback,启动一个定时器,超时后回调它指定的函数。
在rild进程中,有两个线程,一个是主线程,一个是eventLoop线程,所有的命令都是运行在eventLoop线程
三.libreference-ril.so
reference-ril.c
RIL_Init
const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
-----> s_rilenv = env;//保存rild的env
-----> s_dev_path = optarg;//在模拟器上so使用ttyS0,即串口与模块通信
-----> ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);//启动一个线程
mainLoop
for (;;) {
fd = open (s_device_path, O_RDWR);//打开s_device_path设备,即ttyS0设备
s_closed = 0
ret = at_open(fd, onUnsolicited);//onUnsolicited会在readerLoop线程中调用
-----> s_fd = fd;
-----> s_unsolHandler = h;
-----> ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);//启动一个新的线程
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);//在rild的eventLoop线程中,执行initializeCallback
sleep(1);
waitForClose();//等待s_state_cond条件,该函数一旦退出,那将导致重新初始化
-----> while (s_closed == 0) {
pthread_cond_wait(&s_state_cond, &s_state_mutex);
}
}
-----> s_rilenv = env;//保存rild的env
-----> s_dev_path = optarg;//在模拟器上so使用ttyS0,即串口与模块通信
-----> ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);//启动一个线程
mainLoop
for (;;) {
fd = open (s_device_path, O_RDWR);//打开s_device_path设备,即ttyS0设备
s_closed = 0
ret = at_open(fd, onUnsolicited);//onUnsolicited会在readerLoop线程中调用
-----> s_fd = fd;
-----> s_unsolHandler = h;
-----> ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);//启动一个新的线程
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);//在rild的eventLoop线程中,执行initializeCallback
sleep(1);
waitForClose();//等待s_state_cond条件,该函数一旦退出,那将导致重新初始化
-----> while (s_closed == 0) {
pthread_cond_wait(&s_state_cond, &s_state_mutex);
}
}
由此可见,mainLoop线程主要是为了启动和恢复readerLoop的。在so中有三个宏需要特别说明一下:
#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)
#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)
#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)
RIL_onRequestComplete
用于在rild发送的请求有响应的时候,回调该函数通知rild
RIL_onUnsolicitedResponse
用于底层有异步事件发生的时候,回调该函数通知rild
RIL_requestTimedCallback
用于请求eventLoop执行一个超时回调
这三个函数指针都是rild注册给so的,他们分别对应ril.cpp中的
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen)
该函数会调用sendResponse函数,这个函数会把response通过socket,以Record的格式发送给Phone Process,该函数运行在eventLoop线程上,特别主要,该函数调用了checkAndDequeueRequestInfo函数,该函数将会检查请求是否在pending队列中,如果在的话,则将其删除,因为已经收到该请求的响应了。
void RIL_onUnsolicitedResponse(int unsolResponse, void *data,
size_t datalen)
该函数会调用sendResponse函数,这个函数会把response通过socket,以Record的格式发送给Phone Process,该函数运行在eventLoop线程上
RIL_requestTimedCallback (RIL_TimedCallback callback, void *param,
const struct timeval *relativeTime)
该函数会在eventLoop中加入一个超时事件
在mainLoop中就调用了RIL_requestTimedCallback函数,它将会导致initializeCallback函数被调用
#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)
#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)
#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)
RIL_onRequestComplete
用于在rild发送的请求有响应的时候,回调该函数通知rild
RIL_onUnsolicitedResponse
用于底层有异步事件发生的时候,回调该函数通知rild
RIL_requestTimedCallback
用于请求eventLoop执行一个超时回调
这三个函数指针都是rild注册给so的,他们分别对应ril.cpp中的
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen)
该函数会调用sendResponse函数,这个函数会把response通过socket,以Record的格式发送给Phone Process,该函数运行在eventLoop线程上,特别主要,该函数调用了checkAndDequeueRequestInfo函数,该函数将会检查请求是否在pending队列中,如果在的话,则将其删除,因为已经收到该请求的响应了。
void RIL_onUnsolicitedResponse(int unsolResponse, void *data,
size_t datalen)
该函数会调用sendResponse函数,这个函数会把response通过socket,以Record的格式发送给Phone Process,该函数运行在eventLoop线程上
RIL_requestTimedCallback (RIL_TimedCallback callback, void *param,
const struct timeval *relativeTime)
该函数会在eventLoop中加入一个超时事件
在mainLoop中就调用了RIL_requestTimedCallback函数,它将会导致initializeCallback函数被调用
initializeCallback
static void initializeCallback(void *param)
at_handshake();
-----> err = at_send_command_full_nolock ("ATE0Q0V1", NO_RESULT,NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL);
-----> err = writeline (command);
-----> err = pthread_cond_wait(&s_commandcond, &s_commandmutex);//等待s_commandcond条件,在readerLoop线程中读到OK响应的时候,该条件会置位
at_send_command
-----> err = at_send_command_full_nolock(command, type,
responsePrefix, smspdu,
timeoutMsec, pp_outResponse);
。。。
该函数会调用一系列的at_send_command,发送一系列的AT command
readerLoop
mainLoop中启动了新的线程,该线程是读取at command的response
static void *readerLoop(void *arg)
{
for (;;) {
line = readline();
if(isSMSUnsolicited(line)) {
line1 = strdup(line);
line2 = readline();
if (s_unsolHandler != NULL) {
s_unsolHandler (line1, line2);
}
} else {
processLine(line);
}
}
onReaderClosed();
return NULL;
}
至此,我们对整个AT command的发送和接收,就比较清楚了,rild的eventLoop调用RIL_RadioFunctions.onRequest发送请求,该函数实际上是so中的onRequest函数,它最终调用了at_send_command_full_nolock函数发送AT command,并进入休眠状态,等待响应。另外一个读线程readerLoop不断读取底层通过ttyS0发来的数据,在确认收到响应后,将唤醒eventLoop线程,继续后面的请求。
at_send_command_full_nolock
但是除此之外,还需要考虑在发送请求后,一直收不到响应,或者读线程在读取数据过程中读取失败的请求(例如硬件错误?),看如下的分析:
static int at_send_command_full_nolock (const char *command, ATCommandType type,
const char *responsePrefix, const char *smspdu,
long long timeoutMsec, ATResponse **pp_outResponse)
{
err = writeline (command);
s_type = type;
s_responsePrefix = responsePrefix;
s_smsPDU = smspdu;
sp_response = at_response_new();
while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
if (timeoutMsec != 0) {
#ifdef USE_NP
err = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, timeoutMsec);
#else
err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);
#endif /*USE_NP*/
} else {
err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
}
if (err == ETIMEDOUT) {
err = AT_ERROR_TIMEOUT;
goto error;
}
}
if (pp_outResponse == NULL) {
at_response_free(sp_response);
} else {
/* line reader stores intermediate responses in reverse order */
reverseIntermediates(sp_response);
*pp_outResponse = sp_response;
}
sp_response = NULL;
if(s_readerClosed > 0) {
err = AT_ERROR_CHANNEL_CLOSED;
goto error;
}
err = 0;
error:
clearPendingCommand();
return err;
}
该函数是一个阻塞函数,它运行在rild的eventLoop线程中,它指定了条件等待的超时时间,在正常情况下,如果在等待时间内,条件满足,也就是读 线程读到了响应,那么at命令发送成功。如果等待超时,则要进行超时处理,即clearPendingCommand(),在mainLoop中调用了 at_set_on_timeout(onATTimeout),clearPendingCommand会调用该函数
static void onATTimeout()
{
LOGI("AT channel timeout; closing\n");
at_close();//该函数将导致readerLoop线程退出
s_closed = 1;
/* FIXME cause a radio reset here */
setRadioState (RADIO_STATE_UNAVAILABLE);//s_closed和s_state_cond置位将导致mainLoop中的WaitForClose函数退出,继而导致重新初始化reader线程
}
void at_close()
{
if (s_fd >= 0) {
close(s_fd);
}
s_fd = -1;
pthread_mutex_lock(&s_commandmutex);
s_readerClosed = 1;
pthread_cond_signal(&s_commandcond);
pthread_mutex_unlock(&s_commandmutex);
/* the reader thread should eventually die */
}
在mainLoop中的at_set_on_reader_closed函数,也是为了防止ttyS0读取失败,意外发生,此时也会导致读线程重新初始化
at_set_on_reader_closed(onATReaderClosed)
static void onATReaderClosed()
{
LOGI("AT channel closed\n");
at_close();
s_closed = 1;
setRadioState (RADIO_STATE_UNAVAILABLE);
}
四.at command
at命令可以看做是硬件和软件的接口,不同的硬件厂家提供不同的AT命令集以提供不同的功能,但是at命令的基本格式是固定,它一般通过串口承载数据,当然也不是一定要使用串口,也可以使用其他的承载方式,例如socket。由于at命令是半双工,即同时只能有一个方向的数据流动,所以一般的情况下,由上层发送AT+CMD,下层发送响应,通常都以\r\n作为结束,如果内容中有\r\n的话,则需要转义。另外一种情况是由底层发送请求,一般的格式是+CMD:,此时上层一般不需要发送响应