关于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_GSM0710int ldisc = N_GSM0710;ioctl(fd, TIOCSETD, &ldisc);//---------------------------在设置串口的线路规程时会执行如下的操作!tty_open(...)tty_lookup_driver(...) //通过主次设备号获取对应的索引get_tty_driver(...) //获取主次设备号在tty_drivers链表中的索引tty_driver_lookup_tty(...) //通过索引获取对应的ttyretval = tty->ops->open(tty, filp); //调用n_gsm.c gsmld_open(...)gsmld_open(...)gsm_alloc_mux(...) //多路复用协议分配一个gsmgsmld_attach_gsm(...) //ld线路规程绑定上面分配的gsmgsm_activate_mux(...) //gsm激活muxgsm_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); //高级模式关闭DLCIif (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_CLOSEDif (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到MODEMdlci->state = DLCI_OPENING; //置dlci状态为DLCI_OPENINGgsm_command(dlci->gsm, dlci->addr, SABM|PF); //发送SABM/PFmod_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_OPENwake_up(&dlci->gsm->event); //同时唤醒dlci->gsm->event事件tty_port_block_til_ready(...)tty_port_raise_dtr_rts(...)gsm_dtr_rts(...)gsmtty_modem_update(...) //猫状态命令MSCgsm_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->writegsmtty_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. 数据通道交互

linux GSM0710(2)相关推荐

  1. UART 和 N_GSM协议 及 linux设备驱动TTY关系

    UART 这个文章写得东西不少 https://blog.csdn.net/daocaokafei/article/details/113664078?utm_medium=distribute.pc ...

  2. 过滤Linux下不同大小的文件,linux查找当前目录下 M/G 大小的文件,删除Linux下指定大小的文件

    过滤Linux下不同大小的文件,linux查找当前目录下 M/G 大小的文件,删除Linux下指定大小的文件 find ./ -type f -size +1G| xargs rm 在清理系统日志文件 ...

  3. linux环境下nacos的安装+启动,阿里云服务器安装nacos

    nacos安装+启动(linux环境): 基础:安装java环境 官网下载压缩包:如 nacos-server-1.2.1.tar.gz 放在自定义目录下 # 解压 tar -xvf nacos-se ...

  4. Alibaba Cloud Linux 2.1903 LTS 64位服务器yum源下载404,Alibaba Cloud Linux 2实例中使用docker-ce、epel等YUM源安装软件失败

    [Alibaba Cloud Linux 2.1903 LTS 64位]服务器yum源下载404 failure: repodata/repomd.xml from docker-ce-stable: ...

  5. Linux下创建硬链接,文件访问为空,提示:xxxx: 符号连接的层数过多

    Linux下创建软链接|硬链接,文件访问为空,提示:x x x: 符号连接的层数过多. 原因:创建符号链接的时候未使用绝对路径,无论是源文件路径还是目标路径,都需要使用绝对路径. 如: ln -s / ...

  6. 作为一个java程序员,常用的linux命令(越攒越多)

    本篇记录我在工作中不断遇到的常用的linux命令,并进行总结,时常更新! 1. 升级服务时先停止服务,然后进行替换 linux中杀进程时候,如果你是知道它所占用的端口号的话,可以通过 netstat ...

  7. 设置linux初始root密码

    简单一步设置linux第一个root密码 sudo passwd root #输入当前账户密码 #输入准备设置的root密码 #确认密码 如下所示:

  8. Linux/docker下oracle开启监听,开启自动启动

    写在前头: 之前呢,使用docker安装了oracle,但它默认是会关闭的.使用了几天以后突然连接异常了,报的问题是oracle监听有问题了,我知道了是oracle服务自动关闭了,监听也跟着关了.所以 ...

  9. Linux通过端口号杀死指定进程

    前言: 我们在服务器上升级项目的时候,需要将原来的项目停止,然后启动新的项目. 这时候我们只知道应用所占的端口号,如何将进程杀死呢? linux中杀进程时候,如果你是知道它所占用的端口号的话,可以通过 ...

  10. 设置腾讯云linux服务器中 MySQL 允许远程访问

    申请了一台linux腾讯云服务器,想要把数据库搭建在上面,本地的Windows直接可以访问 以下就是具体的操作流程,首先你需要安装好一个mysql,安装方法–>mysql安装(Linux) 接着 ...

最新文章

  1. .Lucky后缀勒索病毒数据解密
  2. spring MVC cors跨域实现源码解析
  3. 美国疫情加剧:特朗普检测虚惊一场,女儿伊万卡开始“隔离”,马云捐助百万口罩...
  4. 为什么选用 React 创建混合型移动应用?
  5. Java对【JSON数据的解析】--fastjson解析法
  6. boost::bron_kerbosch_all_cliques用法的测试程序
  7. WC2019 自闭记
  8. python中二进制整数_Python程序查找表示二进制整数的必要位数
  9. distributed crawl
  10. 公开课精华 | 无人驾驶中感知的挑战与尝试
  11. RedHat Linux下获取snmp信息不全的解决办法
  12. 福建建设工程质量安全网
  13. 客户端触发PostBack回发的两种写法
  14. 2022考研计算机-数据库原理教程1-7章
  15. 服务器存储技术千人群为:39472354
  16. python:TypeError: ‘dict_keys‘ object does not support indexing
  17. 解决MATLAB的xlsread函数读取表格失败
  18. 【犯二记录】链表结点换位引发的思维僵化,太可怕,智商 == 0
  19. 三重积分平均值_直角坐标系下的三重积分的几何可视化解释图解高等数学
  20. Python:6-1 水仙数的统计1 (4分)

热门文章

  1. 我的计算机无法远程连接桌面,远程桌面无法连接怎么办?windows 无法使用远程桌面连接的解决方法...
  2. python实现遗传算法
  3. 思科模拟器配置静态路由(下一跳使用IP)
  4. 关于latex第四级标题问题
  5. php apache mpm,RHEL 7 Apache MPM 配置
  6. AD如何修改PCB文件的黑色编辑区
  7. openbmc开发29:开关机管理
  8. echarts模仿excel复合饼图(饼-饼)
  9. 大数据算法-重复率计算 ICardinality
  10. java毕业答辩演讲稿,2019毕业答辩演讲稿大全