ril/rild/rild.c->main()为函数入口
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1.消息队列select为非阻塞的去轮询事件
2.read的阻塞的去读取上层发下来的命令,并响应
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int main(int argc, char **argv)
{
const char * rilLibPath = NULL;
char **rilArgv;
void *dlHandle;
const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
const RIL_RadioFunctions *funcs;
char libPath[PROPERTY_VALUE_MAX];
unsigned char hasLibArgs = 0;
........
OpenLib:
#endif
switchUser();
/*打开dlopen()函数,就会动态去加载动态库vendor RIL 获取由RIL_register(funcs);注册进来的参数,并解析*/
dlHandle = dlopen(rilLibPath, RTLD_NOW);
if (dlHandle == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
exit(-1);
}
/*消息队列的入口,添加到select,用阻塞方式去读取那些ril_event_set()的数据##每当看到打印信息,不按顺序打下来说明阻塞##*/
RIL_startEventLoop();
/*通过dlsym函数得到rilInit函数指针的引用*/
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(-1);
}
if (hasLibArgs) {
rilArgv = argv + i - 1;
argc = argc -i + 1;
} else {
static char * newArgv[MAX_LIB_ARGS];
static char args[PROPERTY_VALUE_MAX];
rilArgv = newArgv;
property_get(LIB_ARGS_PROPERTY, args, "");
argc = make_argv(args, rilArgv);
}
// Make sure there's a reasonable argv[0]
rilArgv[0] = argv[0];
/*利用得到的rilInit函数指针,调用真正的RIL_Init ,实际是动态加载动态库去链接reference-ril.c ,由dlopen()函数加载*/
funcs = rilInit(&s_rilEnv, argc, rilArgv);
/*RIL_register作用一:把vendor RIL(即RIL_init) 注册到reference-ril库去等待,dopen()函数加载链接
附:RIL_init通过是onRequest()方法,将上层来的请求进行映射后转换成对应的AT命令发给硬件,rild通过RIL_register注册这一指针。
RIL_register作用二:创建rild socket主要是等待java层得数据通过,传到下一层,还创建debug socket*/
RIL_register(funcs);
done:
while(1) {
// sleep(UINT32_MAX) seems to return immediately on bionic
sleep(0x00ffffff);
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
所有文件:
hardware/ril/reference-ril$ ls
Android.mk atchannel.h at_tok.h misc.h NOTICE atchannel.c at_tok.c ril_event.h reference-ril.c misc.c MODULE_LICENSE_APACHE2
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在编译时libril被链入rild,它为rild提供了event处理功能,还提供了在rild与Vendor RIL之间传递请求和响应消息的能力。
libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递给librefrence_ril.so, 同时把来自librefrence_ril.so的反馈回传给调用进程。
librefrence.so负责直接与modem通信,这包括将来自libril的指令转换为AT指令,并且将AT指令写入modem中。
reference-ril会接收调用者传来的参数,参数内容为与radio的通信方式。如通过串口连接modem,那么参数为这种形式:-d /dev/ttySx
funcs =rilInit(&s_rilEnv, argc, rilArgv);//实际是通过动态加载动态库的方式执行reference-ril.c中的RIL_Init
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
reference-ril/reference-ril.c ->RIL_RadioFunctions *RIL_Init()函数
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{
int ret;
int fd = -1;
int opt;
pthread_attr_t attr;
s_rilenv = env;
LOGD("--referencen_ril.c-- RIL_RadioFunctions -shi-xian--*RIL_Init-----");
while ( -1 != (opt = getopt(argc, argv, "p:d:s:"))) {
switch (opt) {
case 'p':
s_port = atoi(optarg);
if (s_port == 0) {
usage(argv[0]);
return NULL;
}
LOGI("Opening loopback port %d\n", s_port);
break;
//获取dev/ttyUSB,只是打开而已,是否获取,由线程mainLoop执行完才知道
case 'd':
s_device_path = optarg;
LOGI("Opening tty device %s\n", s_device_path);
break;
case 's':
s_device_path = optarg;
s_device_socket = 1;
LOGI("Opening socket %s\n", s_device_path);
break;
default:
usage(argv[0]);
return NULL;
}
}
if (s_port < 0 && s_device_path == NULL) {
usage(argv[0]);
return NULL;
}
//上面获取了设备节点和socket
pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
LOGD("---reference-ril.c --ril_init--ru-kou- ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);-")
//创建一个线程,单独去读取串口数据
ret = pthread_create(&s_tid_mainloop, &attr,mainLoop, NULL);//创建一个线程mainLoop
return &s_callbacks;//返回一个ril_init,给 RIL_register(&s_callbacks);回调,进行初始化
}
#########################################################################################################################
这个任务的入口是RIL_Init, RIL_Init首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket. 接下来便新开一个线程继续初始化, 即mainLoop。mainLoop的主要任务是建立起与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应。
在注册一些基础回调(timeout,readerclose)后,mainLoop首先打开硬件设备文件,建立起与硬件的通信,s_device_path和s_port是前面获取的设备路径参数,将其打开(两者可以同时打开并拥有各自的reader,这里也很容易添加双卡双待等支持)。接下来通过at_open函数建立起这一设备文件上的reader等待循环,这也是通过新建一个线程完成, ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr),入口点readerLoop。
AT命令都是以/r/n或/n/r的换行符来作为分隔符的,所以readerLoop是line驱动的,除非出错,超时等,否则会读到一行完整的响应或主动上报,才会返回。这个循环跑起来以后,我们基本的AT响应机制已经建立了起来。它的具体分析,包括at_open中挂接的ATUnsolHandler, 我们都放到后面分析response的连载文章里去。
有了响应的机制(当然,能与硬件通信也已经可以发请求了),通过RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0),跑到initializeCallback中,执行一些Modem的初始化命令,主要都是AT命令的方式。发AT命令的流程,我们放到后面分析request的连载文章里。这里可以看到,主要是一些参数配置,以及网络状态的检查等
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mainloop的作用:主要任务是建立起与硬件的通信,然后通过at_open()->readloop()->line()方法阻塞等待是否又数据过来,并向硬件主动上报或响应(OK)。
发命令的是由initializeCallback()发过来的ATE0Q0V1等
1.打开rild socket(init.rc传过来的)是用于等待read()后java层传来的数据(再去调nitializeCallback发at命令)
2.就是调用RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);函数,用来向modem发生AT命令
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void *
mainLoop(void *param)
{
LOGD("----shi-xian---mainLoop-----");
LOGI("------------in mainLoop\n");
int fd;
int ret;
static struct timeval TIMEVAL_DELAYINIT = {0,0};//延时函数
AT_DUMP("== ", "entering mainLoop()", -1 );
at_set_on_reader_closed(onATReaderClosed);
at_set_on_timeout(onATTimeout);
for (;;) {
fd = -1;
while (fd < 0) {
if (s_port > 0) {
fd = socket_loopback_client(s_port, SOCK_STREAM);
} else if (s_device_socket) {
if (!strcmp(s_device_path, "/dev/socket/qemud")) {
/* Qemu-specific control socket */
//1:
LOGD("--1----socket_local_client---after--qian-yi-ge-");
fd = socket_local_client( "qemud", ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM );
if (fd >= 0 ) {
char answer[2];
if ( write(fd, "gsm", 3) != 3 ||
read(fd, answer, 2) != 2 ||
memcmp(answer, "OK", 2) != 0)
{
close(fd);
fd = -1;
}
}
}
else
//1:
LOGD("---2---socket_local_client---after--hou-yi-ge-");
fd = socket_local_client( s_device_path, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM );
} else if (s_device_path != NULL) {
fd = open (s_device_path, O_RDWR);
if ( fd >= 0 ) {
/* disable echo on serial ports */
struct termios ios;
tcgetattr( fd, &ios );
ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
tcsetattr( fd, TCSANOW, &ios );
LOGI("---open() sucess\n");
}else{
LOGI("---open() failed\n");
}
}
if (fd < 0) {
perror ("opening AT interface. retrying...");
sleep(10);
/* never returns */
}
}
s_closed = 0;
//2:
LOGD("--jin--ru---at_open---");
ret = at_open(fd, onUnsolicited);
if (ret < 0) {
LOGE ("AT error %d on at_open\n", ret);
return 0;
}
TIMEVAL_DELAYINIT.tv_sec = 15;
LOGD("going to enter initializeCallback\n");
LOGD ("Will delay RIL initialization for %d seconds", TIMEVAL_DELAYINIT.tv_sec);
//3:
LOGD("---jin -ru--RIL_requestTimedCallback(initializeCallback, NULL,
&TIMEVAL_0);---");
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);//打印AT> ATE0Q0V1
// Give initializeCallback a chance to dispatched, since
// we don't presently have a cancellation mechanism
sleep(1);
waitForClose();
LOGI("Re-opening after close");
}
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
at_open()的作用:1.可以添加自己想要的modem
2.函数建立起这一设备文件上的reader等待循环
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* Starts AT handler on stream "fd'
* returns 0 on success, -1 on error
*/
int at_open(int fd, ATUnsolHandler h)
{
int ret;
pthread_t tid;
pthread_attr_t attr;
LOGD("---shi--xian --at_open-----------");
s_fd = fd;
s_unsolHandler = h;
s_readerClosed = 0;
s_responsePrefix = NULL;
s_smsPDU = NULL;
sp_response = NULL;
/* Android power control ioctl */
#ifdef HAVE_ANDROID_OS
#ifdef OMAP_CSMI_POWER_CONTROL
ret = ioctl(fd, OMAP_CSMI_TTY_ENABLE_ACK);
if(ret == 0) {
int ack_count;
int read_count;
int old_flags;
char sync_buf[256];
old_flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, old_flags | O_NONBLOCK);
do {
ioctl(fd, OMAP_CSMI_TTY_READ_UNACKED, &ack_count);
read_count = 0;
do {
ret = read(fd, sync_buf, sizeof(sync_buf));
if(ret > 0)
read_count += ret;
} while(ret > 0 || (ret < 0 && errno == EINTR));
ioctl(fd, OMAP_CSMI_TTY_ACK, &ack_count);
} while(ack_count > 0 || read_count > 0);
fcntl(fd, F_SETFL, old_flags);
s_readCount = 0;
s_ackPowerIoctl = 1;
}
else
s_ackPowerIoctl = 0;
#else // OMAP_CSMI_POWER_CONTROL
s_ackPowerIoctl = 0;
#endif // OMAP_CSMI_POWER_CONTROL
#endif /*HAVE_ANDROID_OS*/
pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
#ifdef HUAWEI_EM770W //新增的modem,在创建一个线程去实现它的功能
int fd2 = -1;
while(fd2 < 0) {
fd2 = open ("/dev/ttyUSB2", O_RDWR);
if (fd2 < 0) {
perror ("opening URC interface. retrying...");
sleep(10);
}
}
if(fd2 > 0) {
urc_fd = fd2;
struct termios ios;
tcgetattr( fd2, &ios );
ios.c_lflag = 0;
tcsetattr( fd2, TCSANOW, &ios );
}
ret = pthread_create(&s_tid_reader_urc, &attr, urc_readerLoop, &attr);
if (ret < 0) {
perror ("pthread_create");
return -1;
}
#else
LOGD("---at_open----ru-kou--ret = pthread_create(&s_tid_reader, &attr, readerLoop,&attr);------");
ret = pthread_create(&s_tid_reader, &attr,readerLoop, &attr);
if (ret < 0) {
perror ("pthread_create");
return -1;
}
#endif
return 0;
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
readerLoop()作用:阻塞地去读取,modem发过来的AT命令,并回应(OK)和上报上层,后再去阻塞的形式读取等待下一个AT命令
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static void *readerLoop(void *arg)
{
LOGD("---shi-xian --readerLoop--");
for (;;) {
const char * line;
line = readline();
if (line == NULL) {
break;
}
if((isSMSUnsolicited(line)) {
char *line1;
const char *line2;
LOGD("---shi-xian --readerLoop-isSMSUnsolicited(line)---");
// The scope of string returned by 'readline()' is valid only
// till next call to 'readline()' hence making a copy of line
// before calling readline again.
line1 = strdup(line);
line2 = readline();
if (line2 == NULL) {
break;
}
if (s_unsolHandler != NULL) {
s_unsolHandler (line1, line2);
}
free(line1);
} else {
processLine(line);
}
#ifdef HAVE_ANDROID_OS
if (s_ackPowerIoctl > 0) {
/* acknowledge that bytes have been read and processed */
ioctl(s_fd, OMAP_CSMI_TTY_ACK, &s_readCount);
s_readCount = 0;
}
#endif /*HAVE_ANDROID_OS*/
}
onReaderClosed();
return NULL;
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* Reads a line from the AT channel, returns NULL on timeout.
* Assumes it has exclusive read access to the FD
*
* This line is valid only until the next call to readline
*
* This function exists because as of writing, android libc does not
* have buffered stdio.
*/
static const char *readline()
{
ssize_t count;
char *p_read = NULL;
char *p_eol = NULL;
char *ret;
LOGD("----shi--xian---readline-----");
/* this is a little odd. I use *s_ATBufferCur == 0 to
* mean "buffer consumed completely". If it points to a character, than
* the buffer continues until a \0
*/
if (*s_ATBufferCur == '\0') {
/* empty buffer */
s_ATBufferCur = s_ATBuffer;
*s_ATBufferCur = '\0';
p_read = s_ATBuffer;
} else { /* *s_ATBufferCur != '\0' */
/* there's data in the buffer from the last read */
// skip over leading newlines
while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n')
s_ATBufferCur++;
p_eol = findNextEOL(s_ATBufferCur);
if (p_eol == NULL) {
/* a partial line. move it up and prepare to read more */
size_t len;
len = strlen(s_ATBufferCur);
memmove(s_ATBuffer, s_ATBufferCur, len + 1);
p_read = s_ATBuffer + len;
s_ATBufferCur = s_ATBuffer;
}
/* Otherwise, (p_eol !- NULL) there is a complete line */
/* that will be returned the while () loop below */
}
while (p_eol == NULL) {
if (0 == MAX_AT_RESPONSE - (p_read - s_ATBuffer)) {
LOGE("ERROR: Input line exceeded buffer\n");
/* ditch buffer and start over again */
s_ATBufferCur = s_ATBuffer;
*s_ATBufferCur = '\0';
p_read = s_ATBuffer;
}
do {
count = read(s_fd, p_read,
MAX_AT_RESPONSE - (p_read - s_ATBuffer));
} while (count < 0 && errno == EINTR);
if (count > 0) {
AT_DUMP( "<< ", p_read, count );
s_readCount += count;
p_read[count] = '\0';
// skip over leading newlines
while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n')
s_ATBufferCur++;
p_eol = findNextEOL(s_ATBufferCur);
p_read += count;
} else if (count <= 0) {
/* read error encountered or EOF reached */
if(count == 0) {
LOGD("atchannel: EOF reached");
} else {
LOGD("atchannel: read error %s", strerror(errno));
}
return NULL;
}
}
/* a full line in the buffer. Place a \0 over the \r and return */
ret = s_ATBufferCur;
*p_eol = '\0';
s_ATBufferCur = p_eol + 1; /* this will always be <= p_read, */
/* and there will be a \0 at *p_read */
//在这打印Modem回应的OK信息
LOGD("AT< %s\n", ret);
return ret;//返回,读取好的上面信息
}
------------------