Luat Air720S系列模块_CMUX_使用说明

LINUX下的CMUX使用
1.启动CMUX模式

通常模块发送AT+CMUX命令来激活多路复用,该命令格式为:

命令类型

语法

返回

设置命令

AT+CMUX= [<mode>[,<subset>[,<port_speed>[,<N1>[,<T1>[,<N2>[,<T2>[,<T3>[,<k>]]]]]]]]]

 

OK

查询命令

AT+CMUX?

+CMUX:[<mode>[,<subset>[,<port_speed>[,<N1>[,<T1>[,<N2>[,<T2>[,<T3>[,<k>]]]]]]]]]

 

OK

测试命令

AT+CMUX=?

+CMUX:(<mode>),(<subset>s),( <port_speed>s),(<N1>s),(<T1>s),(<N2>s),(<T2>s),(<T3>s),(<k>s) 

 

OK

OK

 

参数定义:

参数

定义

取值

对取值的说明

复用透传机制

0

基本选择

控制通道的复用器建立的方式

0

仅使用UIH

传输速率,单位为bits/s

1

9600

2

19200

3

38400

4

57600

5

115200

6

230400

7

460800

最大报文大小

1~255

默认:127

接收timer10ms为单位

1-255

默认:10(100 ms)

重传的最大数量

0-100

默认:3

复用器控制通道的响应定时器,以10ms 为单位

2-255

默认:30

唤醒响应定时器,以秒为单位

1-255

默认:10

窗口大小,适用于有错误恢复选项的高级操作

1-7

默认:2


2.CMUX 帧结构 

多路复用有三种操作模式:基本模式、带错误恢复功能的高级模式、不带错误恢复功能的高级模式(具体可以参见GSM 07.10协议)。不同的GPRS模块也支持不同的模式。

 

其中,帧类型可分控制帧和信息帧。

CMUX双方通过控制帧协商用以构建、拆除虚拟链路,控制帧又为以下几种:

SABM:建立DLC

UA:响应SABM帧或DISC

DM:链路未成功建立时,对收到的DISC命令的响应

DISC:通知对端拆除链接,在DLC0发送DISC帧,等于退出MUX功能

 

信息帧:

UIH\UI\I:这三种是信息帧,也就是携带要传输数据的帧。

3.LINUX下的CMUX使用

3.1 挂载USB设备

在可移动设备,勾选QinHeng USB2.0-Serial

然后打开终端控制台,ls /dev/ttyUSB*查看USB设备,如下表示挂载成功。


3.2 CMUX虚拟串口生成

参考用例:https://www.kernel.org/doc/html/latest/driver-api/serial/n_gsm.html


3.2.1 测试实例gsmmux.h源代码

#ifndef _LINUX_GSMMUX_H

#define _LINUX_GSMMUX_H

 

struct gsm_config

{

unsigned int adaption;

unsigned int encapsulation;

unsigned int initiator;

unsigned int t1;

unsigned int t2;

unsigned int t3;

unsigned int n2;

unsigned int mru;

unsigned int mtu;

unsigned int k;

unsigned int i;

unsigned int unused[8]; /* Padding for expansion without

   breaking stuff */

};

 

#define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config)

#define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config)

 

struct gsm_netconfig {

unsigned int adaption;  /* Adaption to use in network mode */

unsigned short protocol;/* Protocol to use - only ETH_P_IP supported */

unsigned short unused2;

char if_name[IFNAMSIZ]; /* interface name format string */

__u8 unused[28];        /* For future use */

};

 

#define GSMIOC_ENABLE_NET      _IOW('G', 2, struct gsm_netconfig)

#define GSMIOC_DISABLE_NET     _IO('G', 3)

 

 

#endif

3.2.2 测试实例cmux.c 源代码

#include <stdlib.h>

#include <stdio.h>

#include <stdarg.h>

#include <string.h>

#include <sys/ioctl.h>

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

/**

* gsmmux.h provides n_gsm line dicipline structures and functions.

* It should be kept in sync with your kernel release.

*/

#include "gsmmux.h"

 

/* n_gsm ioctl */

#ifndef N_GSM0710

# define N_GSM0710 21

#endif

 

/* attach a line discipline ioctl */

#ifndef TIOCSETD

# define TIOCSETD 0x5423

#endif

 

/* serial port of the modem */

//#define SERIAL_PORT "/dev/ttyUSB0"

 

/* line speed */

#define LINE_SPEED B115200

 

/* maximum transfert unit (MTU), value in bytes */

#define MTU 512

 

/**

* whether or not to create virtual TTYs for the multiplex

* 0 : do not create

* 1 : create

*/

#define CREATE_NODES 1

 

/* number of virtual TTYs to create (most modems can handle up to 4) */

#define NUM_NODES 4

 

/* name of the virtual TTYs to create */

#define BASENAME_NODES "/dev/ttyGSM"

 

/* name of the driver, used to get the major number */

#define DRIVER_NAME "gsmtty"

 

/**

* whether or not to print debug messages to stderr

* 0 : debug off

* 1 : debug on

*/

#define DEBUG 1

 

/**

* whether or not to detach the program from the terminal

* 0 : do not daemonize

* 1 : daemonize

*/

#define DAEMONIZE 1

 

 /* size of the reception buffer which gets data from the serial line */

#define SIZE_BUF 256

 

 

/**

* Prints debug messages to stderr if debug is wanted

*/

static void dbg(char *fmt, ...) {

va_list args;

 

if (DEBUG) {

fflush(NULL);

va_start(args, fmt);

vfprintf(stderr, fmt, args);

va_end(args);

fprintf(stderr, "\n");

fflush(NULL);

}

return;

}

 

/**

* Sends an AT command to the specified line and gets its result

* Returns  0 on success

* -1 on failure

*/

int send_at_command(int serial_fd, char *command, int max_retry) {

char buf[SIZE_BUF];

int r;

int retry = 0;

 

do{

/* write the AT command to the serial line */

if (write(serial_fd, command, strlen(command)) <= 0)

err(EXIT_FAILURE, "Cannot write to ttyUSB");

/* wait a bit to allow the modem to rest */

sleep(1);

 

/* read the result of the command from the modem */

memset(buf, 0, sizeof(buf));

r = read(serial_fd, buf, sizeof(buf));

if (r == -1)

{

dbg("read errno %d", errno);

retry++;

dbg("retry write time: %d", retry);

}

/* if there is no result from the modem, return failure */

if (r == 0) {

dbg("%s\t: No response", command);

retry++;

//return -1;

}

if(retry!= 0 && retry >= max_retry)

{

err(EXIT_FAILURE, "Cannot read ttyUSB");

}

else

{

break;

}

}while(1);

 

 

 

/* if we have a result and want debug info, strip CR & LF out from the output */

if (DEBUG) {

int i;

char bufp[SIZE_BUF];

memcpy(bufp, buf, sizeof(buf));

for(i=0; i

if (bufp[i] == '\r' || bufp[i] == '\n')

bufp[i] = ' ';

}

dbg("%s\t: %s", command, bufp);

}

 

/* if the output shows "OK" return success */

if (strstr(buf, "OK\r") != NULL) {

return 0;

}

return -1;

 

}

 

 

/**

* Gets the major number of the driver device

* Returns  the major number on success

* -1 on failure

*/

int get_major(char *driver) {

 

FILE *fp;

char *line = NULL;

size_t len = 0;

ssize_t read;

char device[20];

int major = -1;

/* open /proc/devices file */

if ((fp = fopen("/proc/devices", "r")) == NULL)

err(EXIT_FAILURE, "Cannot open /proc/devices");

 

/* read the file line by line */

while ((major == -1) && (read = getline(&line, &len, fp)) != -1) {

/* if the driver name string is found in the line, try to get the major */

if (strstr(line, driver) != NULL) {

if (sscanf(line,"%d %s\n", &major, device) != 2)

major = -1;

}

/* free the line before getting a new one */

if (line) {

free(line);

line = NULL;

}

 

}

 

/* close /proc/devices file */

fclose(fp);

 

return major;

}

 

/**

* Creates nodes for the virtual TTYs

* Returns the number of nodes created

*/

int make_nodes(int major, char *basename, int number_nodes) {

 

int minor, created = 0;

dev_t device;

char node_name[15];

mode_t oldmask;

 

/* set a new mask to get 666 mode and stores the old one */

oldmask = umask(0);

 

for (minor=1; minor

 

/* append the minor number to the base name */

sprintf(node_name, "%s%d", basename, minor);

/* store a device info with major and minor */

device = makedev(major, minor);

/* create the actual character node */

if (mknod(node_name, S_IFCHR | 0666, device) != 0) {

warn("Cannot create %s", node_name);

} else {

created++;

dbg("Created %s", node_name);

}

 

}

 

/* revert the mask to the old one */

umask(oldmask);

 

return created;

}

 

/**

* Removes previously created TTY nodes

* Returns nothing, it doesn't really matter if it fails

*/

void remove_nodes(char *basename, int number_nodes) {

 

int node;

char node_name[15];

 

for (node=1; node

 

/* append the minor number to the base name */

sprintf(node_name, "%s%d", basename, node);

/* unlink the actual character node */

dbg("Removing %s", node_name);

if (unlink(node_name) == -1)

warn("Cannot remove %s", node_name);

 

}

 

return;

}

 

/**

* Function raised by signal catching

*/

void signal_callback_handler(int signum) {

remove_nodes(BASENAME_NODES, NUM_NODES);

return;

}

int main(int argc, char** argv) {

 

int serial_fd, major;

struct termios tio;

int ldisc = N_GSM0710;

struct gsm_config gsm;

char atcommand[40];

 

char* port;

 

if(argc != 2)

{

printf("usage\n ./cmux /dev/ttyUSBx\n");

exit(-1);

}

port = argv[1];

 

/* print global parameters */

dbg("SERIAL_PORT = %s", port);

 

/* open the serial port */

serial_fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

if (serial_fd == -1)

{

dbg("openport ret = %d\n", errno);

err(EXIT_FAILURE, "Cannot open %s", port);

}

/* get the current attributes of the serial port */

if (tcgetattr(serial_fd, &tio) == -1)

err(EXIT_FAILURE, "Cannot get line attributes");

/* set the new attrbiutes */

tio.c_iflag = 0;

tio.c_oflag = 0;

tio.c_cflag = CS8 | CREAD | CLOCAL;

tio.c_cflag &= (~CRTSCTS);

tio.c_lflag = 0;

tio.c_cc[VMIN] = 1;

tio.c_cc[VTIME] = 0;

/* write the speed of the serial line */

if (cfsetospeed(&tio, LINE_SPEED) < 0 || cfsetispeed(&tio, LINE_SPEED) < 0)

err(EXIT_FAILURE, "Cannot set line speed");

/* write the attributes */

if (tcsetattr(serial_fd, TCSANOW, &tio) == -1)

err(EXIT_FAILURE, "Cannot set line attributes");

 

/**

* Send AT commands to put the modem in CMUX mode.

* This is vendor specific and should be changed

* to fit your modem needs.

* The following matches Quectel M95.

*/

//if (send_at_command(serial_fd, "AT+IFC=2,2\r") == -1)

// errx(EXIT_FAILURE, "AT+IFC=2,2: bad response");

if (send_at_command(serial_fd, "AT+CGMM\r", 3) == -1)

warnx("AT+GMM: bad response");

if (send_at_command(serial_fd, "AT\r", 0) == -1)

warnx("AT: bad response");

sprintf(atcommand, "AT+CMUX=0,0,5\r");

if (send_at_command(serial_fd, atcommand, 0) == -1)

errx(EXIT_FAILURE, "Cannot enable modem CMUX");

 

/* use n_gsm line discipline */

sleep(2);

if (ioctl(serial_fd, TIOCSETD, &ldisc) < 0)

err(EXIT_FAILURE, "Cannot set line dicipline. Is 'n_gsm' module registred?");

 

/* get n_gsm configuration */

if (ioctl(serial_fd, GSMIOC_GETCONF, &gsm) < 0)

err(EXIT_FAILURE, "Cannot get GSM multiplex parameters");

 

/* set and write new attributes */

gsm.initiator = 1;

gsm.encapsulation = 0;

gsm.mru = MTU;

gsm.mtu = MTU;

gsm.t1 = 10;

gsm.n2 = 3;

gsm.t2 = 30;

gsm.t3 = 10;

 

if (ioctl(serial_fd, GSMIOC_SETCONF, &gsm) < 0)

err(EXIT_FAILURE, "Cannot set GSM multiplex parameters");

dbg("Line dicipline set");

/* create the virtual TTYs */

if (CREATE_NODES) {

int created;

if ((major = get_major(DRIVER_NAME)) < 0)

errx(EXIT_FAILURE, "Cannot get major number");

if ((created = make_nodes(major, BASENAME_NODES, NUM_NODES)) < NUM_NODES)

warnx("Cannot create all nodes, only %d/%d have been created.", created, NUM_NODES);

}

 

/* wait to keep the line discipline enabled, wake it up with a signal */

signal(SIGINT, signal_callback_handler);

signal(SIGTERM, signal_callback_handler);

/* detach from the terminal if needed */

if (DAEMONIZE) {

dbg("Going to background");

if (daemon(0,0) != 0)

err(EXIT_FAILURE, "Cannot daemonize");

}

 

dbg("catching sig");

 

pause();

/* remove the created virtual TTYs */

if (CREATE_NODES) {

remove_nodes(BASENAME_NODES, NUM_NODES);

}

 

/* close the serial line */

close(serial_fd);

 

return EXIT_SUCCESS;

}

3.2.3 生成cmux可执行文件

通执行gcc cmux.c -o cmux cmux.c编译成可执行文件

3.2.4 虚拟串口生成

通执行 ./cmux /dev/ttyUSB0 就会创建几个虚拟串口

下图中的ttyGSM1,ttyGSM2,ttyGSM3,ttyGSM4及为虚拟出的串口

4.常见问题处理

4.1 常见AT 错误处理

命令

错误情况

错误原因

处理

AT+CMUX

+CME ERROR: 980

参数输入错误

检查一下输入参数是否正确

4.2 VMware无法识别USB设备的解决方法

我的虚拟机上认不到USB设备,发现是VMware USB Arbitration Service服务没有启动,手动启动VMware USB Arbitration Service服务,再重启VMware,虚拟机就能识别出USB设备了。

具体步骤:
1.点击开始->运行,在对话框中输入"services.msc",确定,打开windows服务管理器。

2.在服务列表中选中"VMware USB Arbitration Service",双击打开属性对话框,再选择"启动",就能启动VMware USB Arbitration Service服务了。

3.关闭VMware软件,并重新打开,启动一下虚拟机。

4.3 虚拟串口生成失败

如下图所示

出现原因是没有选择正确的USB口,我们720SL开发板上的开关要拨到UART2一端,出现上面的错误的原因是因为开关要拨到UART1一端了,把开关拨到UART2一端,重启一下模块,然后在做上述操作。


  • 发表于 2020-05-08 18:03
  • 阅读 ( 3594 )

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
不写代码的码农
沈园园

1 篇文章

作家榜 »

  1. 技术销售Delectate 43 文章
  2. 陈夏 26 文章
  3. 国梁 24 文章
  4. miuser 21 文章
  5. 晨旭 20 文章
  6. 朱天华 19 文章
  7. 金艺 19 文章
  8. 杨奉武 18 文章