PXA1802使用HSIC接口与AP连接,HSIC枚举实现7个虚拟通道ttyUSB0~6,为方便扩展,Marvell在HSIC虚拟通道0上实现了3GPP GSM-07.10 MUX协议,MUX协议提供多路数据流复用底层单一通道,多路数据流互不干扰的多路复用方法。MUX通道主要用于AT命令,通话音量控制,NVM操作等小数据量的通讯,网络PS业务和CP调试LOG都直接使用HSIC虚拟通道。本文主要介绍MUX协议帧结构、数据收发、通道控制等信息。
MUX协议允许多个并发的活动运行在一个底层TE(Terminal Equipment)和MS(Mobile Station)的串行接口上,下图是GSM-07.10文档上描述MUX协议栈的层次结构,显示了多层协议与功能层次结构,multiplexer层提供4个独立的数据流支持,发送数据时并不需要添加额外的数据帧,数据流通道使用DLC(Data Link Connection)表示,每个DLC有独立的编号,在PXA1802驱动中convergence层没有使用,physical层直接使用HSIC通道实现。
TE MS
+--------------------+ +--------------------+
TE Process(4) | | | | | | | | | | MS Process(4)
+----+-----+----+----+ +----+-----+----+----+
Convergence Layer | | | | | | | | | | Convergence Layer
+--------------------+ +--------------------+
Multiplexer Layer | | | | Multiplerxer Layer
+--------------------+ +--------------------+
Physical Layer | | | | Physical Layer
+--------------------+ +--------------------+
| | | |
| | | |
| +---------------------------------+ |
+-------------------------------------+
MUX协议内容的细节可以参考本文结尾的链接,以下只介绍在PXA1802中MUX协议驱动模块实现中需要关注的重点,这些包括会话建立与结束,数据传输与接收以及部分控制帧,标准协议的其他细节在驱动实现时只作了解,另外,驱动也没有完全实现标准协议的所有内容。
MUX帧结构
GSM-07.10支持3种传输模式,项目中只实现最简单的Basic模式,其余高级模式都带有数据保护或错误恢复等功能,是对Basic模式的加强,后面也只在Basic模式上介绍各种操作的实现。协议一共定义了6种类型帧,帧结构都相同,都包含一个开始标识字节和结尾标识字节,一个地址字节,一个控制字节,一到两个长度字节,内容空间和一个校验字节,具体帧结构定义如下。
Flag Addr Ctrl Len Info FCS Flag
+----+----+----+-----+----------+---+---+
| 1 | 1 | 1 | 1~2 | User def | 1 | 1 |
+----+----+----+-----+----------+---+---+
- Flag是帧开始与结尾的标记,占一个字节,是一个常量值,在Basic模式里为0x9F。一次可传输多个帧,上一帧的结尾标记可作为下一帧的开始标记。
- Addr占一个字节,3~8bit表示MUX通道编号,1bit是扩展位,固定为1,2bit标记当前是命令帧还是响应帧。
- Ctrl占一个字节,表示数据包的类型,其中5bit是P/F位,具体含义参考文档5.2.1.3 Setion。
- Len占一到两个字节,1bit是扩展位,如果内容字段长度小于0x7F,Len占一个字节,扩展位为1,内容长度占Len的2~8bit
- FCS 是校验字节,是Flag后面,Info前面内容的校验信息,如果是UI类型的帧,还包含Info部分,校验算法文档有给出
所有在TE和MS间传输的帧都是上面描述的结构,部分控制帧没有Info字段,用Ctrl表示帧类型,Basic模式协议一共支持下面6种类型帧,AT命令等数据内容通常使用UIH类型帧传输。
- Set Asynchronous Balanced Mode (SABM)–建立逻辑通道;
- command Disconnect (DISC) command-取消逻辑通道;
- Unnumbered Acknowledgement (UA) response-响应建立通道;
- Disconnected Mode (DM) response-响应取消逻辑通道;
- Unnumbered information with header check (UIH);
- Unnumbered Information (UI).
DLC的创建与释放流程
应用打开设备准备发送数据前,MUX驱动会阻塞在打开进程中,直到打开成功。MUX驱动在收到打开消息时,会向打开通道发送SABM帧,如果DLC0此时没有被打开,还会先在DLC0上执行一次打开操作,待DLC0打开成功后才允许打开别的DLC通道。一般来说,CP侧收到SABM帧后会响应UA帧表示建立通道,AP收到UA帧再返回打开成功状态。关闭DLC时发送DISC帧,等待响应DM帧时表示DLC关闭成功。理论上打开与关闭是对等的,AP与CP侧都可以主动发起,但项目实现中只有AP可以发起打开与关闭,并且HSIC通道0固定运行MUX协议,不能被关掉。以下给出理论上DLC打开与关闭流程。
Userspace AP Mux driver CP Mux driver
fd_open(i) --------> mux_open(i)
-----SABM(0)----->
<-----UA(0)-------
-----SABM(i)----->
<-----UA(i)-------
<--Opened--
.
write("AT") -------> mux_write()
----UIH("AT")---->
<----UIH("OK")----
"OK" = read()<-------mux_push()
.
fd_close(i) ------> mux_close(i)
-----DISC(i)----->
<-----DM(i)-------
另外,MUX驱动中还支持一个MSC(Modem Status Command), PN(Port Negotiation)等,主要是AP与CP侧MUX驱动同步一些状态,参考协议文档了解具体信息。
DLC数据发送与接收流程
除DLC0外,应用可以使用每个DLC发送数据到CP侧对应的DLC,CP也会将处理结果返回到该DLC,多个应用可以使用不同编号的DLC与CP通讯,由MUX协议保证通道的独立性。实际上层应用开发时具体每个DLC的用途需要参考CP的说明文档。协议有描述DLC通道的优先级,驱动实现时所有发到DLC通道的数据都会填充成MUX帧,按优先级序号链接到优先级链表上,发送线程从链表上按顺序取出MUX帧,发送到HSIC驱动。优先级相同的DLC帧轮流发送。具体实现流程用部分代码表示。
while (count) {
size = min_t(uint, count, dlc->mtu);
skb = alloc_skb(size + MUX_SKB_RESERVE, GFP_KERNEL);
if (!skb)
break;
// 为帧头部预留空间
skb_reserve(skb, SKB_HEAD_RESERVE);
// 填充发送内容数据
memcpy(skb_put(skb, size), buf + sent, size);
// 填充Len字段,长度大于0x7F时占两字节
if (size > 127) {
hdr = (void *) skb_push(skb, 4);
put_unaligned(cpu_to_le16(__len16(size)), (__le16 *) &hdr->len);
} else {
hdr = (void *) skb_push(skb, 3);
hdr->len = __len8(size);
}
hdr->flag = 0x9F;
hdr->addr = dlc->addr;
hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
// fill FCS
crc = skb_put(skb, 1);
*crc = __fcs((void *) hdr);
// fill end tag
flag = skb_put(skb, 1);
*flag = 0x9F;
// tx mux frame queue for deliver
skb_queue_tail(&dlc->mux_tx_q, skb);
// next frame
sent += size;
count -= size;
}
应用发送到DLC的数据都会分成小于mtu字节的帧,填充帧头与校验后挂到发送链表,发送线程根据链表顺序取对应DLC上的帧发送到HSIC驱动,由底层完成实际传输。根据MUX协议,在发送线程中可合并同一DLC上的多个帧一次传输到CP,这部分目前还没有实现,发送线程部分代码如下,HSIC部分的传输涉及到USB协议,会在单独的文章里面讲述。
/* tx all skb by priority list, one skb for each by priority */
mux_for_each:
cur_priority = 0; /* set highest priority first */
list_for_each_entry_safe(dlc, dlc_n, &mux_data->tx_mux_head, mux_list) {
if (skb_queue_empty(&dlc->mux_tx_q))
continue;
/* something about prioity queue
* Prority 0 channel will be sended first(MUX control)
* higher prority channel will deliver all skb than low
* the same prority channel will deliver one skb by turns
*/
if (cur_priority == 0)
cur_priority = dlc->priority;
else if (cur_priority < dlc->priority)
goto mux_for_each;
skb = skb_dequeue(&dlc->mux_tx_q);
ret = ld->send(ld, skb);
if (ret < 0) {
mif_err("ld->send fail (%s, err %d)\n", dlc->name, ret);
dev_kfree_skb_any(skb);
}
}
Links
- [3GPP关于GSM TS 07.10协议的文档] (http://www.3gpp.org/ftp/Specs/archive/07_series/07.10/)
- [关于07.10协议简单介绍] (http://blog.csdn.net/codejoker/article/details/6205898)
- Implementation of the GSM 07.10 Linux Device Driver,Tuukka Karvonen,21.04.2004