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;
}