RIL层代码的理解(电话系统)

我主要参考网上的资料和相关书籍。
这个只是初稿,没有整理,而且代码还没看完,还会继续完善。
RIL
Rild
rild中实现的main函数是ril层的入口,负责完成初始化工作。
Libreference-ril.so
Rild通过dlopen方式加载,结合稍微松散,这也是因为libreference-ril.so主要负责与modem硬件通信的缘故。这样做方便替换或修改以适合更多的modem产品。并将libril.so传递来的请求转换为AT命令,然后传递给modem,同时监控modem的反馈信息,并将反馈信息传给libril.so。在初始化时,rild通过RIL_Init获取一组函数指针并与之建立关系。
Radiooptions
Radiooptions通过获取启动参数,通过socket与rild通信,可供调试时配置modem参数。

rild.c中的main函数,主要完成三个任务:
1.	开启libril.so中的event机制,由RIL_startEventLoop()实现,最核心的是一个多路I/O消息循环。
RIL_startEventLoop()函数在ril.cpp中,主要通过pthread_create(&s_tid_dispatch,&attr,eventLoop,NULL)创建一个dispatch线程,入口点在eventLoop,它在ril.cpp中实现,并调用ril_event_loop()(在RIL_event.cpp中)函数,建立消息队列机制。

struct ril_event{
		struct ril_event *next;  //下一个消息
		struct ril_event *prev;	//前一个消息
		int fd;				//fd句柄,与struct ril_event绑定
		int index;				//侦听列表(watch_list)中的索引,侦听列表采用的是数组而不是链表
		bool persist;			//是否常驻侦听列表(watch_list)中,若常驻,则在转到pending_list中的时候不删除
		struct timeval timeout;	//超时,在处理消息时,与当前的时间比较,以确定是否处理该消息
		ril_event_cb func;		//回调函数。此消息对应的处理函数
		void *param;			//回调函数的参数
}


eventLoop()
ril_event_init()
ril_event_set(struct ril_event *ev,int fd,bool persist,ril_event_cb func,void *param)
ril_event_add(struct ril_event *ev)
ril_timer_add(struct ril_event *ev,struct timeval *tv)
ril_event_del(struct ril_event *ev)
ril_event_loop(),每次循环主要做三件事:
1.	初始化。主要是获取要等待的描述符,以及获取超时信息,以便能够及时处理定时消息。
2.	等待。等待socket客户端,看是否有新消息,或间隔一定的时间,处理定时消息。
3.	依次处理三种类型的消息。

Memcpy(&rfds,&readFds,sizeof(fd_set));
获取要等待的描述符。(为什么每次循环都要做呢?)

if(-1 == calcNextTimeout(&tv){		//获取超时参数,若失败则返回-1
		dlog(“~~~ no timers;blocking indefinitely ~~~”);
		ptv = NULL;
}else{		//获取成功,则使用该值。
		dlog(“~~~ blocking for %ds + %dus ~~~”,(int)tv.tvsec,(int)tv.tv_usec);
		ptv = &tv;   //ptv在select()中使用
}
//计算处理第一个消息的时间点与当前时间的间隔
static int calcNextTimeout(struct timeval *tv)
{
		struct ril_event *tev = timer_list.next;   //获取第一个定时消息的时间间隔。定时消息是排序的。
		// Sorted list, so calc based on first node
		if (tev == &timer_list){		// calcNextTimeout()函数先是判断超时消息列表是否为空,为空则返回-1
			// no pending timers
			return -1;
		}

		if (timercmp(&tev->timeout, &now, >)) {	//处理消息的时间还没到(时间点)
        timersub(&tev->timeout, &now, tv);
    } else {	//处理消息的时间已过
        // timer already expired.
        tv->tv_sec = tv->tv_usec = 0;		//tv的最小值为0,此时会导致select()直接退出
}
}

n = select(nfds, &rfds, NULL, NULL, ptv);
实际上等待的描述符只有两种:socket描述符和用于发送定时消息的pipe描述符

最后,就是处理三种类型的消息:
	//Check for timeouts
	processTimeouts();		//处理定时消息
	//Check for read-ready
	processReadReadies(&rfds,n);	//处理侦听消息
	//Fire away
	firePending();		//处理挂起消息

	processTimeouts()是处理定时消息。它查找当前时间点以前的消息(因为是排序的,所以只要查找前面几个就可以)。然后,将这些消息放到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);		//从timer_list中移除
        addToList(tev, &pending_list);	//添加到pending_list中
        tev = next;
    }
    MUTEX_RELEASE();
    dlog("~~~~ -processTimeouts ~~~~");
}

接下来处理watch_list列表。processReadReadies(&rfds,n)的主要工作是从侦听列表中取出消息,移至pending_list中。如果ril_event的persist设置了,就不会从侦听列表中移除该消息。
static void processReadReadies(fd_set * rfds, int n)
{
    dlog("~~~~ +processReadReadies (%d) ~~~~", n);
    MUTEX_ACQUIRE();

    for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) {
        struct ril_event * rev = watch_table[i];
        if (rev != NULL && FD_ISSET(rev->fd, rfds)) {   //FD_ISSET()判断描述符是否在当前循环的等待列表(rfds)中
            addToList(rev, &pending_list);	//添加到pending_list中
            if (rev->persist == false) {		//判断ril_event的persist是否设置了,如果没有设置,就调用removeWatch(rev,i)将该消息从watch_list中移除
                removeWatch(rev, i);
            }
            n--;
        }
    }

    MUTEX_RELEASE();
    dlog("~~~~ -processReadReadies (%d) ~~~~", n);
}

最后处理pending_list。依次调用pending_list中的消息的回调函数。
static void firePending()
{
    dlog("~~~~ +firePending ~~~~");
    struct ril_event * ev = pending_list.next;
    while (ev != &pending_list) {  	//pending_list不为空
        struct ril_event * next = ev->next;
        removeFromList(ev);
        ev->func(ev->fd, 0, ev->param);	//func是回调函数,fd为消息中的描述符,以此与ril_event联系
        ev = next;
    }
    dlog("~~~~ -firePending ~~~~");
}


具体流程:ril_event_int()完成后,通过ril_event_set配置一个新的ril_event,并通过ril_event_add加到队列之中(什么队列?)(实际用的是rilEventAddWakeup),它会把队列里所有的ril_event的fd加入readFds集合中。在进入ril_event_loop之前,已经加入了一个s_wakeupfd_event,通过pipe机制(pipe()函数会创建一对文件描述符,一个用于读,一个用于写,都被记录在全局变量中。它实际是用来发送定时消息的)实现的,这个event为了可以在一些情况下,能内部唤醒ril_event_loop的多路复用阻塞。然后调用ril_event_loop(),其中的I/O多路复用机制(select)会等待这些fd状态的变化,如果任何一个fd有数据写入,则进入流程processTimeouts(),processReadies(&rfds,n),firePending()。
  int filedes[2];
  ret = pipe(filedes);
	 s_fdWakeupRead = filedes[0];	//消息循环中用于侦听
  s_fdWakeupWrite = filedes[1];	//用于通知消息循环定义消息已发送

消息循环(ril_event_loop()函数内)位于单独的线程中,用来处理三种消息,分别对应三个不同的队列,面对的是三种不同的需求:
a.	定时列表(timer_list):此队列中的消息主要用来处理一些延时的操作。比如:从飞信模式切换到通信模式(实际是打开通信模块)时,若SIM卡未准备好,那么就需要延时一段时间再检查是否准备好,此时就需要将消息扔至此队列。
b.	侦听列表(watch_list):此队列中的消息一是作为socket服务端,用来侦听客户端的请求;另一个是作为本进程中的其他线程(如检查通信模块来电消息的线程)传递过来的消息。
c.	挂起列表(pending_list):实际上在处理上面两种消息的时候,并不真正的处理消息体,而是将符合条件的消息丢到本队列中。由于消息附带有消息处理函数,所以在处理本队列消息时,只要直接触发就可以。

2.	初始化libreference-ril.so,通过RIL_init完成。
RIL_Iint通过参数获取硬件接口的设备文件或模拟硬件接口的socket,然后新开一个mainLoop线程继续初始化。
mainLoop的主要任务是建立与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应。
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;

    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;

            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;
    }

    pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);

    return &s_callbacks;
}

static void *
mainLoop(void *param)
{
       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 */
                    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
                    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 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) {
                    /* disable echo on serial ports */
                    struct termios  ios;
                    tcgetattr( fd, &ios );
                    ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
                    tcsetattr( fd, TCSANOW, &ios );
                }
            }

            if (fd < 0) {
                perror ("opening AT interface. retrying...");
                sleep(10);
                /* never returns */
            }
        }

        s_closed = 0;
        ret = at_open(fd, onUnsolicited);

        if (ret < 0) {
            LOGE ("AT error %d on at_open\n", ret);
            return 0;
        }

        RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);

        // Give initializeCallback a chance to dispatched, since
        // we don't presently have a cancellation mechanism
        sleep(1);

        waitForClose();
        LOGI("Re-opening after close");
    }
}


static UserCallbackInfo *
internalRequestTimedCallback (RIL_TimedCallback callback, void *param,
                                const struct timeval *relativeTime)
{ 	//用于发送定时消息。它包含了三个参数:callback回调函数、param参数、relativeTime时间间隔
    struct timeval myRelativeTime;
    UserCallbackInfo *p_info;

    p_info = (UserCallbackInfo *) malloc (sizeof(UserCallbackInfo));

    p_info->p_callback = callback;
    p_info->userParam = param;

    if (relativeTime == NULL) {
        /* treat null parameter as a 0 relative time */
        memset (&myRelativeTime, 0, sizeof(myRelativeTime));
    } else {
        /* FIXME I think event_add's tv param is really const anyway */
        memcpy (&myRelativeTime, relativeTime, sizeof(myRelativeTime));
    }

    ril_event_set(&(p_info->event), -1, false, userTimerCallback, p_info);		//对ril_event进行设置

    ril_timer_add(&(p_info->event), &myRelativeTime);		//将定时消息添加到timer_list中,myRelativeTime为多久后出发消息

    triggerEvLoop();	//通知消息循环
    return p_info;
}


// Add timer event
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 = -1; // make sure fd is invalid

        struct timeval now;
        getNow(&now);
        timeradd(&now, tv, &ev->timeout);

        // keep list sorted
        while (timercmp(&list->timeout, &ev->timeout, < )  	//找到插入位置。插入的ril_event的位置之前的消息的timeout小于它的timeout,大于或等于它的timeout的消息都在它的后面
                && (list != &timer_list)) {
            list = list->next;
        }
        // list now points to the first event older than ev
        addToList(ev, list);
    }

    MUTEX_RELEASE();
    dlog("~~~~ -ril_timer_add ~~~~");
}



static void triggerEvLoop() {
    int ret;
    if (!pthread_equal(pthread_self(), s_tid_dispatch)) {
        /* trigger event loop to wakeup. No reason to do this,
         * if we're in the event loop thread */
         do {
            ret = write (s_fdWakeupWrite, " ", 1);	//向管道的写端写入数据
         } while (ret < 0 && errno == EINTR);
    }
}
为什么采用管道来通知定时消息已送至消息队列?这是为了和socket描述符保持一致,以便采用一致的方式来处理。

3.	通过RIL_init获取RIL_RadioFuntions函数指针,通过RIL_register完成注册,并打开与上层通信的socket通道。
   RIL_RadioFunctions结构是RIL实现库需要实现的部分,通过RIL_Init返回rild。
至此,rild到RIL的实现库请求发送路径和反之的响应路径就已经搭建起来了。






RILD消息循环是一个通用的消息循环模型,也可以说是一种设计模式。

RIL_register()在ril.cpp中实现->ril_event_set(&s_listen_event,s_fdListen,false,listenCallback,NULL)->listenCallback(int fd,short flags,void *param)->ril_event_set(&s_commands_event,s_fdCommand,1,processCommandsCallback,p_rs)->processCommandsCallback



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值