CMUX not working with HL7812

Hello,
I am trying to use CMUX on HL7812 gateway.
Sierra Wireless™ HL78xx AT Commands Interface Guide

Firstly I downloaded CMUX application guide from below link.
AirPrime - HL78xx - Setting Up CMUX in a Linux Environment
I am able to create the virtual ports, but not able to communicate to any ports.

I searched online, removed sleep delays, modified CMUX retransmission and transmission settings with different combinations, but it does not work.
I check hardware connection, I tried with on board USB-UART port also RS232 DB9 connection , but it does not work.
I even try to change

  • &D Command: Set Data Terminal Ready (DTR) – (set to 2)

  • &C Command: Set Data Carrier Detect (DCD) – (set to 1)

  • &S Command: DSR Option – (set to 0)

  • IPR Command: Set Fixed Local/DTE Rate (set to 115200)

  • +IFC Command: DTE-DCE Local Flow Control (set to 2,2)

Still I am not able to get CMUX working,
when I virtual port and send AT , it returns " ERROR ERROR " for all four ports.

I need help to remove the road block.

Below is the my latest cmux c code, which is result of all my modifications.

/**
*	Cmux
*	Enables GSM 0710 multiplex using n_gsm
*
*	Copyright (C) 2013 - Rtone - Nicolas Le Manchet <nicolaslm@rtone.fr>
*
*	This program is free software: you can redistribute it and/or modify
*	it under the terms of the GNU General Public License as published by
*	the Free Software Foundation, either version 3 of the License, or
*	(at your option) any later version.
*
*	This program is distributed in the hope that it will be useful,
*	but WITHOUT ANY WARRANTY; without even the implied warranty of
*	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*	GNU General Public License for more details.
*
*	You should have received a copy of the GNU General Public License
*	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <net/if.h>
#include <linux/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <signal.h>
#include <syslog.h>
#include <sys/sysmacros.h>
/** 
*	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

/* line speed */
#define LINE_SPEED	B115200

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

/**
* 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/ttySWI"

/* 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	0

 /* 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) {
	
	char buf[SIZE_BUF];
	int r;

	/* write the AT command to the serial line */
	if (write(serial_fd, command, strlen(command)) <= 0)
		err(EXIT_FAILURE, "Cannot write to serial port");
	
	/* 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)
    {
		err(EXIT_FAILURE, "Cannot read from serial port");
        return -1;
    }
	
	/* if there is no result from the modem, return failure */
	if (r == 0) {
		dbg("%s\t: No response", command);
		return -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<strlen(bufp); 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;		

}

/**
*	Function raised by signal catching
*/
void signal_callback_handler(int signum) {
    dbg("signal_callback_handler called, signal = %d", signum);
    syslog (LOG_INFO, "signal_callback_handler called, signal = %d received", signum);
	return;
}

/**
*	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<number_nodes+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<number_nodes+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;
}

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 serial_port[20]={0};
    int max_port_length = 20;
    int counter;
    if(argc==2) 
    { 
        dbg("\nNumber Of Arguments Passed: %d",argc); 
        dbg("\n----Following Are The Command Line Arguments Passed----"); 
        for(counter=0;counter<argc;counter++) 
            dbg("\nargv[%d]: %s",counter,argv[counter]); 
        if(strstr(argv[1], "/dev/ttyUSB") == NULL)
        {
            dbg("Invalid serial port provided. Use \"/dev/ttyUSBx\"");
            return EXIT_FAILURE;
        }
        if(strlen(argv[1]) >= max_port_length)
        {
            dbg("Serial port length should be less than %d", max_port_length);
            return EXIT_FAILURE;
        }
    }
    else
    {
        dbg("Invalid arguments. Use \"./cmux /dev/ttyUSBx\"");
        return EXIT_FAILURE;
    }
    int at_fd1, at_fd2, at_fd3, at_fd4;
	struct termios at_tio;

	/* print global parameters */
	strncpy(serial_port, argv[1], strlen(argv[1]));
    serial_port[max_port_length-1] = '\0';
    dbg("\nSerial port to open: %s, %s",serial_port, argv[1]); 

	/* open the serial port */
	serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
	if (serial_fd == -1)
		err(EXIT_FAILURE, "Cannot open %s", serial_port);
	
	/* get the current attributes of the serial port */
	if (tcgetattr(serial_fd, &tio) == -1)
		err(EXIT_FAILURE, "Cannot get line attributes");
	
    openlog ("cmux", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
    syslog (LOG_INFO, "Logging from CMUX application");
	/* 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 Sierra Wireless HL77xx and HL78xx device.
	*/
    if (send_at_command(serial_fd, "AT\r") == -1)
	{
        warnx("AT: bad response");             
	}

	if (send_at_command(serial_fd, "ATE0\r") == -1)
		warnx("ATE0: bad response");


    if (send_at_command(serial_fd, "AT+CGMR\r") == -1)
	{
        warnx("AT+GMM: bad response");
	}

	if (send_at_command(serial_fd, "AT+IFC=2,2\r") == -1)
		warnx("AT+IFC=0,0: bad response");	


	if (send_at_command(serial_fd, "AT+IPR=115200\r") == -1)
		warnx("AT+IPR=115200&w: bad response");

	if (send_at_command(serial_fd, "AT&S0\r") == -1)
		warnx("AT&E0&K1&D2: bad response");

	if (send_at_command(serial_fd, "AT&D2\r") == -1)
	warnx("AT&E0&K1&D2: bad response");

	if (send_at_command(serial_fd, "AT&C1\r") == -1)
	warnx("AT&E0&K1&D2: bad response");

	// if (send_at_command(serial_fd, "AT&W0\r") == -1)
	// 	warnx("AT&W0: bad response");

    sprintf(atcommand, "AT+CMUX=0,0,5,%d,10,3,30,10\r", MTU);
    if (send_at_command(serial_fd, atcommand) == -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);
	    }
		else
		{
			// sleep(2);
			/* open the virtual ports */
			at_fd1 = open("/dev/ttySWI1", O_RDWR | O_NOCTTY | O_NDELAY);
			if (at_fd1 == -1)
				err(EXIT_FAILURE, "Cannot open port ttySWI1");

			/* get the current attributes of the serial port */
			if (tcgetattr(at_fd1, &at_tio) == -1)
				err(EXIT_FAILURE, "Cannot get line attributes");

			/* set the new attrbiutes */
			at_tio.c_iflag = 0;
			at_tio.c_oflag = 0;
			at_tio.c_cflag = CS8 | CREAD | CLOCAL;
			at_tio.c_cflag |= CRTSCTS;
			at_tio.c_lflag = 0;
			at_tio.c_cc[VMIN] = 1;
			at_tio.c_cc[VTIME] = 0;

			/* write the speed of the serial line */
			if (cfsetospeed(&at_tio, LINE_SPEED) < 0 || cfsetispeed(&at_tio, LINE_SPEED) < 0)
				err(EXIT_FAILURE, "Cannot set line speed");

			/* write the attributes */
			if (tcsetattr(at_fd1, TCSANOW, &at_tio) == -1)
				err(EXIT_FAILURE, "Cannot set line attributes");

			at_fd2 = open("/dev/ttySWI2", O_RDWR | O_NOCTTY | O_NDELAY);
			if (at_fd2 == -1)
				err(EXIT_FAILURE, "Cannot open port ttySWI2");

			/* get the current attributes of the serial port */
			if (tcgetattr(at_fd2, &at_tio) == -1)
				err(EXIT_FAILURE, "Cannot get line attributes");

			/* set the new attrbiutes */
			at_tio.c_iflag = 0;
			at_tio.c_oflag = 0;
			at_tio.c_cflag = CS8 | CREAD | CLOCAL;
			at_tio.c_cflag |= CRTSCTS;
			at_tio.c_lflag = 0;
			at_tio.c_cc[VMIN] = 1;
			at_tio.c_cc[VTIME] = 0;

			/* write the speed of the serial line */
			if (cfsetospeed(&at_tio, LINE_SPEED) < 0 || cfsetispeed(&at_tio, LINE_SPEED) < 0)
				err(EXIT_FAILURE, "Cannot set line speed");

			/* write the attributes */
			if (tcsetattr(at_fd2, TCSANOW, &at_tio) == -1)
				err(EXIT_FAILURE, "Cannot set line attributes");


			at_fd3 = open("/dev/ttySWI3", O_RDWR | O_NOCTTY | O_NDELAY);
			if (at_fd3 == -1)
				err(EXIT_FAILURE, "Cannot open port ttySWI3");

			/* get the current attributes of the serial port */
			if (tcgetattr(at_fd3, &at_tio) == -1)
				err(EXIT_FAILURE, "Cannot get line attributes");

			/* set the new attrbiutes */
			at_tio.c_iflag = 0;
			at_tio.c_oflag = 0;
			at_tio.c_cflag = CS8 | CREAD | CLOCAL;
			at_tio.c_cflag |= CRTSCTS;
			at_tio.c_lflag = 0;
			at_tio.c_cc[VMIN] = 1;
			at_tio.c_cc[VTIME] = 0;

			/* write the speed of the serial line */
			if (cfsetospeed(&at_tio, LINE_SPEED) < 0 || cfsetispeed(&at_tio, LINE_SPEED) < 0)
				err(EXIT_FAILURE, "Cannot set line speed");

			/* write the attributes */
			if (tcsetattr(at_fd3, TCSANOW, &at_tio) == -1)
				err(EXIT_FAILURE, "Cannot set line attributes");



		    at_fd4 = open("/dev/ttySWI4", O_RDWR | O_NOCTTY | O_NDELAY);
			if (at_fd4 == -1)
				err(EXIT_FAILURE, "Cannot open port ttySWI4");	
			/* get the current attributes of the serial port */
			if (tcgetattr(at_fd4, &at_tio) == -1)
				err(EXIT_FAILURE, "Cannot get line attributes");

			/* set the new attrbiutes */
			at_tio.c_iflag = 0;
			at_tio.c_oflag = 0;
			at_tio.c_cflag = CS8 | CREAD | CLOCAL;
			at_tio.c_cflag |= CRTSCTS;
			at_tio.c_lflag = 0;
			at_tio.c_cc[VMIN] = 1;
			at_tio.c_cc[VTIME] = 0;

			/* write the speed of the serial line */
			if (cfsetospeed(&at_tio, LINE_SPEED) < 0 || cfsetispeed(&at_tio, LINE_SPEED) < 0)
				err(EXIT_FAILURE, "Cannot set line speed");

			/* write the attributes */
			if (tcsetattr(at_fd4, TCSANOW, &at_tio) == -1)
				err(EXIT_FAILURE, "Cannot set line attributes");	

            dbg("Verifying communicaiton on /ttySWIx ports.");
            syslog (LOG_INFO, "Verifying communicaiton on /ttySWIx ports.");
			if (send_at_command(at_fd1, "AT\r") == -1)
				warnx("AT: bad response for ttySWI1");

			if (send_at_command(at_fd2, "ATI\r") == -1)
				warnx("AT: bad response for ttySWI2");

			if (send_at_command(at_fd3, "ATI3\r") == -1)
				warnx("AT: bad response for ttySWI3");

		    if (send_at_command(at_fd4, "AT+CGMR\r") == -1)
				warnx("AT: bad response for ttySWI4");

            syslog (LOG_INFO, "Closing DLC-port handles");

            if (at_fd1 != -1)
                close(at_fd1);
            if (at_fd2 != -1)
                close(at_fd2);
            if (at_fd3 != -1)
                close(at_fd3);
            if (at_fd4 != -1)
                close(at_fd4);
		}
	}

    dbg("User applications can now use /ttySWIx ports.");
    syslog (LOG_INFO, "User applications can now use /ttySWIx ports.");
    
	/* detach from the terminal if needed */
	if (DAEMONIZE) {
		dbg("Going to background");
        syslog (LOG_INFO, "CMUX demonizing. Going to background.");
		if (daemon(1,1) != 0)
			err(EXIT_FAILURE, "Cannot daemonize");
	}

	/* wait to keep the line discipline enabled, wake it up with a signal */
	signal(SIGINT, signal_callback_handler);
	signal(SIGTERM, signal_callback_handler);
    signal(SIGHUP, signal_callback_handler);
    signal(SIGUSR1, signal_callback_handler);
	pause();

    dbg("Closing serial port handle - %s. Can take upto 5 seconds", serial_port);
    syslog (LOG_INFO, "Closing serial port handle. Can take upto 5 seconds. ");
	close(serial_fd);
    sleep(5);
    
    syslog (LOG_INFO, "Preparing %s to send dlcClose command.", serial_port);

	serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
	if (serial_fd == -1)
		err(EXIT_FAILURE, "Cannot open %s", serial_port);
	

	if (tcgetattr(serial_fd, &tio) == -1)
		err(EXIT_FAILURE, "Cannot get line attributes");
	

	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;
	
	
	if (cfsetospeed(&tio, LINE_SPEED) < 0 || cfsetispeed(&tio, LINE_SPEED) < 0)
		err(EXIT_FAILURE, "Cannot set line speed");
	
	
	if (tcsetattr(serial_fd, TCSANOW, &tio) == -1)
		err(EXIT_FAILURE, "Cannot set line attributes");

	const char dlcClose[]= {0xF9, 0x03, 0x53, 0x01, 0xFD, 0xF9};

	char buf[SIZE_BUF];
	int r;
    syslog (LOG_INFO, "Sending dlcClose = %s --> string on %s", dlcClose, serial_port);
	/* write the AT command to the serial line */
	if (write(serial_fd, dlcClose, strlen(dlcClose)) <= 0)
		err(EXIT_FAILURE, "Cannot write to %s", serial_port);
	
	/* 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)
		err(EXIT_FAILURE, "Cannot read %s", serial_port);
	
	/* if there is no result from the modem, return failure */
	if (r == 0) {
		syslog(LOG_INFO,"%s\t: No response", dlcClose);
		return -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<strlen(bufp); i++) {
			if (bufp[i] == '\r' || bufp[i] == '\n')
				bufp[i] = ' ';
		}
		syslog(LOG_INFO,"Printing dlcClose Output: %s\t: %s", dlcClose, bufp);
	}

	/* remove the created virtual TTYs */
    dbg("Removing virtual ports.");
	syslog (LOG_INFO, "Removing virtual ports.");
	if (CREATE_NODES) {
		remove_nodes(BASENAME_NODES, NUM_NODES);
	}
    sleep(2);
	/* close the serial line */
    dbg("Closing serial port handle - %s. Can take upto 5 seconds", serial_port);
    syslog (LOG_INFO, "Closing serial port handle - %s. Can take upto 5 seconds", serial_port);
	close(serial_fd);
    sleep(2);
    syslog (LOG_INFO, "Closing log file.");
    /* close handle to syslog */
    closelog ();
    dbg("CMUX application has exited now!!!");
    syslog (LOG_INFO, "CMUX application has exited now!!!");
	return EXIT_SUCCESS;
}

Sierra Wireless™ HL78xx AT Commands Interface Guide
AT Interface guide which I am using for HL7812

Hi @vaibhav.kambli
In the cmux.c file downloaded from the source, please just add the line as shown below:

image

ttyUSBx is the physical serial AT port of your HL7812 module in the Linux platform

Then try again. Do the issue still happens in this case?

Thanks,

Just for reference the issue of this topic is in all the following FW versions: 3.1 3.2 3.3 4.1 4.2.