rilmulticard.docx

上传人:b****8 文档编号:9090696 上传时间:2023-02-03 格式:DOCX 页数:13 大小:66.81KB
下载 相关 举报
rilmulticard.docx_第1页
第1页 / 共13页
rilmulticard.docx_第2页
第2页 / 共13页
rilmulticard.docx_第3页
第3页 / 共13页
rilmulticard.docx_第4页
第4页 / 共13页
rilmulticard.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

rilmulticard.docx

《rilmulticard.docx》由会员分享,可在线阅读,更多相关《rilmulticard.docx(13页珍藏版)》请在冰豆网上搜索。

rilmulticard.docx

rilmulticard

AndroidhardwareRIL层多卡功能的具体实现

修改记录

作者

日期

修改内容

黄理洪

2010-5-20

加入智能识别卡槽、逻辑卡号到真实卡号的映射、自动关闭未插卡的modem等说明。

黄理洪

2010-5-15

增加“修改上报radiostate的方法”

黄理洪

2010-4-11

初稿

一、原有RIL层简介

android的hardwareRIL(radiointerfacelayer)是手机modem和androidframework层的接口,代码位于hardware/ril/,文件类型包含c和c++,共约6千多行。

整个ril编译出来得到三个文件:

libreference-ril.so(负责直接和modem交互)

libril.so(负责通过socket和framework层交互)

rild(作为守护进程的可执行程序,是上面两个库的入口)

上面两个.so库实现了三个的线程:

Mainloopthread,只是起初始化串口和modem的作用,后期处于idle状态;

requestdispatchthread,负责分发framework层对modem的ATcommand;

responsereaderthread,负责从modem读取response。

下图是RIL层的总体框图:

绿色箭头指示

responsereaderthread

ATcommand是发送给modem的AT指令,如自动注册网络用AT+COPS=0,拨打移动客服用ATD10086;挂电话用ATH。

Response是由modem返回的响应信息,包括请求响应(指对ATcommand的响应)和主动响应(英文称为unsolicitedresponse,包括短信和手机网络主动上报信息)。

二、改造需求

现在的ril只支持一个modem带一张手机卡,我们要实现多模多卡多待手机就需要让它同时支持多个modem,多张手机卡。

需要支持的modem按照网络模式分,有下列四类:

1、GSMmodem

a.单卡GSMmodem

b.双卡GSMmodem

2、TDS-CDMAmodem(属于中国移动的3G标准)

3、CDMA2000EVDOmodem(属于中国电信的3G标准)

4、WCDMAmodem(属于中国联通的3G标准)

目前尚未发现支持双卡的3Gmodem,所以一个多模手机的极限情况是同时具有上面四种modem,带5张手机卡(2张2G的sim卡,3张3G卡)。

我们的ril层改造目标就是支持这种极限连接方式。

如同其他对android的改造一样,在达到目的的前提下,我们希望对android的修改越少越好。

目前我们只有智源单卡GSMmodemIW368,和联芯TDS-CDMA\modemLC6311,这两个模块可以用于多卡功能的前期调试。

三、具体实现

1.ril层对modem和framework层的信息接口的重新约定

Modem主要有uart接口和usb接口(3G比2G有更高的数据传输速度,一般用usb接口;华为海思正在研发的4GLTEmodem加上了1Gbit/s的以太网接口实现更高的速度),从ril角度来看,它是几个虚拟的串口设备。

比如展讯的双卡GSMmodem,接在物理的uart2上,那么uart2对应的/dev/ttyS2将虚拟出/dev/pts/0、/dev/pts/1和/dev/pts/2三个虚拟串口设备分别对应sim1和sim2的AT指令通道和上网的gprs通道;又如大唐的3GmodemLC6311,接在我们cpu的usbhost接口上,通过usbserial驱动虚拟出/dev/ttyUSB0~5,共6个ttyUSB串口设备,我们只用其中的ttyUSB3作为AT指令通道,ttyUSB5作为上网数据通道。

对于ril层多卡的改造,我们主要考虑AT指令通道的串口设备(下面简称为AT串口设备),暂时不考虑上网数据通道的串口设备,因为ril层三个线程Mainloopthread,requestdispatchthread,ATreaderthread操作的都是AT指令通道。

上网数据通道仅被一个datacallrequest调用,很单纯。

一张手机卡(下面有时称其为card)对应一个AT串口设备,所以要建一个cardid和AT串口设备的描述符fd的对应表:

Modem网络模式

fd

(AT串口设备描述符)

Cardid(十进制)

GSM

fd_G0

11

fd_G1

12

WCDMA

fd_W0

21

TDS-CDMA

fd_T0

31

CDMA2000(EVDO)

fd_C0

41

其中,双卡GSMmodem的fd_G0和fd_G1是通过open/dev/pts/0和/dev/pts/1得到的设备描述符,分别对应两张sim卡。

fd_T0是openttyUSB3得到的设备描述符,对应一张中国移动手机卡。

严格地说,cardid其实应该称为卡槽id,因为对于多卡手机来说,卡槽常有而卡不常有。

每次开机,ril层首先自己探测哪些卡槽插了卡,当与framework的socket通信建立时,就把所有已插入卡的id传给framework。

framework层通过cardid的十位数就可以得知该手机卡的网络模式。

在reference-ril/misc.h中,定义下面表示真实卡号的宏,和表示卡槽信息的结构体:

#defineCARDID_GSM_011

#defineCARDID_GSM_112

#defineCARDID_TDSCDMA_021

#defineCARDID_EVDO_031

#defineCARDID_WCDMA_041

structcard_slot_t{

intid;

char*modem_name;

char*tty_name;

intfd;

};

在reference-ril/misc.c中定义具体的卡槽信息:

structcard_slot_tcard_slot_info[]={

{CARDID_TDSCDMA_0,"LC6311","/dev/ttyUSB3",-1},

{CARDID_GSM_0,"IW368","/dev/pts/2",-1},

{0}

};

其中card_slot_info[].fd是在open/dev/ttyUSB3或/dev/pts/2成功后,用open的返回值来赋值。

 

在framework层,GSM、WCDMA和TDS-CDMA三种网络模式都使用GSMPhone类,EVDO使用CDMAPhone类。

为了方便处理,framework使用逻辑卡号,逻辑卡号的十位数表示该卡使用GSMPhone类还是CDMAPhone类,分别对应于1和2;个位数表示某类的第几张卡。

举个例子,如果实际插入卡槽的卡的真实卡号有11、31和41,那么它们对应的逻辑卡号分别是11、12和21。

真实卡号比逻辑卡号带有更多的网络类型信息,也需要保留。

理论上逻辑卡号和真实卡号的转换工作可以放在framework中做,但在实践中遇到了一些困难,所以还是放在hardwareril中来做。

这样framework和ril之间就采用逻辑卡号来通信。

也就是说framework把“逻辑卡号+request”发给ril,ril把“逻辑卡号+response”发给framework。

 

清楚了ril层对modem和framework层的信息接口,下面就可以展开具体的ril层的修改工作了。

修改ril主要有三部分工作,分别对应ril的三个线程。

首先需要增加两个全局变量:

request_card_id(表示正分发的ATcommandrequest发向哪张卡,会被mainloop和requestdispatch两个线程使用)和response_card_id(表示正从哪张卡读取response信息,只被responsereaderthread使用)。

2.修改mainloopthread

上面提到,该线程只是起初始化串口和modem的作用,原有程序只初始化了一个串口设备和一个modem,现在要根据card_slot_info[]的内容来初始化多个串口设备和modem。

下图为修改之后的mainloop相关的流程图,蓝色为修改部分。

01

3.修改requestdispatchthread

framework层通过socket把requestcardid传递给ril,ril在processCommandBuffer()中把requestcardid从socketdata中取出,最后在writeline()和writectrlz()中通过requestcardid判断往哪个modem发送指令。

下图为修改之后的requestdispatchthread相关的流程图,蓝色为修改部分。

02

4.修改ATreaderthread

修改readloop函数,使其用select方法阻塞式地同时监视所有手机卡的fd是否可读,某个fd可读就解除阻塞,根据fd设置response_card_id,然后调用readline读取response。

在程序中response被分为三种:

1、短信息;

2、主动上报(unsolicitedresponse),如网络状态、信号强度等;

3、ATcommandrequest的response。

如果是短信息或主动上报,那么就在RIL_onUnsolicitedResponse()里把response_card_id和response一起封到parcel里,通过socket传给framework层。

如果是ATcommandresponse,并且此时responsecardid==requestcardid,那么释放信号量解除ATcommandrequestthread的阻塞。

会不会出现读到的是ATresponse但responsecardid!

=requestcardid的情况?

如果不考虑主动上报被误认为ATresponse的情况(这种情况在后面的技术要点备忘里讨论),那么不会发生这种情况,因为某卡读到了ATresponse说明之前该卡发过ATcommandrequest,它发ATcommand的时候,就会阻塞住,一直等ATresponse,不会让其他卡有发ATcommand的机会。

所以,当收到ATresponse时,不用判断response_card_id和request_card_id是否相等,就可以直接解除ATcommand的阻塞了。

接触阻塞之后的requestdispatchthread在RIL_onRequestComplete()里把request_card_id和经过解析的response一起封到parcel里,通过socket传给framework层。

下图是ATreaderthread的程序流程图,其中蓝色部分是针对多卡功能的修改。

03

5.修改上报radiostate的方法

原始的androidRIL代码只用一个变量sState来表示卡的radiostate,要支持多卡,就必须用一个数组sState[MAX_CARD_NUM]来表示所有卡的radiostate。

除了与framework层的socket通信刚建立时的radiostate是非请求的主动上报外,其余的radiostate都是framework通过requestradiopower获取的。

下图是RIL处理requestradiopower的程序流程图,其中蓝色部分是针对多卡功能的修改。

04

下图是android原始的framework收到RIL上报的radiostate后的相应动作的程序流程图:

四、技术要点备忘

1.表示多卡中哪一张卡的信息,用全局变量表示还是使用参数传递的选择

使用参数传递的方法对代码的改动量很大,需要给很多相关函数增加相应的表示哪张卡的参数。

使用两个全局变量request_card_id和response_card_id,大大简化改造工作,因为rild是守护进程,只会运行一个,request_card_id和response_card_id只会被一个线程使用,不存在多线程安全问题。

2.读取多卡时使用多线程还是select的选择

如果不使用select同时检测多张卡,那么就要对每张卡开一个线程来读取信息,这将使改造工作很复杂。

3.多卡功能使atchannel.c中的readerLoop()的情况变复杂,要针对各种情景进行修改。

情景1:

一张卡一次主动上报了大量信息,里面包含了若干行信息,都缓存到readbuffer里了,readline()只能一次返回一行数据进行处理,如果在处理完所有信息行之前,另一张卡产生response了,select检测到它的fd可读,于是responsecardid改为这张的id了,之前缓存在readbuffer里的数据就都被误认为是这张卡的了。

要避免这种情况产生,就需要在一张卡可读时,把该卡缓存在readbuffer中的response全都处理完。

情景2:

比如发送下面查询网络注册状态的AT指令:

AT+CREG?

response返回下面两行:

+CREG:

1,1,"0408","5C15"

OK

response的每一行都是以\r\n结尾的,readline()根据行尾的\r或\n得知读取到一行,对于上面的两行response,line=readline()将被调用两次,line是指向所读到的当前行的指针。

第一行不是finalresponse,程序会调用addIntermediate(line)把它们保存起来,等到作为finalresponse的“OK”返回时,因发送查询网络状态请求“AT+CREG?

”而阻塞的ATcommandthread才解除阻塞。

如果在读取到+CREG:

1,1,"0408","5C15"时,其它卡突然主动上报了一个网络状态的信息,主动上报的前缀+CREG与上一张卡AT+CREG?

的response的前缀一样,ATcommandthread会误以为这是上一张卡的response的信息而错误地用addIntermediate(line)来保存信息。

有两种解决方法:

一是,在addIntermediate(line)之前判断responsecardid与requestcardid是否相等,相等才add,不相等就认为是主动上报或短信;二是,一旦某张卡开始接收ATcommandresponse,就一直针对这张卡循环调用readline()和processline(),直到该ATcommandresponse完全被处理完毕。

我们采用第二种方法,因为这种方法代码修改起来相对简单。

情景3:

当读到下面的response:

OK

readline()会返回指向”OK”的指针,同时s_ATBufferCur跳过,指向了

再次调用readline()时,readline()首先判断s_ATBufferCur是否为NULL,如果非NULL就认为s_ATBuffer里有未处理完的数据,但其实只有一个,这种情况下会再去阻塞地read,会一直阻塞住直到下次该卡可读为止,这期间,就算其他卡可读也没有机会去读了。

这对于单卡可以容忍,但对于多卡显然是不合理的。

综合解决方法:

综合考虑上面三种情景,对atchannel.c的readerLoop()做了下面蓝色部分所示的修改。

staticvoid*readerLoop(void*arg)

{

for(;;){

constchar*line;

memcpy(&rfds,&s_readFds,sizeof(fd_set));

n=select(nfds,&rfds,NULL,NULL,NULL);

for(i=0;i

if(FD_ISSET(card_slot_info[i].fd,&rfds)){

s_read_fd=card_slot_info[i].fd;

break;

}

}

response_card_id=fd_to_card_id(s_read_fd);

card_response_final=1;//addIntermediate(line)canmakeit0

for(;;){

line=readline();

if(isSMSUnsolicited(line)){

s_unsolHandler(line1,line2);

}else{

processLine(line);

}

if((card_response_final==1)&&((*s_ATBufferCur=='\0')||((*s_ATBufferCur=='\n')&&(strlen(s_ATBufferCur)==1))))

break;

}

}

}

当一张卡被select认为可读时,会循环调用readline()进行读取,退出循环的条件是(card_response_final==1)&&((*s_ATBufferCur=='\0')||((*s_ATBufferCur=='\n')&&(strlen(s_ATBufferCur)==1))),其中条件((*s_ATBufferCur=='\0')||((*s_ATBufferCur=='\n')&&(strlen(s_ATBufferCur)==1))避免了第一个和第三个情景的问题,条件(card_response_final==1)避免了第二个情景的问题。

4.如果有多个modem,可以把没插卡的modem关闭,以节省功耗

但如果所有的modem都没插卡,那么不能都把它们都关闭,要留一个开着,否则不能打紧急电话。

这种情况下是默认把card_slot_info[0]对应的modem打开。

对于没有插卡的卡槽,不必让select监视它是否可读,最好从fdset中把它的fd删除,因为内核对select实现是轮询方式,监听的fd越多,越费时间,但考虑多监听一两个空卡槽对效率影响可以忽略不计,所以这个改进目前还没实现。

一般需要考虑select效率的是网络服务器,它可能需要监听几千个fd,而有些fd很不活跃,这时就要考虑用epoll来替代select,epoll可以根据fd的活跃度调整监听策略。

5.智能识别卡槽

如果手机modem支持的卡槽个数大于手机实际拥有的卡槽个数,那么就要使用卡槽的智能识别。

比如某手机里有一个支持双卡的GSMmodem和一个支持单卡的EVDOmodem,理论上需要安装三个卡槽,但受到手机内部空间的限制,只能存在两个卡槽,插入这两个卡槽的卡的组合可能是:

G卡+G卡,G卡+C卡和C卡+G卡。

C卡必须接在EVDOmodem上,G卡必须连在GSMmodem上,所以卡槽和modem连接需要智能调节。

本文以这种情况为例,说明一下智能识别卡槽的方法。

初始状态让卡槽1和卡槽2都默认连接在GSMmodem上(如上图所示),这种初始状态可以让探测卡槽过程中切换卡槽次数最少。

为了让程序健壮,考虑了插卡的各种组合情况:

G+G,G+C,C+G,C+C,错卡+G或C。

具体实现的程序流程图如下:

05

6.linux内核中和modem相关的驱动

drivers/misc/jz_modem.c:

hardwareril通过该文件实现的ioctl来控制各modem底层行为,如上电、复位、切换卡槽、切换音频连接等。

在hardwareril中,ioctl的case相关的宏定义位于reference-ril/misc.h,这些定义要和jz_modem.c中的定义一致。

drivers/input/misc/jz_ringin.c:

处理在cpu休眠时,modem的ringin中断把cpu唤醒。

五、针对RIL多卡的具体代码

在android源码根目录下,

cdhardware/ril

svndiff-r673:

HEAD

六、参考资料

1.AdvancedProgrammingintheUNIX®Environment(关于socket、select和pthread的相关知识)

2.各modem的AT指令集

文中程序流程图采用MicrosoftOfficeVisio2003绘制。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1