linux GSM0710(2)

关于MUX源码分析可参考之前的一篇博客:点击打开链接

名词定义

       MUX: multiplexer protocol   多路复用协议,将原有的一条物理通道虚拟成几条可并发的逻辑通道,同网络协议中的物理链路,逻辑链路有点类似.实现上也类似,将逻辑通道上的数据打包,加上包头包尾,再发到物理通道
      SABM: 即 Set Asynchronous Balanced Mode的缩写 ,叫设置异步平衡模式

GSM 0710六种包

1. SABM 
2. DISC

这两个包是分别来建立、断开逻辑通道
3. UA
4. DM

这两个包是分别来响应正确、错误
5. UIH
6. UI

这两个可以命令包、也可以是状态包,区别是前者不对包数据部分进行校验

GSM 0710三种传输格式
1. Basic

格式:Flags(1B) Address(1B) Control(1B) Length(1~2B) Info(Length指定的可变长度) FCS(1B) Flag(1B)

Flags: 格式固定为0xF9

Address: 逻辑通道编号,类似于MAC地址,Bit1为E/A(表示该地址域是否只有本字节,为1表示只有本字节)、Bit2为C\R(用来指示是命令还是响应),bit3~8为逻辑通道号。

Control:
    Frame Type 1 2 3 4 5 6 7 8 备注
    SABM (Set Asynchronous Balanced Mode) 1 1 1 1 P/F 1 0 0
    UA (Unnumbered Acknowledgement) 1 1 0 0 P/F 1 1 0
    DM (Disconnected Mode) 1 1 1 1 P/F 0 0 0
    DISC (Disconnect) 1 1 0 0 P/F 0 1 0
    UIH(Unnumbered Information with Header check) 1 1 1 1 P/F 1 1 1

    UI (Unnumbered Information) 1 1 0 0 P/F 0 0 0 可选

        P/F是Poll/Final位,表示测试/返回。后续会介绍。比如建立DLC的时候,主机发SABM帧, 并把P置1,如果成功,对方返回UA帧,并把F置1,否则返回DM帧,并把P置1。
Length: 只有在基本模式下,该域才存在。可以是1个字节,也可以是2个字节长度,EA位表示该域是否只有本字节。为1表示本字节就是长度域,为0表示后续还有一个字节。

2. Advanced without error recovery

格式:Flag(1B) Address(1B) Control(1B) Information(长度不确定) FCS(1B) Flags(1B)

Flag:  格式固定为0x7e

3. Advance with error recovery(高级模式错误纠正)

n_gsm.c内部结构体定义:

1. struct gsm_dlci:数据链路链接标识
       每个活动的数据链路都有一个gsm_dlci结构体,它是用来绑定链路层的一个an optional layer tty,为了避免复杂性,当串口复用关闭时要立马释放gsm_dlci。

2. struct gsm_mux:多路复用结构体,通过ldisc可以绑定该线路规程。

基本模式数据帧格式分析(该格式为逻辑通道建立):

gsmld_output: 00000000: f9 03 ef 09 e3 05 07 0d fb f9
f9: 帧头,MUX普通模式
03:  地址域,bit1-EA=1,表示长度就是本字节,如果是0表示还有后续字节
bit2-C/R=1,表示命令/响应,这里是主机到GPRS模块,所以为命令,
bit3~8=0,为逻辑通道号,0表示命令通道
ef:  控制域,表示UIH
09:  信息域, bit1=1 表示信息域长度就是本字节,为0表示后续还有字节
bit2~bit8=4 表示长度为4个字节,
e3 05 07 0d: 数据域,4个字节
fb: CRC校验

f9: 结尾

n_gsm.txt流程

1.创建设备节点
MAJOR=`cat /proc/devices |grep gsmtty | awk '{print $1}` 
	for i in `seq 1 4`; do 
		mknod /dev/ttygsm$i c $MAJOR $i 
	done
2. 打开真实串口
	fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
3. 设置线路规程为N_GSM0710
	int ldisc = N_GSM0710;
	ioctl(fd, TIOCSETD, &ldisc);
	//---------------------------
	在设置串口的线路规程时会执行如下的操作!
	tty_open(...)
		tty_lookup_driver(...) //通过主次设备号获取对应的索引
			get_tty_driver(...) //获取主次设备号在tty_drivers链表中的索引
		tty_driver_lookup_tty(...) //通过索引获取对应的tty
		retval = tty->ops->open(tty, filp); //调用n_gsm.c gsmld_open(...)
		gsmld_open(...)
			gsm_alloc_mux(...) //多路复用协议分配一个gsm
			gsmld_attach_gsm(...) //ld线路规程绑定上面分配的gsm
				gsm_activate_mux(...) //gsm激活mux
					gsm_dlci_alloc(...) //根据地址分配dlci链路控制接口,由于这里的流程是绑定地址0,即命令通道
4. 配置GSM
	/* get n_gsm configuration */
	ioctl(fd, GSMIOC_GETCONF, &c);
	/* we are initiator and need encoding 0 (basic) */
	c.initiator = 1;
	c.encapsulation = 0;
	/* our modem defaults to a maximum size of 127 bytes */
	c.mru = 127;
	c.mtu = 127;
	/* set the new configuration */
	ioctl(fd, GSMIOC_SETCONF, &c);
	----------------------------------
	tty_ioctl(...) 
		tiocsetd(...) 
			gsmld_ioctl(...)
				gsmld_config(...)
					gsm_dlci_begin_close(...) //函数内部dlci->state=DLCI_CLOSED,所以这里直接退出!
					gsm_cleanup_mux(...) //关闭之前的多路复用串口
							//......
							if (dlci) {
								gc = gsm_control_send(gsm, CMD_CLD, NULL, 0); //高级模式关闭DLCI
								if (gc)
									gsm_control_wait(gsm, gc); //发送成功就等待关闭DLCI的返回帧,这里会阻塞等待返回
							}
							//......
					gsm_activate_mux(...) //
						init_timer(&gsm->t2_timer); //初始化定时器
						gsm->t2_timer.function = gsm_control_retransmit; //数据发送失败、或没有响应时的重发回调函数
						gsm->t2_timer.data = (unsigned long)gsm; //定时器参数
						init_waitqueue_head(&gsm->event); //初始化一个等待事件
						spin_lock_init(&gsm->control_lock);
						spin_lock_init(&gsm->tx_lock);

						if (gsm->encoding == 0) //基本模式
							gsm->receive = gsm0_receive;
						else //高级模式
							gsm->receive = gsm1_receive;
						gsm->error = gsm_error;
						dlci = gsm_dlci_alloc(gsm, 0); //分配一个地址为0的DLCI命令数据链路控制接口
							init_timer(&dlci->t1); //初始化t1定时器
							dlci->t1.function = gsm_dlci_t1; //绑定定时器的回调函数
							dlci->t1.data = (unsigned long)dlci; //定时器回调函数的参数
							tty_port_init(&dlci->port);
							dlci->port.ops = &gsm_port_ops;
							dlci->gsm = gsm;
							dlci->addr = addr;
							dlci->adaption = gsm->adaption;
							dlci->state = DLCI_CLOSED; //置DLCI初始化状态为DLCI_CLOSED
							if (addr) //地址不为0表示数据通道
								dlci->data = gsm_dlci_data;
							else //为命令通道
								dlci->data = gsm_dlci_command;
							gsm->dlci[addr] = dlci; //将相应的dlci绑定到addr对应的gsm->dlci[addr]数组中
					gsm_dlci_begin_open(...) //发送SABM/PF,然后直接退出,这里有个疑问,就是M590E老模块没有及时响应,是否是这里没有阻塞或加一个延时时间???
5. 打开虚拟串口open("/dev/ttygsm1") 
	----------------------------------
	tty_open(...)
		gsmtty_install(...)
			gsm_dlci_alloc(...) //分配一个dlci链路控制接口
		retval = tty->ops->open(tty, filp) 
			gsmtty_open(...)
				gsm_dlci_begin_open(...) //链路控制接口与MODEM模块建立,即发送SABM/PF到MODEM
					dlci->state = DLCI_OPENING; //置dlci状态为DLCI_OPENING
					gsm_command(dlci->gsm, dlci->addr, SABM|PF); //发送SABM/PF
					mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); //重启重发定时器
					
				gsmld_receive_buf(...) //接收MODEM响应数据
					gsm->receive(...)=>gsm0_receive(...)/gsm1_receive(...)
						gsm_queue(...) //接收到MODEM响应数据处理
							gsm_dlci_open(...) 
								del_timer(&dlci->t1); //删除重发定时器
								dlci->state = DLCI_OPEN; //置dlci状态为DLCI_OPEN
								wake_up(&dlci->gsm->event); //同时唤醒dlci->gsm->event事件
			
			tty_port_block_til_ready(...)
				tty_port_raise_dtr_rts(...)
					gsm_dtr_rts(...)
						gsmtty_modem_update(...) //猫状态命令MSC
							gsm_control_send(...) //组帧,MSC命令发送
								wait_event(gsm->event, gsm->pending_cmd == NULL); 
								mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); //更新定时器
								gsm_control_transmit(...)
									gsm_data_queue(...)
										__gsm_data_queue(...)
											gsm_data_kick(...)
												gsm->output(...)=>gsmld_output(...)
												
							gsm_control_wait(...)
								wait_event(gsm->event, control->done == 1); //阻塞,等待事件,即等待上面的MSC数据从MODEM返回
								
							gsmld_receive_buf(...) //接收MODEM的MSC响应数据
								gsm->receive(...)=>gsm0_receive(...)/gsm1_receive(...)
									gsm_queue(...) //接收到MODEM的MSC响应数据处理
										dlci->data(dlci, gsm->buf, gsm->len)//回调下面的接口函数
										gsm_dlci_command(...)
											gsm_control_response(...)
												ctrl = gsm->pending_cmd;
												command |= 1;
												if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) {
													del_timer(&gsm->t2_timer); //删除定时器
													gsm->pending_cmd = NULL;
													/* Rejected by the other end */
													if (command == CMD_NSC)
														ctrl->error = -EOPNOTSUPP;
													ctrl->done = 1;
													wake_up(&gsm->event); //到这里,唤醒上面gsm_control_wait中的阻塞事件
												}
6. 发送AT.
tty_write(...)
	do_tty_write(...)=>ld->ops->write
		gsmtty_write(...)
			gsm_dlci_data_kick(...)
				gsm_dlci_data_output(...)
					__gsm_data_queue(...)
						gsm_data_kick(...)
							gsm->output(...)=>gsmld_output(...)
			gsm_dlci_data_sweep(...) //如果缓冲区数据没有传输完,将继续发送,而完成该操作的就是本函数,其实这里采用了递归机制
				gsm_dlci_data_output()
					__gsm_data_queue(...)
						gsm_data_kick(...)
							gsm->output(...)=>gsmld_output(...) 
							//到这里发送完进行判断缓冲区中的数据是否全部传输完,否则将继续调用gsm_dlci_data_sweep(...)

			//接收数据接口
			gsmld_receive_buf(...)
				gsm->receive(...)=>gsm0_receive() //基本模式
					gsm_queue(...)
						dlci->data=>gsm_dlci_data(...) //MODEM返回数据进行处理
							tty_insert_flip_string(...) //MODEM返回的数据传输的tty io层
							tty_flip_buffer_push(port); //通知tty io层,MODEM数据已经到达,tty io层在将数据传送到应用层


交互流程如下:

1. MUX 虚拟地址0(命令通道)通信帧(SABM/PF)
终端发送:gsmld_output: 00000000: f9 03 3f 01 1c f9
终端接收:gsmld_receive: 00000000: f9 03 73 01 d7 f9 
2. MUX 虚拟地址1(数据通道)通信帧(SABM/PF)
终端发送:gsmld_output: 00000000: f9 07 3f 01 de f9 
终端接收:gsmld_receive: 00000000: f9 07 73 01 15 f9

老的模块和新的模块在这一步会不相同,新的模块在SABM/PF协商时可以正常收到数据,而老的模块却会收到多帧M590E上报的MODEM猫状态,如下:
终端接收:gsmld_receive: 00000000: f9 01 ef 0b e3 07 07 0c 01 79 f9  //M590E上报的MODEM状态
f9 07 73 01 15 f9  //虚拟地址1 SABM/PF返回帧,是我们需要的
f9 01 ef 0b e3 07 07 0c 01 79 f9  //M590E上报的MODEM状态
f9 01 ef 0b e3 07 07 06 01 79 f9  //M590E上报的MODEM状态
f9 01 ef 0b e3 07 07 06 01 79 f9  //M590E上报的MODEM状态
f9 01 ef 0b e3 07 07 06 01 79 f9  //M590E上报的MODEM状态
f9 01 ef 0b e3 07 07 06 01 79 f9  //M590E上报的MODEM状态

为了解决老模块问题,修改两点:

① n_gsm.c驱动定时器时间扩大20倍,即原来100ms->现在2s,340ms->6.8s

② 过滤掉MODEM模块上报的猫状态,分别为“07 0c 01”和“07 06 01”

3. 终端设置M590E MODEM的猫状态
终端发送:gsmld_output: 00000000: f9 03 ef 09 e3 05 07 0d fb f9  
终端接收:gsmld_receive: 00000000: f9 01 ef 09 e1 05 07 0d 9a f9

4. 通过MUX虚拟串口1发送AT
终端发送:gsm_data_kick: 00000000: f9 07 ef 03 41 d4 f9 //发送‘A’
  gsm_data_kick: 00000000: f9 07 ef 03 54 d4 f9 //发送‘T’
终端接收:gsmld_receive: 00000000: f9 05 ef 0d 0d 0a 4f 4b 0d 0a 5f f9 //接收”ok“
5. 数据通道交互
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值