D-Modem/slmodemd/modem_test.c
2022-12-01 21:40:04 +08:00

482 lines
11 KiB
C

/*
*
* Copyright (c) 2002, Smart Link Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. Neither the name of the Smart Link Ltd. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
*
* modem_test.c -- modem simulator.
*
* Author: Sasha K (sashak@smlink.com)
*
*
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <modem.h>
#include <modem_debug.h>
#define INFO(fmt,args...) fprintf(stderr, fmt , ##args );
#define ERR(fmt,args...) fprintf(stderr, "error: " fmt , ##args );
#define DBG(fmt,args...) if(modem_debug_level) \
fprintf(stderr, "main: " fmt , ##args );
//#define SIMULATE_RING 1
/* modem init externals : FIXME remove it */
extern int dp_dummy_init(void);
extern void dp_dummy_exit(void);
extern int dp_sinus_init(void);
extern void dp_sinus_exit(void);
extern int prop_dp_init(void);
extern void prop_dp_exit(void);
extern int datafile_load_info(char *name,struct dsp_info *info);
extern int datafile_save_info(char *name,struct dsp_info *info);
/* global config data */
extern unsigned modem_debug_logging;
struct modem_test {
struct modem *modem;
struct modem_test *link;
int in,out;
unsigned int delay;
unsigned int started;
unsigned pty_closed;
unsigned close_count;
};
#define CLOSE_COUNT_MAX 100
/* static data */
static char inbuf[4096];
static char outbuf[4096];
/* 'driver' simulation */
static int modem_test_start (struct modem *m)
{
struct modem_test *t = m->dev_data;
DBG("modem_test_start...\n");
t->delay = 256;
t->started = 1;
memset(outbuf,0,t->delay);
write(t->out,outbuf,t->delay);
return 0;
}
static int modem_test_stop (struct modem *m)
{
struct modem_test *t = m->dev_data;
DBG("modem_test_stop...\n");
t->started = 0;
t->delay = 0;
return 0;
}
static int modem_test_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
{
struct modem_test *t = m->dev_data;
DBG("modem_test_ioctl: cmd %x, arg %lx...\n",cmd,arg);
switch (cmd) {
case MDMCTL_CAPABILITIES:
return -1;
case MDMCTL_HOOKSTATE:
return 0;
case MDMCTL_SPEED:
return 0;
case MDMCTL_GETFMTS:
case MDMCTL_SETFMT:
return 0;
case MDMCTL_SETFRAGMENT:
return 0;
case MDMCTL_SPEAKERVOL:
return 0;
case MDMCTL_CODECTYPE:
return CODEC_UNKNOWN;
case MDMCTL_IODELAY:
return t->delay/2;
default:
break;
}
return -2;
}
struct modem_driver modem_test_driver = {
.name = "modem_test driver",
.start = modem_test_start,
.stop = modem_test_stop,
.ioctl = modem_test_ioctl,
};
/*
*
*/
static volatile sig_atomic_t keep_running = 1;
void mark_termination(int signum)
{
DBG("signal %d: mark termination.\n",signum);
keep_running = 0;
}
static int modem_test_run(struct modem_test *modems)
{
struct timeval tmo;
fd_set rset,eset;
struct termios termios;
struct modem_test *t;
void *in;
int max_fd = 0;
int count, ret;
#ifdef SIMULATE_RING
#define RING_HZ 20
#define RING_ON 1*RING_HZ
#define RING_OFF 4*RING_HZ
unsigned rcount = RING_ON;
#endif
while(keep_running) {
for( t = modems ; t->modem ; t++ )
if(t->modem->event)
modem_event(t->modem);
#ifdef SIMULATE_RING
tmo.tv_sec = 0;
tmo.tv_usec= 1000000/RING_HZ;
#else
tmo.tv_sec = 1;
tmo.tv_usec= 0;
#endif
FD_ZERO(&rset);
FD_ZERO(&eset);
max_fd = 0;
for( t = modems ; t->modem ; t++ ) {
FD_SET(t->in,&rset);
FD_SET(t->in,&eset);
if(t->in > max_fd)
max_fd = t->in;
if( t->pty_closed && t->close_count ) {
if ( !t->started ||
t->close_count++ > CLOSE_COUNT_MAX )
t->close_count = 0;
}
else if (t->modem->xmit.size - t->modem->xmit.count > 0) {
FD_SET(t->modem->pty,&rset);
if(t->modem->pty > max_fd)
max_fd = t->modem->pty;
}
}
ret = select(max_fd + 1,&rset,NULL,&eset,&tmo);
if (ret < 0) {
if (errno == EINTR) {
continue;
}
ERR("select: %s\n",strerror(errno));
return ret;
}
//DBG("select = %d\n",ret);
if (ret == 0) {
#ifdef SIMULATE_RING
if(!modems->modem->started) {
rcount++;
if (rcount <= RING_ON)
modem_ring(modems->modem);
else if (rcount > RING_OFF)
rcount = 0;
}
#endif
continue;
}
for( t = modems ; t->modem ; t++ ) {
if(FD_ISSET(t->in,&eset)) {
DBG("dev exception...\n");
}
}
for( t = modems ; t->modem ; t++ ) {
if(FD_ISSET(t->in,&rset)) {
//DBG("dev read...\n");
count = read(t->in,inbuf,sizeof(inbuf)/2);
if(count < 0) {
ERR("dev read: %s\n",strerror(errno));
return -1;
}
else if (count == 0) {
DBG("dev read = 0\n");
continue;
}
in = inbuf;
if(t->modem->update_delay < 0) {
if ( -t->modem->update_delay >= count/2) {
DBG("change delay -%d...\n", count/2);
t->delay -= count;
t->modem->update_delay += count/2;
continue;
}
DBG("change delay %d...\n", t->modem->update_delay);
in -= t->modem->update_delay*2;
count += t->modem->update_delay*2;
t->delay += t->modem->update_delay*2;
t->modem->update_delay = 0;
}
if(t->started) {
modem_process(t->modem,in,outbuf,count>>1);
}
else {
memset(outbuf,0,count);
/* ring here */
}
count = write(t->out,outbuf,count);
if(count < 0) {
ERR("dev write: %s\n",strerror(errno));
return -1;
}
else if (count == 0) {
DBG("dev write = 0\n");
}
if(t->modem->update_delay > 0) {
DBG("change delay %d...\n", t->modem->update_delay);
memset(outbuf, 0, t->modem->update_delay*2);
count = write(t->out,outbuf,t->modem->update_delay*2);
if(count < 0) {
ERR("dev write: %s\n",strerror(errno));
return -1;
}
t->delay += t->modem->update_delay*2;
t->modem->update_delay = 0;
}
}
}
for( t = modems ; t->modem ; t++ ) {
int pty = t->modem->pty;
if(FD_ISSET(pty,&rset)) {
//DBG("pty read...\n");
/* check termios */
tcgetattr(pty,&termios);
if(memcmp(&termios,&t->modem->termios,
sizeof(termios))) {
DBG("termios changed.\n");
modem_update_termios(t->modem, &termios);
}
/* read data */
count = t->modem->xmit.size - t->modem->xmit.count;
if(count == 0)
continue;
if (count > sizeof(inbuf))
count = sizeof(inbuf);
count = read(pty,inbuf,count);
if(count < 0) {
if(errno == EAGAIN) {
DBG("pty read, errno = EAGAIN\n");
continue;
}
if(errno == EIO) {
if(!t->pty_closed) {
DBG("pty closed.\n");
t->pty_closed = 1;
if(termios.c_cflag&HUPCL)
modem_hangup(t->modem);
}
t->close_count = 1;
//DBG("pty read, errno=EIO\n");
continue;
}
else
ERR("pty read: %s\n",
strerror(errno));
return -1;
}
else if (count == 0) {
DBG("pty read = 0\n");
}
t->pty_closed = 0;
//DBG("pty read %d\n",count);
count = modem_write(t->modem,inbuf,count);
if(count < 0) {
DBG("modem_write failed.\n");
return -1;
}
}
}
}
return 0;
}
static int modem_test_init(struct modem_test *t, const char *name, int in, int out)
{
struct termios termios;
char *pty_name;
int pty;
int ret;
memset(t,0,sizeof(*t));
t->in = in;
t->out = out;
pty = getpt();
if (pty < 0 || grantpt(pty) < 0 || unlockpt(pty) < 0) {
ERR("getpt: %s\n",strerror(errno));
exit(-1);
}
ret = tcgetattr(pty, &termios);
/* non canonical raw tty */
cfmakeraw(&termios);
cfsetispeed(&termios, B115200);
cfsetospeed(&termios, B115200);
ret = tcsetattr(pty, TCSANOW, &termios);
if (ret) {
ERR("tcsetattr: %s\n",strerror(errno));
exit(-1);
}
fcntl(pty,F_SETFL,O_NONBLOCK);
pty_name = ptsname(pty);
t->modem = modem_create(&modem_test_driver,name);
if(!t->modem) {
return -1;
}
t->modem->name = name;
t->modem->pty = pty;
//t->modem->dev = dev;
t->modem->dev_name = name;
t->modem->pty_name = pty_name;
// datafile_load_info(basename(dev_name),&t->modem->dsp_info);
modem_update_termios(t->modem,&termios);
t->modem->dev_data = t;
DBG("created %s: %s\n",t->modem->name,t->modem->pty_name);
return 0;
}
static void modem_test_free(struct modem_test *t)
{
int pty = t->modem->pty;
modem_delete(t->modem);
close(pty);
}
int modem_test()
{
struct modem_test *ma, *mb;
struct modem_test modems[3] = {};
int pipe1[2], pipe2[2];
int ret = 0;
modem_debug_init("test");
memset(modems,0,sizeof(modems));
dp_dummy_init();
dp_sinus_init();
prop_dp_init();
modem_timer_init();
ma = &modems[0];
mb = &modems[1];
if(pipe(pipe1) < 0 || pipe(pipe2) < 0) {
ERR("pipe: %s\n",strerror(errno));
exit(-1);
}
modem_test_init(ma,"modemA",pipe1[0],pipe2[1]);
modem_test_init(mb,"modemB",pipe2[0],pipe1[1]);
ma->link = mb;
mb->link = ma;
signal(SIGINT, mark_termination);
signal(SIGTERM, mark_termination);
/* main loop here */
ret = modem_test_run(modems);
modem_test_free(ma);
modem_test_free(mb);
dp_dummy_exit();
dp_sinus_exit();
prop_dp_exit();
exit(ret);
return 0;
}
int main(int argc, char *argv[])
{
extern void modem_cmdline(int argc, char *argv[]);
modem_debug_level = 1;
modem_cmdline(argc,argv);
return modem_test();
}