平时打手机,用手机上网用的最多的就是2G/2.5G的gsm和gprs。究竟有多少人知道这个GSM/GPRS协议栈是如何工作的?
现在3G:WCDMA/CDMA2000/TDSCDMA等3G技术,即UMTS越来越扮演者重要的角色。但是究竟有多少人知道原来UMTS的数据传输除了空中接口即,a-bis接口发生了巨大变化外,其他的协议部分整体上是与GSM/GPRS,尤其是GPRS很相近的。
大家都学过TCP/IP协议栈,都知道MAC/IP/TCP+UDP/APP的协议结构。但是未来越来越重要的移动通信的基础的协议栈有谁留意过吗?
TCP/IP有开源的源代码,而GSM/GPRS的源代码可是有着非常强的版权保护的。我们都知道MTK是中国山寨的鼻祖,他家的核心技术之一当然就是通信芯片,而通信芯片中的核心当然就是协议栈啦,而他家的协议栈据我了解其实是英国CCWW公司授权的,也就是从外国人那里买过来的源代码,跟高通卖CDMA的方式差不多啦。
我们有没有必要深入的学习一下GSM/GPRS这个已经似乎有点过时的协议栈呢?我想现在很多喜欢操作系统的同学一定知道当今的Linux2.6和Linux0.1之间的血脉关系吧:虽然基本全变了(我们的GSM/GPRS和如今的UMTS可不是基本全变啊~),但是其哲学原理还是一样的(强烈推荐《操作系统之哲学原理》这本书,看的热血沸腾哦)
因为兴趣,因为项目的原因,我将会努力的把这个系列写完,为了鞭策自己,也可以给大家学习提供一些小小的参考。
这个系列和关于GSM/GPRS的教科书最大的区别就是完全以源代码为主线,而已经被国内外的无数教材和标准规范写烂了的内容,这里就会尽量少的设计。这个系列不是一个GSM/GPRS的入门读物,最开始的形式我尽可能的写的通俗易懂、由浅入深,从整体到局部,从概念到原理。但是这个总归是我的学习过程的一个小的总结和抽象,以为个人的背景知识不同,可能理解的程度也不同,我写的有不好的地方,请大家多多包涵。如果有什么问题,当然希望一起讨论,留言我会一一回复的。
各位看官,因为我们的重点是源代码,而商用的GSM/GPRS代码又是有版权的,而且现如今,除了OpenBTS这个开源的基站端协议栈有源代码外,我们没有可以参考的商用代码。但是,我们不是有协议方针工具吗,庆幸的是,我手头上有一份可以参考的部分GSM源代码。这个GSM源代码包括MS端和BS端,实现了大部分的功能。可以在协议方针工具中正确的运行。因此,我就以此套代码为基础,来一层一层的剖析这个GSM源代码,然后一点一点的过渡到GPRS上去。
好,废话不多说了。让我开始吧:
源代码包括:
cellular_gsm.h layer3_gsm.h mac_gsm.h phy_gsm.h Layer3_gsm.cpp mac_gsm.cpp phy_gsm.cpp
我们先从mac_gsm.cpp开始
mac_gsm.cpp首先分析mac_gsm.cpp就不得不要先看一下mac.cpp,mac.cpp我们所使用的协议仿真软件中用来实现mac层的源文件。因为GSM协议只是这个协议方针软件中的一种MAC层协议而已,而为了我们未来要添加实现自己的GPRS协议栈,我们非常有必要看一看这个mac.cpp文件。
学习这个mac.cpp文件的目的是要知道在mac_gsm.cpp中的那些函数:初始化函数,分配器,从上下层接收数据,以及其他的由mac.cpp调用的函数是如何在mac.cpp中组织的。
在mac.cpp中: 1 头文件要包含mac_gsm.h,这里面定义了一些结构,所以当添加自己的mac协议时,也要添加相应的一个头文件。
2 IPv4AddressToHWAddress这个函数是将IP地址转换成MacHWAddress
其中MAC_PROTOCOL_GSM这个宏常量是在mac.h中定义的
所以,如果要添加自己的协议,也要在这里添加一个常量
3 MacConfigureHWAddress这里表示如果macProtocolName==GSM的话,就使用MAC_SetFourByteMacAddress,也就是使用4个字节的MAC地址
3 AddNodeToSubnet这个AddNodeToSubnet函数很长,我们来从总体上分析一下这个函数:
从结构上来看:
最前面的部分是对个别的几个协议进行特殊地处理
然后从这里开始:
开始初始化这些协议
PHY-MODEL一直到这里:下面出现的协议需要一个PHY模型
显示做一下需要的预处理功能
然后开始处理各个协议:
总体上分为两部分:FCSC-一部分,非FSCS一部分。我们的GSM需要的PHY MODEL就属于这个非FSCS中的一部分:
所以,如果添加一个类似GSM的协议,这里应当也要填上一个对应的处理项
到这里:需要添加PHY MODEL就完了,简短的做一下下一步的预处理,然后正是开始一个一个处理协议:根据macProtocolName,让相应的mac 层协议进行初始化
我关注的GSM的初始化在这里:
其中重点是MacGsmInit(node,interfaceIndex,nodeInput,nodesInSubnet),这个函数定义在mac_gsm.cpp中
4 AddNodeToIpv6Network这里的情况和AddNodeToSubnet差不多,只不过是Ipv6版本的。
也是有一个处理PHY-MODEL:
然后初始化mac 层协议:
5 ProcessLinkLine:这个函数中也出现了GSM相关的配置:
#暂不知道用途
6 ProcessSubnetLine
这里也出现了GSM相关的配置:
#暂不知道用途
6 MAC_processEvent用来模拟MAC层收到消息时的行为
调用MAC_PROTOCOL_GSM时的分配器,将调用的结点,结点中的接口索引号,和消息传给GSM MAC层的分配器:GacGsmLayer,在mac_gsm中定义
7 MAC_NetworkLayerHasPacketToSend当网络层队列为空时,处理从网络层来的数据包
调用mac_gsm.cpp中定义的GSM协议对应的处理函数
在进一步跟进MacGsmNetworkLayerHasPacketToSend()函数到mac_gsm.cpp中时,发现这个函数是个空的:
#悬疑
8 MAC_ReceivePacketFromPhy在这个函数里,调用mac_gsm.cpp中定义的MacGsmReceivePacketFromPhy
9 MAC_ReceivePhyStatusChangeNotificatio调用mac_gsm.cpp中定义的MacGsmReceivePhyStatusChangeNotification()
10 MAC_Finalize调用GSM的finalize函数
11 MAC_IsWirelessNetwork如果是MAC_PROTOCOL_GSM,那么此函数就返回true
12 MAC_IsOneHopBroadcastNetwork 完
上面就是mac.cpp中使用的有关GSM的函数,我们接下来进入mac_gsm.cpp来深入的看看。
在mac_gsm.cpp中一共就3500行代码,看看我如何将它分解,分而治之
1 MacGsmInit函数定义:
本函数用来初始化MS和BS的MAC层
输入参数为:
1 需要被初始化地结点
2 输入的config文件
函数的行为:
1 先定义一些需要用的变量
2 为MacDataGsm结构体gsm分配内存空间,使用MEM_malloc,这个由QualNet提供的main.h中声明。估计这是封装了普通的内存分配,然后更好的防治内存泄露等问题。
3 调用ctoa( getSimTime( node ),clockStr ); ctoa()和getSimTime()都在QualNet提供的clock.h中声明。还不知道这个的作用
4 然后是打印一则调试信息,这里使用了一个小技巧:通过宏来控制打印不同级别的调试信息:
5 初始化gsm,这里遵守的是何时使用合适初始化
6 初始化随机数,具体的作用现在还不解:
RANDOM_SetSeed()在QualNet提供的Random.cpp中定义
7 初始化wasFound变量,这个变量用来检查后面读入配置文件是否正常返回。
8 读入配置文件并判断是否正常返回,否则抛出异常:
这里IO_ReadString()在QualNet提供的Fileio.h中提供
ERROR_Assert()则在QualNet_error.h中声明。
这种用法在《系统程序员成长计划》有所描述。大概就是说:如果用错误的参数测试,期望assert被触发,但如果assert被触发了,自动程序测试就死掉了,一旦自动测试程序死掉,就无法继续验证下一个assert了。为了解决这个悖论,我从glib里面学了一招,在检查时不用assert,而只是打印出一个警告,代码也很简明,按它的方式,我这样检查:
return_val_if_fail(cursor !=NULL, DLIST_RET_INVALID_PARAMS);
我需要定义一个宏:
#define return_val_if_fail(p,ret) if(!(p)) \
{printf("%s:%d Warning: "#p" failed. \n", __func__,__LINE__);return (ret);}
这样一来,遇到无效参数时,我们可以看到一个警告信息,同时又不会影响自动测试
9 接下来是重点,本函数会根据varStrValue的值,分三种情况进行处理
A GSM-MS
B GSM-BS
C GSM-MSC
我会进一步分析其中的内容,稍后
10 调用MacGsmInitStats( node,nodeInput,interfaceIndex ); 这个函数也在mac_gsm.cpp中定义。
应该是初始化统计信息用的函数,这个函数很短,这里就看一下:
我们接下来针对MS端地MAC层初始化,更进一步的来看一下初始化过程
varStrValue == GSM-MS1 声明一个MacGsmMsInfo的指针 msInfo
2 打印调试信息:Node x: GSM_MS
3 设置结构体MacDataGsm gsm中的一些变量:
gsm->nodeType
gsm->msInfo
为其分配一个MacGsmMsInfo结构体大小的空间,然后初始化为0
msInfo->downlinkControlChannelIndex ;int
msInfo->cellSelectionTimer ;Message指针
4 读入GSM-CONTROL-CHANNEL,根据读入的信息,初始化:
msInfo->numControlChannels ;int
msInfo->controlChannels ;short []
5 初始化
msInfo->numBcchChannels ;int
msInfo->nextBcchChannelToListen ;int
6 初始化,
msInfo->bcchList[] ;GsmBcchListEntry []
7 调用MacGsmMsStartListeningToBcchListChannels( node, gsm );
此函数在mac_gsm.cpp中定义
让MS开始根据配置文件中定的BCCH信道上侦听
8 初始化
msInfo->cellSelectionTimer
通过调用MacGsmStartTimer();
9 初始化
msInfo->isDedicatedChannelAssigned
msInfo->isCellSelected
msInfo->timeSinceCellSelected
msInfo->slotNumber
msInfo->controlFrameNumber
msInfo->frameNumber
10 读PHY-GSM-TX-POWER,
初始化 msInfo->txPower_dbm
11 读PHY-GSM-RX-THRESHOLD
初始化 msInfo->rxLevAccessMin_dbm
12 初始化
msInfo->maxTxPower_dbm
初始化 node->gsmNodeParameters->cellIdentity
node->gsmNodeParameters->lac
原来node.h中还有gsm专用的结构体:gsmNodeParameters,这个结构体在Cellular_gsm.h中定义:
BS端的初始化过程咱略,以后补充
varStrValue == GSM-BS varStrValue == GSM-MSC 函数关系图:今日针对MacGsmInit这个初始化函数,由哪些其他的函数被调用,如下图:
大家可能已经看到上图中函数名前的一些编号,这些编号的索引如下图:
我会把已经分析或者用到过的函数标注出来,这样一是可以知道我们当前的进度,而是可以知道还有哪些没有分析到的地方。
今天就先到这里吧
没有评论:
发表评论