freeswitch/libs/spandsp/tests/socket_harness.c

372 lines
11 KiB
C

/*
* SpanDSP - a series of DSP components for telephony
*
* socket_harness.c
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2007 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \page bitstream_tests_page Bitstream tests
\section bitstream_tests_page_sec_1 What does it do?
\section bitstream_tests_page_sec_2 How is it used?
*/
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#define _GNU_SOURCE
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
#include "spandsp.h"
#include "pseudo_terminals.h"
#include "socket_harness.h"
//#define SIMULATE_RING 1
#define CLOSE_COUNT_MAX 100
/* static data */
static int16_t inbuf[4096];
static int16_t outbuf[4096];
static volatile sig_atomic_t keep_running = true;
static void log_signal(int signum)
{
fprintf(stderr, "Signal %d: mark termination.\n", signum);
keep_running = false;
exit(2);
}
/*- End of function --------------------------------------------------------*/
int terminal_write(void *user_data, const char *buf, int len)
{
socket_harness_state_t *s;
s = (socket_harness_state_t *) user_data;
return write(s->pty_fd, buf, len);
}
/*- End of function --------------------------------------------------------*/
int socket_harness_run(socket_harness_state_t *s, int kick)
{
struct timeval tmo;
fd_set rset;
fd_set eset;
struct termios termios;
int max_fd;
int count;
int samples;
int tx_samples;
int ret;
if (kick)
{
samples = 160;
tx_samples = s->tx_callback(s->user_data, outbuf, samples);
if (tx_samples < samples)
memset(&outbuf[tx_samples], 0, (samples - tx_samples)*2);
if ((count = write(s->audio_fd, outbuf, samples*2)) < 0)
{
if (errno != EAGAIN)
{
fprintf(stderr, "Error: audio write: %s\n", strerror(errno));
return -1;
}
/* TODO: */
}
}
while (keep_running)
{
//if (s->modem->event)
// modem_event(s->modem);
#if defined(SIMULATE_RING)
tmo.tv_sec = 0;
tmo.tv_usec= 1000000/RING_HZ;
#else
tmo.tv_sec = 1;
tmo.tv_usec= 0;
#endif
max_fd = 0;
FD_ZERO(&rset);
FD_ZERO(&eset);
FD_SET(s->audio_fd, &rset);
FD_SET(s->audio_fd, &eset);
FD_SET(s->pty_fd, &rset);
FD_SET(s->pty_fd, &eset);
if (s->audio_fd > max_fd)
max_fd = s->audio_fd;
if (s->pty_fd > max_fd)
max_fd = s->pty_fd;
if (s->pty_closed && s->close_count)
{
if (!s->started || s->close_count++ > CLOSE_COUNT_MAX)
s->close_count = 0;
}
else if (s->terminal_free_space_callback(s->user_data))
{
FD_SET(s->pty_fd, &rset);
if (s->pty_fd > max_fd)
max_fd = s->pty_fd;
}
if ((ret = select(max_fd + 1, &rset, NULL, &eset, &tmo)) < 0)
{
if (errno == EINTR)
continue;
fprintf(stderr, "Error: select: %s\n", strerror(errno));
return ret;
}
if (ret == 0)
{
/* Timeout */
#if defined(SIMULATE_RING)
if (!modem->modem->started)
{
rcount++;
if (rcount <= RING_ON)
modem_ring(modem->modem);
else if (rcount > RING_OFF)
rcount = 0;
}
#endif
continue;
}
if (FD_ISSET(s->audio_fd, &rset))
{
if ((count = read(s->audio_fd, inbuf, sizeof(inbuf)/2)) < 0)
{
if (errno != EAGAIN)
{
fprintf(stderr, "Error: audio read: %s\n", strerror(errno));
return -1;
}
count = 0;
}
if (count == 0)
{
fprintf(stderr, "Audio socket closed\n");
return 0;
}
samples = count/2;
usleep(125*samples);
s->rx_callback(s->user_data, inbuf, samples);
tx_samples = s->tx_callback(s->user_data, outbuf, samples);
if (tx_samples < samples)
memset(&outbuf[tx_samples], 0, (samples - tx_samples)*2);
if ((count = write(s->audio_fd, outbuf, samples*2)) < 0)
{
if (errno != EAGAIN)
{
fprintf(stderr, "Error: audio write: %s\n", strerror(errno));
return -1;
}
/* TODO: */
}
if (count != samples*2)
fprintf(stderr, "audio write = %d\n", count);
}
if (FD_ISSET(s->pty_fd, &rset))
{
/* Check termios */
tcgetattr(s->pty_fd, &termios);
if (memcmp(&termios, &s->termios, sizeof(termios)))
s->termios_callback(s->user_data, &termios);
/* Read data */
if ((count = s->terminal_free_space_callback(s->user_data)))
{
if (count > sizeof(inbuf))
count = sizeof(inbuf);
if ((count = read(s->pty_fd, inbuf, count)) < 0)
{
if (errno == EAGAIN)
{
fprintf(stderr, "pty read, errno = EAGAIN\n");
}
else
{
if (errno == EIO)
{
if (!s->pty_closed)
{
fprintf(stderr, "pty closed.\n");
s->pty_closed = 1;
if ((termios.c_cflag & HUPCL))
s->hangup_callback(s->user_data, 0);
}
s->close_count = 1;
}
else
{
fprintf(stderr, "Error: pty read: %s\n", strerror(errno));
return -1;
}
}
}
else
{
if (count == 0)
fprintf(stderr, "pty read = 0\n");
s->pty_closed = false;
s->terminal_callback(s->user_data, (uint8_t *) inbuf, count);
}
}
}
}
return 0;
}
/*- End of function --------------------------------------------------------*/
socket_harness_state_t *socket_harness_init(socket_harness_state_t *s,
const char *socket_name,
const char *tag,
int caller,
put_msg_func_t terminal_callback,
termio_update_func_t termios_callback,
modem_status_func_t hangup_callback,
put_msg_free_space_func_t terminal_free_space_callback,
span_rx_handler_t rx_callback,
span_rx_fillin_handler_t rx_fillin_callback,
span_tx_handler_t tx_callback,
void *user_data)
{
int sockfd;
int listensockfd;
struct sockaddr_un serv_addr;
struct sockaddr_un cli_addr;
socklen_t servlen;
socklen_t clilen;
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "Socket failed - errno = %d\n", errno);
return NULL;
}
if (s == NULL)
{
if ((s = (socket_harness_state_t *) malloc(sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
signal(SIGINT, log_signal);
signal(SIGTERM, log_signal);
s->terminal_callback = terminal_callback;
s->termios_callback = termios_callback;
s->hangup_callback = hangup_callback;
s->terminal_free_space_callback = terminal_free_space_callback;
s->rx_callback = rx_callback;
s->rx_fillin_callback = rx_fillin_callback;
s->tx_callback = tx_callback;
s->user_data = user_data;
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
/* This is a generic Unix domain socket. */
strcpy(serv_addr.sun_path, socket_name);
printf("Creating socket '%s'\n", serv_addr.sun_path);
servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family) + 1;
if (caller)
{
fprintf(stderr, "Connecting to '%s'\n", serv_addr.sun_path);
if (connect(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
{
fprintf(stderr, "Connect failed - errno = %d\n", errno);
exit(2);
}
fprintf(stderr, "Connected to '%s'\n", serv_addr.sun_path);
}
else
{
fprintf(stderr, "Listening to '%s'\n", serv_addr.sun_path);
listensockfd = sockfd;
/* The file may or may not exist. Just try to delete it anyway. */
unlink(serv_addr.sun_path);
if (bind(listensockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
{
fprintf(stderr, "Bind failed - errno = %d\n", errno);
exit(2);
}
listen(listensockfd, 5);
clilen = sizeof(cli_addr);
if ((sockfd = accept(listensockfd, (struct sockaddr *) &cli_addr, &clilen)) < 0)
{
fprintf(stderr, "Accept failed - errno = %d", errno);
exit(2);
}
fprintf(stderr, "Accepted on '%s'\n", serv_addr.sun_path);
}
if (pseudo_terminal_create(&s->modem))
{
fprintf(stderr, "Failed to create pseudo TTY\n");
exit(2);
}
s->audio_fd = sockfd;
s->pty_fd = s->modem.master;
return s;
}
/*- End of function --------------------------------------------------------*/
int socket_harness_release(socket_harness_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
int socket_harness_free(socket_harness_state_t *s)
{
free(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/