2011-10-20 16:34:37 +00:00
|
|
|
/*
|
|
|
|
* mISDNv2 CAPI 2.0 daemon
|
|
|
|
*
|
|
|
|
* Written by Karsten Keil <kkeil@linux-pingi.de>
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011 Karsten Keil <kkeil@linux-pingi.de>
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2 as published by the
|
|
|
|
* Free Software Foundation. See the LICENSE file included with
|
|
|
|
* this package for more details.
|
|
|
|
*/
|
|
|
|
|
2011-10-29 13:13:58 +00:00
|
|
|
#include <stdio.h>
|
2011-10-20 16:34:37 +00:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <mISDN/q931.h>
|
|
|
|
#include "m_capi.h"
|
|
|
|
#include "mc_buffer.h"
|
|
|
|
#include "m_capi_sock.h"
|
2011-11-25 15:36:12 +00:00
|
|
|
#include "alaw.h"
|
|
|
|
#ifdef USE_SOFTFAX
|
|
|
|
#include "g3_mh.h"
|
|
|
|
#endif
|
2011-10-28 10:58:42 +00:00
|
|
|
/* should be moved to capi_debug.h */
|
|
|
|
#include <capi_debug.h>
|
|
|
|
|
2011-10-20 16:34:37 +00:00
|
|
|
#ifndef DEF_CONFIG_FILE
|
|
|
|
#define DEF_CONFIG_FILE "/etc/capi20.conf"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
PIT_None = 0,
|
|
|
|
PIT_mISDNmain,
|
|
|
|
PIT_CAPImain,
|
|
|
|
PIT_NewConn,
|
|
|
|
PIT_Application,
|
|
|
|
PIT_Bchannel,
|
|
|
|
} pollInfo_t;
|
|
|
|
|
|
|
|
struct pollInfo {
|
|
|
|
pollInfo_t type;
|
|
|
|
void *data;
|
2011-10-29 13:13:58 +00:00
|
|
|
};
|
2011-10-20 16:34:37 +00:00
|
|
|
|
|
|
|
static struct pollfd *mainpoll;
|
|
|
|
static struct pollInfo *pollinfo;
|
|
|
|
static int mainpoll_max = 0;
|
|
|
|
static int mainpoll_size = 0;
|
|
|
|
#define MAINPOLL_LIMIT 256
|
|
|
|
|
|
|
|
static char def_config[] = DEF_CONFIG_FILE;
|
|
|
|
static char *config_file;
|
|
|
|
static unsigned int debugmask = 0;
|
|
|
|
static int do_daemon = 1;
|
|
|
|
static int mIsock;
|
|
|
|
static int mCsock;
|
|
|
|
static struct pController *mI_Controller;
|
|
|
|
static int mI_count;
|
|
|
|
static FILE *DebugFile = NULL;
|
|
|
|
static char *DebugFileName = NULL;
|
|
|
|
|
2011-11-25 15:36:12 +00:00
|
|
|
static void usage(void)
|
2011-10-20 16:34:37 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr, "Usage: mISDNcapid [OPTIONS]\n");
|
|
|
|
fprintf(stderr, " Options are\n");
|
|
|
|
fprintf(stderr, " -?, --help this help\n");
|
|
|
|
fprintf(stderr, " -c, --config <file> use this config file - default %s\n", def_config);
|
|
|
|
fprintf(stderr, " -d, --debug <level> set debug level\n");
|
|
|
|
fprintf(stderr, " -D, --debug-file <debug file> use debug file (default stdout/stderr)\n");
|
|
|
|
fprintf(stderr, " -f, --foreground run in forground, not as daemon\n");
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
2011-11-25 15:36:12 +00:00
|
|
|
static int opt_parse(int ac, char *av[])
|
2011-10-20 16:34:37 +00:00
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int option_index = 0;
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"help", 0, 0, '?'},
|
|
|
|
{"config", 1, 0, 'c'},
|
|
|
|
{"debug-file", 1, 0, 'D'},
|
|
|
|
{"debug", 1, 0, 'd'},
|
|
|
|
{"foreground", 0, 0, 'f'},
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
c = getopt_long(ac, av, "?c:D:d:f", long_options, &option_index);
|
|
|
|
if (c == -1)
|
|
|
|
break;
|
|
|
|
switch (c) {
|
|
|
|
case 0:
|
|
|
|
fprintf(stderr, "option %s", long_options[option_index].name);
|
|
|
|
if (optarg)
|
|
|
|
fprintf(stderr, " with arg %s", optarg);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
if (optarg)
|
|
|
|
config_file = optarg;
|
|
|
|
else {
|
|
|
|
fprintf(stderr, "option -c but no filename\n");
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
if (optarg)
|
|
|
|
DebugFileName = optarg;
|
|
|
|
else {
|
|
|
|
fprintf(stderr, "option -D but no filename\n");
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
if (optarg) {
|
|
|
|
errno = 0;
|
|
|
|
debugmask = (unsigned int)strtol(optarg, NULL, 0);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "cannot read debuglevel from %s - %s\n", optarg, strerror(errno));
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "option -d but no value for debugmask\n");
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
do_daemon = 0;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
usage();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c = ac - optind;
|
|
|
|
if (c != 0) {
|
|
|
|
fprintf(stderr, "unknown options: %s\n", av[optind]);
|
|
|
|
return -2;
|
2011-10-29 13:13:58 +00:00
|
|
|
}
|
2011-10-20 16:34:37 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_config_file(char *name)
|
|
|
|
{
|
|
|
|
int nr_controller = 0, lnr = 0;
|
|
|
|
int contr = 0, capicontr, ena, n;
|
|
|
|
FILE *f;
|
|
|
|
char line[256], *s;
|
|
|
|
|
|
|
|
f = fopen(name, "r");
|
|
|
|
if (!f) {
|
|
|
|
fprintf(stderr, "cannot open %s - %s\n", name, strerror(errno));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
while ((s = fgets(line, 256, f))) {
|
|
|
|
lnr++;
|
|
|
|
switch (*s) {
|
|
|
|
case '\n':
|
|
|
|
case '#':
|
|
|
|
case ';':
|
|
|
|
case '!':
|
|
|
|
/* comment or empty lines */
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!strncasecmp("mISDN", s, 5)) {
|
|
|
|
n = sscanf(&s[5], "%d %d %d", &contr, &capicontr, &ena);
|
|
|
|
switch (n) {
|
|
|
|
case 0:
|
|
|
|
nr_controller = -1;
|
2011-10-29 13:13:58 +00:00
|
|
|
fprintf(stderr, "error in config file %s:%d:%s\n", name, lnr, line);
|
2011-10-20 16:34:37 +00:00
|
|
|
goto err;
|
|
|
|
case 1:
|
|
|
|
capicontr = contr + 1;
|
|
|
|
case 2:
|
|
|
|
ena = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (contr > mI_count - 1) {
|
|
|
|
fprintf(stderr, "Controller %d not detected - ignored\n", contr + 1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (contr < 0 || contr > 126) {
|
2011-10-29 13:13:58 +00:00
|
|
|
fprintf(stderr, "Invalid controller nr (%d) in config file %s, line %d: %s\n", contr + 1, name,
|
|
|
|
lnr, line);
|
2011-10-20 16:34:37 +00:00
|
|
|
nr_controller = -2;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (capicontr < 1 || capicontr > 127) {
|
2011-10-29 13:13:58 +00:00
|
|
|
fprintf(stderr, "Invalid capi controller nr (%d) in config file %s, line %d: %s\n", capicontr,
|
|
|
|
name, lnr, line);
|
2011-10-20 16:34:37 +00:00
|
|
|
nr_controller = -3;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
mI_Controller[contr].mNr = contr;
|
|
|
|
mI_Controller[contr].profile.ncontroller = capicontr;
|
|
|
|
mI_Controller[contr].enable = ena;
|
|
|
|
nr_controller++;
|
|
|
|
}
|
|
|
|
if (!strncasecmp("debugmask", s, 9)) {
|
|
|
|
debugmask |= (unsigned int)strtol(&s[9], NULL, 0);
|
|
|
|
}
|
|
|
|
/* all other is ignored */
|
|
|
|
if (feof(f))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
fclose(f);
|
|
|
|
return nr_controller;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_mainpoll(int fd, pollInfo_t pit)
|
|
|
|
{
|
|
|
|
struct pollfd *newmp;
|
|
|
|
struct pollInfo *newinfo;
|
|
|
|
int i, n;
|
|
|
|
|
|
|
|
for (i = 0; i < mainpoll_max; i++) {
|
|
|
|
if (mainpoll[i].fd == -1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == mainpoll_max) {
|
|
|
|
if (mainpoll_max < mainpoll_size)
|
|
|
|
mainpoll_max++;
|
|
|
|
else if (mainpoll_size < MAINPOLL_LIMIT) {
|
|
|
|
if (mainpoll_size)
|
|
|
|
n = mainpoll_size * 2;
|
|
|
|
else
|
|
|
|
n = 4;
|
|
|
|
newmp = calloc(n, sizeof(*newmp));
|
|
|
|
if (!newmp) {
|
|
|
|
eprint("no memory for %d mainpoll\n", n);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
newinfo = calloc(n, sizeof(*newinfo));
|
|
|
|
if (!newinfo) {
|
|
|
|
free(newmp);
|
|
|
|
eprint("no memory for %d pollinfo\n", n);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
mainpoll_size = n;
|
|
|
|
if (mainpoll) {
|
|
|
|
memcpy(newmp, mainpoll, mainpoll_size * sizeof(*newmp));
|
|
|
|
free(mainpoll);
|
|
|
|
}
|
|
|
|
if (pollinfo) {
|
|
|
|
memcpy(newinfo, pollinfo, mainpoll_size * sizeof(*newinfo));
|
|
|
|
free(pollinfo);
|
|
|
|
}
|
|
|
|
mainpoll = newmp;
|
|
|
|
pollinfo = newinfo;
|
|
|
|
mainpoll_max++;
|
|
|
|
} else {
|
|
|
|
eprint("mainpoll full %d fds\n", mainpoll_size);
|
2011-10-29 13:13:58 +00:00
|
|
|
return -1;
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
mainpoll[i].fd = fd;
|
|
|
|
pollinfo[i].type = pit;
|
|
|
|
mainpoll[i].events = POLLIN | POLLPRI;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int del_mainpoll(int fd)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < mainpoll_max; i++) {
|
|
|
|
if (mainpoll[i].fd == fd) {
|
|
|
|
mainpoll[i].events = 0;
|
|
|
|
mainpoll[i].revents = 0;
|
|
|
|
mainpoll[i].fd = -1;
|
|
|
|
j = i;
|
2011-10-29 13:13:58 +00:00
|
|
|
switch (pollinfo[i].type) {
|
2011-10-20 16:34:37 +00:00
|
|
|
default:
|
|
|
|
if (pollinfo[i].data)
|
|
|
|
free(pollinfo[i].data);
|
|
|
|
case PIT_Application: /* already freed */
|
|
|
|
case PIT_Bchannel: /* Never freed */
|
|
|
|
pollinfo[i].data = NULL;
|
|
|
|
pollinfo[i].type = PIT_None;
|
|
|
|
break;
|
|
|
|
}
|
2011-10-29 13:13:58 +00:00
|
|
|
while (mainpoll_max && j == (mainpoll_max - 1) && mainpoll[j].fd == -1) {
|
2011-10-20 16:34:37 +00:00
|
|
|
mainpoll_max--;
|
|
|
|
j--;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct pController *get_cController(int cNr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < mI_count; i++) {
|
|
|
|
if (mI_Controller[i].enable && mI_Controller[i].profile.ncontroller == cNr)
|
|
|
|
return &mI_Controller[i];
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct pController *get_mController(int mNr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < mI_count; i++) {
|
|
|
|
if (mI_Controller[i].enable && mI_Controller[i].mNr == mNr)
|
|
|
|
return &mI_Controller[i];
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct BInstance *ControllerSelChannel(struct pController *pc, int nr, int proto)
|
|
|
|
{
|
|
|
|
struct BInstance *bi;
|
|
|
|
int pmask;
|
|
|
|
|
|
|
|
if (nr >= pc->BImax) {
|
|
|
|
wprint("Request for channel number %d but controller %d only has %d channels\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
nr, pc->profile.ncontroller, pc->BImax);
|
2011-10-20 16:34:37 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (ISDN_P_B_START <= proto) {
|
|
|
|
pmask = 1 << (proto & ISDN_P_B_MASK);
|
|
|
|
if (!(pmask & pc->devinfo.Bprotocols)) {
|
|
|
|
wprint("Request for channel number %d on controller %d protocol 0x%02x not supported\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
nr, pc->profile.ncontroller, proto);
|
2011-10-20 16:34:37 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pmask = 1 << proto;
|
|
|
|
if (!(pmask & pc->devinfo.Dprotocols)) {
|
|
|
|
wprint("Request for channel number %d on controller %d protocol 0x%02x not supported\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
nr, pc->profile.ncontroller, proto);
|
2011-10-20 16:34:37 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bi = pc->BInstances + nr;
|
|
|
|
if (bi->usecnt) {
|
|
|
|
/* for now only one user allowed - this is not sufficient for X25 */
|
2011-10-29 13:13:58 +00:00
|
|
|
wprint("Request for channel number %d on controller %d but channel already in use\n", nr, pc->profile.ncontroller);
|
2011-10-20 16:34:37 +00:00
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
bi->usecnt++;
|
|
|
|
bi->proto = proto;
|
|
|
|
}
|
|
|
|
return bi;
|
|
|
|
}
|
|
|
|
|
2011-11-25 15:36:12 +00:00
|
|
|
static int recvBchannel(struct BInstance *);
|
|
|
|
|
|
|
|
static void *BCthread(void *arg)
|
|
|
|
{
|
|
|
|
struct BInstance *bi = arg;
|
|
|
|
unsigned char cmd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
bi->running = 1;
|
|
|
|
while (bi->running) {
|
|
|
|
ret = poll(bi->pfd, 2, -1);
|
|
|
|
if (ret < 0) {
|
|
|
|
wprint("Bchannel%d Error on poll - %s\n", bi->nr, strerror(errno));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (bi->pfd[0].revents & POLLIN) {
|
|
|
|
ret = recvBchannel(bi);
|
|
|
|
} else if (bi->pfd[1].revents & POLLIN) {
|
|
|
|
ret = read(bi->pfd[1].fd, &cmd, 1);
|
|
|
|
if (cmd == 42) {
|
|
|
|
bi->running = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
wprint("Bchannel%d no POLLIN event\n", bi->nr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CreateBchannelThread(struct BInstance *bi)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = pipe(bi->cpipe);
|
|
|
|
if (ret) {
|
|
|
|
eprint("error - %s\n", strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = fcntl(bi->cpipe[0], F_SETFL, O_NONBLOCK);
|
|
|
|
if (ret) {
|
|
|
|
eprint("error - %s\n", strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
ret = fcntl(bi->cpipe[1], F_SETFL, O_NONBLOCK);
|
|
|
|
if (ret) {
|
|
|
|
eprint("error - %s\n", strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
bi->pfd[0].fd = bi->fd;
|
|
|
|
bi->pfd[0].events = POLLIN | POLLPRI;
|
|
|
|
bi->pfd[1].fd = bi->cpipe[0];
|
|
|
|
bi->pfd[1].events = POLLIN | POLLPRI;
|
|
|
|
ret = pthread_create(&bi->tid, NULL, BCthread, bi);
|
|
|
|
if (ret) {
|
|
|
|
eprint("Cannot create thread error - %s\n", strerror(errno));
|
|
|
|
close(bi->cpipe[0]);
|
|
|
|
close(bi->cpipe[1]);
|
|
|
|
bi->cpipe[0] = -1;
|
|
|
|
bi->cpipe[1] = -1;
|
|
|
|
bi->pfd[0].fd = -1;
|
|
|
|
bi->pfd[1].fd = -1;
|
|
|
|
} else
|
|
|
|
iprint("Created Bchannel tread %d\n", (int)bi->tid);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int StopBchannelThread(struct BInstance *bi)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
unsigned char cmd;
|
|
|
|
|
|
|
|
if (bi->running) {
|
|
|
|
cmd = 42;
|
|
|
|
if (bi->waiting)
|
|
|
|
sem_post(&bi->wait);
|
|
|
|
write(bi->cpipe[1], &cmd, 1);
|
|
|
|
ret = pthread_join(bi->tid, NULL);
|
|
|
|
iprint("Thread %d joined\n", (int)bi->tid);
|
|
|
|
close(bi->cpipe[0]);
|
|
|
|
close(bi->cpipe[1]);
|
|
|
|
bi->pfd[0].fd = -1;
|
|
|
|
bi->pfd[1].fd = -1;
|
|
|
|
bi->cpipe[0] = -1;
|
|
|
|
bi->cpipe[1] = -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dummy_btrans(struct BInstance *bi, struct mc_buf *mc)
|
|
|
|
{
|
|
|
|
struct mISDNhead *hh = (struct mISDNhead *)mc->rb;
|
|
|
|
|
|
|
|
wprint("Controller%d ch%d: Got %s id %x - but %s called\n", bi->pc->profile.ncontroller, bi->nr,
|
|
|
|
_mi_msg_type2str(hh->prim), hh->id, __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int OpenBInstance(struct BInstance *bi, struct lPLCI *lp, enum BType btype)
|
2011-10-20 16:34:37 +00:00
|
|
|
{
|
2011-10-29 13:13:58 +00:00
|
|
|
int sk;
|
|
|
|
int ret;
|
|
|
|
struct sockaddr_mISDN addr;
|
2011-10-20 16:34:37 +00:00
|
|
|
|
|
|
|
sk = socket(PF_ISDN, SOCK_DGRAM, bi->proto);
|
|
|
|
if (sk < 0) {
|
|
|
|
wprint("Cannot open socket for BInstance %d on controller %d protocol 0x%02x - %s\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
bi->nr, bi->pc->profile.ncontroller, bi->proto, strerror(errno));
|
2011-10-20 16:34:37 +00:00
|
|
|
return -errno;
|
2011-10-29 13:13:58 +00:00
|
|
|
}
|
2011-10-20 16:34:37 +00:00
|
|
|
|
|
|
|
ret = fcntl(sk, F_SETFL, O_NONBLOCK);
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
wprint("fcntl error %s\n", strerror(errno));
|
|
|
|
close(sk);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-11-25 15:36:12 +00:00
|
|
|
switch (btype) {
|
|
|
|
case BType_Direct:
|
|
|
|
bi->from_down = recvBdirect;
|
|
|
|
bi->from_up = ncciB3Data;
|
|
|
|
break;
|
|
|
|
#ifdef USE_SOFTFAX
|
|
|
|
case BType_Fax:
|
|
|
|
bi->from_down = FaxRecvBData;
|
|
|
|
bi->from_up = FaxB3Message;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
eprint("Error unnkown BType %d\n", btype);
|
|
|
|
close(sk);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
bi->type = btype;
|
|
|
|
|
2011-10-20 16:34:37 +00:00
|
|
|
addr.family = AF_ISDN;
|
|
|
|
addr.dev = bi->pc->mNr;
|
|
|
|
addr.channel = bi->nr;
|
|
|
|
|
2011-10-29 13:13:58 +00:00
|
|
|
ret = bind(sk, (struct sockaddr *)&addr, sizeof(addr));
|
2011-10-20 16:34:37 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
wprint("Cannot bind socket for BInstance %d on controller %d (mISDN nr %d) protocol 0x%02x - %s\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
bi->nr, bi->pc->profile.ncontroller, bi->pc->mNr, bi->proto, strerror(errno));
|
2011-10-20 16:34:37 +00:00
|
|
|
close(sk);
|
2011-11-25 15:36:12 +00:00
|
|
|
bi->from_down = dummy_btrans;
|
|
|
|
bi->from_up = dummy_btrans;
|
2011-10-20 16:34:37 +00:00
|
|
|
} else {
|
|
|
|
bi->fd = sk;
|
2011-11-25 15:36:12 +00:00
|
|
|
bi->lp = lp;
|
|
|
|
if (btype == BType_Direct) {
|
|
|
|
ret = add_mainpoll(sk, PIT_Bchannel);
|
|
|
|
if (ret < 0) {
|
|
|
|
eprint("Error while adding mIsock to mainpoll (mainpoll_max %d)\n", mainpoll_max);
|
|
|
|
close(sk);
|
|
|
|
bi->fd = -1;
|
|
|
|
bi->lp = NULL;
|
|
|
|
} else {
|
|
|
|
dprint(MIDEBUG_CONTROLLER, "Controller%d: Bchannel %d socket %d added to poll idx %d\n",
|
|
|
|
bi->pc->profile.ncontroller, bi->nr, sk, ret);
|
|
|
|
pollinfo[ret].data = bi;
|
|
|
|
ret = 0;
|
|
|
|
bi->UpId = 0;
|
|
|
|
bi->DownId = 0;
|
|
|
|
}
|
2011-10-29 13:13:58 +00:00
|
|
|
} else {
|
2011-11-25 15:36:12 +00:00
|
|
|
ret = CreateBchannelThread(bi);
|
|
|
|
if (ret < 0) {
|
|
|
|
eprint("Error while creating B-channel thread)\n");
|
|
|
|
close(sk);
|
|
|
|
bi->fd = -1;
|
|
|
|
bi->lp = NULL;
|
|
|
|
} else {
|
|
|
|
ret = 0;
|
|
|
|
bi->UpId = 0;
|
|
|
|
bi->DownId = 0;
|
|
|
|
}
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-29 13:13:58 +00:00
|
|
|
int CloseBInstance(struct BInstance *bi)
|
|
|
|
{
|
2011-10-20 16:34:37 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (bi->usecnt) {
|
2011-11-25 15:36:12 +00:00
|
|
|
switch (bi->type) {
|
|
|
|
case BType_Direct:
|
|
|
|
if (bi->fd >= 0)
|
|
|
|
del_mainpoll(bi->fd);
|
|
|
|
break;
|
|
|
|
#ifdef USE_SOFTFAX
|
|
|
|
case BType_Fax:
|
|
|
|
StopBchannelThread(bi);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
2011-11-25 15:36:12 +00:00
|
|
|
if (bi->fd >= 0)
|
|
|
|
close(bi->fd);
|
2011-10-20 16:34:37 +00:00
|
|
|
bi->fd = -1;
|
|
|
|
bi->proto = ISDN_P_NONE;
|
|
|
|
bi->usecnt--;
|
2011-11-25 15:36:12 +00:00
|
|
|
if (bi->b3data && bi->lp)
|
|
|
|
B3ReleaseLink(bi->lp, bi);
|
|
|
|
bi->b3data = NULL;
|
2011-10-20 16:34:37 +00:00
|
|
|
bi->lp = NULL;
|
2011-11-25 15:36:12 +00:00
|
|
|
bi->type = BType_None;
|
2011-10-29 13:13:58 +00:00
|
|
|
bi->from_down = dummy_btrans;
|
2011-11-25 15:36:12 +00:00
|
|
|
bi->from_up = dummy_btrans;
|
2011-10-20 16:34:37 +00:00
|
|
|
} else {
|
2011-10-29 13:13:58 +00:00
|
|
|
wprint("BInstance %d not active\n", bi->nr);
|
|
|
|
ret = -1;
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2011-11-25 15:36:12 +00:00
|
|
|
static int recvBchannel(struct BInstance *bi)
|
2011-10-20 16:34:37 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct mc_buf *mc;
|
|
|
|
|
|
|
|
mc = alloc_mc_buf();
|
|
|
|
if (!mc)
|
|
|
|
return -ENOMEM;
|
|
|
|
if (!bi)
|
|
|
|
return -EINVAL;
|
|
|
|
ret = recv(bi->fd, mc->rb, MC_RB_SIZE, MSG_DONTWAIT);
|
|
|
|
if (ret < 0) {
|
|
|
|
wprint("Error on reading from %d errno %d - %s\n", bi->fd, errno, strerror(errno));
|
|
|
|
ret = -errno;
|
|
|
|
} else if (ret == 0) {
|
|
|
|
/* closed */
|
|
|
|
ret = -ECONNABORTED;
|
|
|
|
} else if (ret < 8) {
|
|
|
|
eprint("Short message read len %d (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
ret, mc->rb[0], mc->rb[1], mc->rb[2], mc->rb[3], mc->rb[4], mc->rb[5], mc->rb[6], mc->rb[7]);
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = -EBADMSG;
|
|
|
|
} else if (ret == MC_RB_SIZE) {
|
|
|
|
eprint("Message too big %d (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
ret, mc->rb[0], mc->rb[1], mc->rb[2], mc->rb[3], mc->rb[4], mc->rb[5], mc->rb[6], mc->rb[7]);
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = -EMSGSIZE;
|
|
|
|
}
|
2011-10-29 13:13:58 +00:00
|
|
|
if (ret > 0) {
|
|
|
|
mc->len = ret;
|
|
|
|
ret = bi->from_down(bi, mc);
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
2011-10-29 13:13:58 +00:00
|
|
|
|
|
|
|
if (ret != 0) /* if message is not queued or freed */
|
2011-10-20 16:34:37 +00:00
|
|
|
free_mc_buf(mc);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ReleaseBchannel(int idx)
|
|
|
|
{
|
|
|
|
struct BInstance *bi;
|
|
|
|
|
|
|
|
bi = pollinfo[idx].data;
|
|
|
|
if (!bi)
|
|
|
|
return -1;
|
|
|
|
del_mainpoll(bi->fd);
|
|
|
|
close(bi->fd);
|
|
|
|
bi->fd = -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l3_callback(struct mlayer3 *l3, unsigned int cmd, unsigned int pid, struct l3_msg *l3m)
|
|
|
|
{
|
|
|
|
struct pController *pc = l3->priv;
|
|
|
|
struct mPLCI *plci;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
dprint(MIDEBUG_CONTROLLER, "Controller %d - got %s (%x) from layer3 pid(%x) msg(%p)\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
pc->profile.ncontroller, _mi_msg_type2str(cmd), cmd, pid, l3m);
|
2011-10-20 16:34:37 +00:00
|
|
|
|
|
|
|
plci = getPLCI4pid(pc, pid);
|
|
|
|
|
2011-10-29 13:13:58 +00:00
|
|
|
switch (cmd) {
|
2011-10-20 16:34:37 +00:00
|
|
|
case MT_SETUP:
|
|
|
|
if (plci) {
|
|
|
|
iprint("Controller %d - got %s but pid(%x) already in use\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
pc->profile.ncontroller, _mi_msg_type2str(cmd), pid);
|
2011-10-20 16:34:37 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
plci = new_mPLCI(pc, pid, NULL);
|
|
|
|
if (!plci) {
|
|
|
|
wprint("Controller %d - got %s but could not allocate new PLCI\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
pc->profile.ncontroller, _mi_msg_type2str(cmd));
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MT_SETUP_ACKNOWLEDGE:
|
|
|
|
case MT_CALL_PROCEEDING:
|
|
|
|
case MT_ALERTING:
|
|
|
|
case MT_PROGRESS:
|
|
|
|
case MT_CONNECT:
|
|
|
|
case MT_CONNECT_ACKNOWLEDGE:
|
|
|
|
case MT_DISCONNECT:
|
|
|
|
case MT_RELEASE:
|
|
|
|
case MT_RELEASE_COMPLETE:
|
|
|
|
case MT_HOLD:
|
|
|
|
case MT_HOLD_ACKNOWLEDGE:
|
|
|
|
case MT_HOLD_REJECT:
|
|
|
|
case MT_RETRIEVE:
|
|
|
|
case MT_RETRIEVE_ACKNOWLEDGE:
|
|
|
|
case MT_RETRIEVE_REJECT:
|
|
|
|
case MT_SUSPEND_ACKNOWLEDGE:
|
|
|
|
case MT_SUSPEND_REJECT:
|
|
|
|
case MT_RESUME_ACKNOWLEDGE:
|
|
|
|
case MT_RESUME_REJECT:
|
|
|
|
case MT_NOTIFY:
|
|
|
|
if (!plci)
|
2011-10-29 13:13:58 +00:00
|
|
|
wprint("Controller %d - got %s but no PLCI found\n", pc->profile.ncontroller, _mi_msg_type2str(cmd));
|
2011-10-20 16:34:37 +00:00
|
|
|
break;
|
|
|
|
case MT_FREE:
|
|
|
|
if (!plci)
|
2011-10-29 13:13:58 +00:00
|
|
|
wprint("Controller %d - got %s but no PLCI found\n", pc->profile.ncontroller, _mi_msg_type2str(cmd));
|
2011-10-20 16:34:37 +00:00
|
|
|
else
|
|
|
|
plci->pid = MISDN_PID_NONE;
|
|
|
|
break;
|
|
|
|
case MPH_ACTIVATE_IND:
|
|
|
|
case MT_L2ESTABLISH:
|
|
|
|
case MT_L2RELEASE:
|
|
|
|
case MT_L2IDLE:
|
|
|
|
break;
|
|
|
|
case MT_TIMEOUT:
|
|
|
|
iprint("Controller %d - got %s from layer3 pid(%x) msg(%p) plci(%04x)\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
pc->profile.ncontroller, _mi_msg_type2str(cmd), pid, l3m, plci ? plci->plci : 0xffff);
|
2011-10-20 16:34:37 +00:00
|
|
|
break;
|
2011-10-29 13:13:58 +00:00
|
|
|
case MT_ERROR:
|
2011-10-20 16:34:37 +00:00
|
|
|
wprint("Controller %d - got %s from layer3 pid(%x) msg(%p) plci(%04x)\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
pc->profile.ncontroller, _mi_msg_type2str(cmd), pid, l3m, plci ? plci->plci : 0xffff);
|
2011-10-20 16:34:37 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
wprint("Controller %d - got %s (%x) from layer3 pid(%x) msg(%p) plci(%04x) - not handled\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
pc->profile.ncontroller, _mi_msg_type2str(cmd), cmd, pid, l3m, plci ? plci->plci : 0xffff);
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
if (!ret && plci)
|
|
|
|
ret = plci_l3l4(plci, cmd, l3m);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-11-25 15:36:12 +00:00
|
|
|
int OpenLayer3(struct pController *pc)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!pc->l3) {
|
|
|
|
pc->l3 = open_layer3(pc->mNr, pc->L3Proto, pc->L3Flags, l3_callback, pc);
|
|
|
|
if (!pc->l3) {
|
|
|
|
eprint("Cannot open L3 for controller %d L3 protocol %x L3 flags %x\n", pc->mNr, pc->L3Proto,
|
|
|
|
pc->L3Flags);
|
|
|
|
ret = -EINVAL;
|
|
|
|
} else
|
|
|
|
dprint(MIDEBUG_CONTROLLER, "Controller %d l3 open for protocol %x L3 flags %x\n", pc->mNr,
|
|
|
|
pc->L3Proto, pc->L3Flags);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-20 16:34:37 +00:00
|
|
|
int ListenController(struct pController *pc)
|
|
|
|
{
|
|
|
|
struct lController *lc;
|
|
|
|
uint32_t InfoMask = 0, CIPMask = 0, CIPMask2 = 0;
|
2011-11-25 15:36:12 +00:00
|
|
|
int ret = 0;
|
2011-10-20 16:34:37 +00:00
|
|
|
|
|
|
|
lc = pc->lClist;
|
|
|
|
while (lc) {
|
|
|
|
InfoMask |= lc->InfoMask;
|
|
|
|
CIPMask |= lc->CIPmask;
|
|
|
|
CIPMask2 |= lc->CIPmask2;
|
|
|
|
lc = lc->nextA;
|
|
|
|
}
|
|
|
|
dprint(MIDEBUG_CONTROLLER, "Controller %d change InfoMask %08x -> %08x\n", pc->profile.ncontroller, pc->InfoMask, InfoMask);
|
|
|
|
dprint(MIDEBUG_CONTROLLER, "Controller %d change CIPMask %08x -> %08x\n", pc->profile.ncontroller, pc->CIPmask, CIPMask);
|
|
|
|
dprint(MIDEBUG_CONTROLLER, "Controller %d change CIPMask2 %08x -> %08x\n", pc->profile.ncontroller, pc->CIPmask2, CIPMask2);
|
|
|
|
pc->InfoMask = InfoMask;
|
|
|
|
pc->CIPmask = CIPMask;
|
|
|
|
pc->CIPmask2 = CIPMask2;
|
2011-11-25 15:36:12 +00:00
|
|
|
if ((pc->CIPmask || pc->InfoMask) && !pc->l3) {
|
|
|
|
ret = OpenLayer3(pc);
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
2011-11-25 15:36:12 +00:00
|
|
|
return ret;
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
|
2011-10-28 10:58:42 +00:00
|
|
|
void capi_dump_shared(void);
|
|
|
|
|
2011-10-20 16:34:37 +00:00
|
|
|
static void get_profile(int fd, struct mc_buf *mc)
|
|
|
|
{
|
|
|
|
int ret, cnt, i, contr;
|
|
|
|
struct pController *pc;
|
|
|
|
|
|
|
|
contr = CAPIMSG_U16(mc->rb, 8);
|
|
|
|
memset(&mc->rb[8], 0, 66);
|
|
|
|
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
|
|
|
|
CAPIMSG_SETLEN(mc->rb, 74);
|
|
|
|
if (mc->len < 10 || contr < 0) {
|
|
|
|
capimsg_setu16(mc->rb, 8, MIC_INFO_CODING_ERROR);
|
|
|
|
} else if (contr == 0) {
|
|
|
|
cnt = 0;
|
|
|
|
for (i = 0; i < mI_count; i++) {
|
|
|
|
if (mI_Controller[i].enable)
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
capimsg_setu16(mc->rb, 8, CapiNoError);
|
|
|
|
capimsg_setu16(mc->rb, 10, cnt);
|
|
|
|
} else {
|
|
|
|
pc = get_cController(contr);
|
|
|
|
if (!pc) {
|
2011-10-29 13:13:58 +00:00
|
|
|
capimsg_setu16(mc->rb, 8, 0x2002); /* Illegal controller */
|
2011-10-20 16:34:37 +00:00
|
|
|
} else {
|
|
|
|
capimsg_setu16(mc->rb, 8, CapiNoError);
|
|
|
|
capimsg_setu16(mc->rb, 10, contr);
|
|
|
|
capimsg_setu16(mc->rb, 12, pc->profile.nbchannel);
|
|
|
|
capimsg_setu32(mc->rb, 14, pc->profile.goptions);
|
|
|
|
capimsg_setu32(mc->rb, 18, pc->profile.support1);
|
|
|
|
capimsg_setu32(mc->rb, 22, pc->profile.support2);
|
|
|
|
capimsg_setu32(mc->rb, 26, pc->profile.support3);
|
|
|
|
/* TODO reserved, manu */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = send(fd, mc->rb, 74, 0);
|
|
|
|
if (ret != 74)
|
|
|
|
eprint("error send %d/%d - %s\n", ret, 74, strerror(errno));
|
2011-10-28 10:58:42 +00:00
|
|
|
capi_dump_shared();
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void mIcapi_register(int fd, int idx, struct mc_buf *mc)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
uint16_t aid;
|
|
|
|
struct mApplication *appl;
|
|
|
|
uint32_t b3c, b3b, b3s;
|
|
|
|
|
|
|
|
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
|
|
|
|
aid = CAPIMSG_APPID(mc->rb);
|
|
|
|
if (mc->len == 20) {
|
|
|
|
iprint("register application id %d fd=%d\n", aid, fd);
|
|
|
|
b3c = CAPIMSG_U32(mc->rb, 8);
|
|
|
|
b3b = CAPIMSG_U32(mc->rb, 12);
|
|
|
|
b3s = CAPIMSG_U32(mc->rb, 16);
|
|
|
|
appl = RegisterApplication(aid, b3c, b3b, b3s);
|
|
|
|
if (appl) {
|
|
|
|
capimsg_setu16(mc->rb, 8, CapiNoError);
|
|
|
|
pollinfo[idx].type = PIT_Application;
|
|
|
|
pollinfo[idx].data = appl;
|
|
|
|
appl->fd = fd;
|
|
|
|
} else {
|
|
|
|
eprint("register application id %d fd %d failed\n", aid, fd);
|
|
|
|
capimsg_setu16(mc->rb, 8, CapiMsgOSResourceErr);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
capimsg_setu16(mc->rb, 8, MIC_INFO_CODING_ERROR);
|
|
|
|
ret = send(fd, mc->rb, 10, 0);
|
|
|
|
if (ret != 10)
|
|
|
|
eprint("error send %d/%d - %s\n", ret, 10, strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mIcapi_release(int fd, struct mApplication *appl, struct mc_buf *mc)
|
|
|
|
{
|
|
|
|
int ret, idx;
|
|
|
|
uint16_t aid, info = CapiIllAppNr;
|
|
|
|
|
|
|
|
aid = CAPIMSG_APPID(mc->rb);
|
|
|
|
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
|
|
|
|
iprint("Unregister application %d (%d)\n", aid, appl ? appl->AppId : -1);
|
|
|
|
if (appl) {
|
|
|
|
if (aid == appl->AppId) {
|
|
|
|
idx = del_mainpoll(appl->fd);
|
|
|
|
if (idx < 0)
|
|
|
|
wprint("Application %d not found in poll array\n", aid);
|
|
|
|
else
|
|
|
|
pollinfo[idx].data = NULL;
|
|
|
|
ReleaseApplication(appl);
|
|
|
|
info = CapiNoError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
capimsg_setu16(mc->rb, 8, info);
|
|
|
|
ret = send(fd, mc->rb, 10, 0);
|
|
|
|
if (ret != 10)
|
|
|
|
eprint("error send %d/%d - %s\n", ret, 10, strerror(errno));
|
|
|
|
if (info == CapiNoError)
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_serial_number(int fd, struct mc_buf *mc)
|
|
|
|
{
|
|
|
|
int ret, contr = CAPIMSG_U16(mc->rb, 8);
|
|
|
|
|
|
|
|
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
|
|
|
|
capimsg_setu16(mc->rb, 8, CapiNoError);
|
|
|
|
/* only fake for now since we do not have serial numbers on most HW */
|
|
|
|
sprintf((char *)&mc->rb[10], "%07d", contr);
|
|
|
|
ret = send(fd, mc->rb, 18, 0);
|
|
|
|
if (ret != 18)
|
|
|
|
eprint("error send %d/%d - %s\n", ret, 18, strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_capi_version(int fd, struct mc_buf *mc)
|
|
|
|
{
|
|
|
|
/* all the same for all cards */
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
|
|
|
|
capimsg_setu16(mc->rb, 8, CapiNoError);
|
2011-10-29 13:13:58 +00:00
|
|
|
capimsg_setu32(mc->rb, 10, 2); /* major */
|
|
|
|
capimsg_setu32(mc->rb, 14, 0); /* minor */
|
|
|
|
capimsg_setu32(mc->rb, 18, 0); /* manu major */
|
|
|
|
capimsg_setu32(mc->rb, 22, 1); /* manu minor */
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = send(fd, mc->rb, 26, 0);
|
|
|
|
if (ret != 26)
|
|
|
|
eprint("error send %d/%d - %s\n", ret, 26, strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_manufacturer(int fd, struct mc_buf *mc)
|
|
|
|
{
|
|
|
|
/* We us a generic mISDN for now */
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
|
|
|
|
memset(&mc->rb[8], 0, 66);
|
|
|
|
capimsg_setu16(mc->rb, 8, CapiNoError);
|
|
|
|
sprintf((char *)&mc->rb[10], "mISDN");
|
|
|
|
ret = send(fd, mc->rb, 74, 0);
|
|
|
|
if (ret != 74)
|
|
|
|
eprint("error send %d/%d - %s\n", ret, 74, strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void misdn_manufacturer_req(int fd, struct mc_buf *mc)
|
|
|
|
{
|
|
|
|
/* nothing implemented, only return no error */
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
|
|
|
|
capimsg_setu16(mc->rb, 8, CapiNoError);
|
|
|
|
ret = send(fd, mc->rb, 10, 0);
|
|
|
|
if (ret != 10)
|
|
|
|
eprint("error send %d/%d - %s\n", ret, 10, strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int main_recv(int fd, int idx)
|
|
|
|
{
|
|
|
|
int ret, len, cmd, dl;
|
|
|
|
struct mc_buf *mc;
|
|
|
|
|
|
|
|
mc = alloc_mc_buf();
|
|
|
|
if (!mc)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = recv(fd, mc->rb, MC_RB_SIZE, MSG_DONTWAIT);
|
|
|
|
if (ret < 0) {
|
|
|
|
wprint("Error on reading from %d errno %d - %s\n", fd, errno, strerror(errno));
|
|
|
|
ret = -errno;
|
|
|
|
} else if (ret == 0) {
|
|
|
|
/* closed */
|
|
|
|
ret = -ECONNABORTED;
|
|
|
|
} else if (ret < 8) {
|
|
|
|
eprint("Short message read len %d (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
ret, mc->rb[0], mc->rb[1], mc->rb[2], mc->rb[3], mc->rb[4], mc->rb[5], mc->rb[6], mc->rb[7]);
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = -EBADMSG;
|
|
|
|
} else if (ret == MC_RB_SIZE) {
|
|
|
|
eprint("Message too big %d (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
ret, mc->rb[0], mc->rb[1], mc->rb[2], mc->rb[3], mc->rb[4], mc->rb[5], mc->rb[6], mc->rb[7]);
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = -EMSGSIZE;
|
|
|
|
}
|
|
|
|
if (ret < 0)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
len = CAPIMSG_LEN(mc->rb);
|
|
|
|
cmd = CAPIMSG_CMD(mc->rb);
|
|
|
|
if (cmd != CAPI_DATA_B3_REQ) {
|
|
|
|
if (len != ret) {
|
|
|
|
wprint("Msg len error on %04x read %d but indicated %d bytes\n", cmd, ret, len);
|
|
|
|
if (ret < len)
|
|
|
|
mc->len = ret;
|
|
|
|
} else
|
|
|
|
mc->len = len;
|
|
|
|
} else {
|
|
|
|
dl = CAPIMSG_DATALEN(mc->rb);
|
|
|
|
if ((len + dl) != ret) {
|
|
|
|
wprint("Msg len error on DATA_B3_REQ msg len %d data len %d but read %d\n", len, dl, ret);
|
|
|
|
}
|
|
|
|
mc->len = ret;
|
|
|
|
}
|
|
|
|
switch (cmd) {
|
|
|
|
case MIC_GET_PROFILE_REQ:
|
|
|
|
get_profile(fd, mc);
|
|
|
|
break;
|
|
|
|
case MIC_REGISTER_REQ:
|
|
|
|
mIcapi_register(fd, idx, mc);
|
|
|
|
break;
|
|
|
|
case MIC_RELEASE_REQ:
|
|
|
|
mIcapi_release(fd, pollinfo[idx].data, mc);
|
|
|
|
break;
|
|
|
|
case MIC_SERIAL_NUMBER_REQ:
|
|
|
|
get_serial_number(fd, mc);
|
|
|
|
break;
|
|
|
|
case MIC_VERSION_REQ:
|
|
|
|
get_capi_version(fd, mc);
|
|
|
|
break;
|
|
|
|
case MIC_GET_MANUFACTURER_REQ:
|
|
|
|
get_manufacturer(fd, mc);
|
|
|
|
break;
|
|
|
|
case MIC_MANUFACTURER_REQ:
|
|
|
|
misdn_manufacturer_req(fd, mc);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (pollinfo[idx].type == PIT_Application)
|
|
|
|
ret = PutMessageApplication(pollinfo[idx].data, mc);
|
|
|
|
else {
|
|
|
|
wprint("CMD %x for fd=%d type %d not handled\n", cmd, fd, pollinfo[idx].type);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
end:
|
2011-10-29 13:13:58 +00:00
|
|
|
if (ret != 0) /* if message is not queued or freed */
|
2011-10-20 16:34:37 +00:00
|
|
|
free_mc_buf(mc);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main_loop(void)
|
|
|
|
{
|
|
|
|
int res, ret, i, idx, error = 0;
|
|
|
|
int running = 1;
|
|
|
|
int fd;
|
|
|
|
int nconn = -1;
|
|
|
|
struct sockaddr_un caddr;
|
|
|
|
char buf[4096];
|
|
|
|
socklen_t alen;
|
|
|
|
|
|
|
|
ret = add_mainpoll(mIsock, PIT_mISDNmain);
|
|
|
|
if (ret < 0) {
|
|
|
|
eprint("Error while adding mIsock to mainpoll (mainpoll_max %d)\n", mainpoll_max);
|
|
|
|
return -1;
|
2011-10-29 13:13:58 +00:00
|
|
|
} else
|
|
|
|
iprint("mIsock added to idx %d\n", ret);
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = add_mainpoll(mCsock, PIT_CAPImain);
|
|
|
|
if (ret < 0) {
|
|
|
|
eprint("Error while adding mCsock to mainpoll (mainpoll_max %d)\n", mainpoll_max);
|
|
|
|
return -1;
|
2011-10-29 13:13:58 +00:00
|
|
|
} else
|
|
|
|
iprint("mCsock added to idx %d\n", ret);
|
2011-10-20 16:34:37 +00:00
|
|
|
|
2011-10-29 13:13:58 +00:00
|
|
|
while (running) {
|
2011-10-20 16:34:37 +00:00
|
|
|
ret = poll(mainpoll, mainpoll_max, -1);
|
|
|
|
if (ret < 0) {
|
|
|
|
wprint("Error on poll - %s\n", strerror(errno));
|
|
|
|
error = -errno;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (i = 0; i < mainpoll_max; i++) {
|
|
|
|
if (ret && mainpoll[i].revents) {
|
|
|
|
dprint(MIDEBUG_POLL, "Poll return %d for idx %d ev %x fd %d\n", ret,
|
2011-10-29 13:13:58 +00:00
|
|
|
i, mainpoll[i].revents, mainpoll[i].fd);
|
2011-10-20 16:34:37 +00:00
|
|
|
switch (pollinfo[i].type) {
|
|
|
|
case PIT_Bchannel:
|
|
|
|
if (mainpoll[i].revents & POLLIN) {
|
2011-11-25 15:36:12 +00:00
|
|
|
res = recvBchannel(pollinfo[i].data);
|
2011-10-20 16:34:37 +00:00
|
|
|
} else if (mainpoll[i].revents == POLLHUP) {
|
|
|
|
dprint(MIDEBUG_POLL, "Bchannel socket %d closed\n", mainpoll[i].fd);
|
|
|
|
ReleaseBchannel(i);
|
|
|
|
}
|
|
|
|
break;
|
2011-10-29 13:13:58 +00:00
|
|
|
case PIT_CAPImain: /* new connect */
|
2011-10-20 16:34:37 +00:00
|
|
|
if (mainpoll[i].revents & POLLIN) {
|
|
|
|
caddr.sun_family = AF_UNIX;
|
|
|
|
caddr.sun_path[0] = 0;
|
|
|
|
alen = sizeof(caddr);
|
2011-10-29 13:13:58 +00:00
|
|
|
nconn = accept(mCsock, (struct sockaddr *)&caddr, &alen);
|
2011-10-20 16:34:37 +00:00
|
|
|
if (nconn < 0) {
|
|
|
|
eprint("Error on accept - %s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
dprint(MIDEBUG_POLL, "New connection on capisocket\n");
|
|
|
|
idx = add_mainpoll(nconn, PIT_NewConn);
|
|
|
|
if (idx < 0)
|
|
|
|
eprint("Cannot add fd=%d to mainpoll\n", nconn);
|
|
|
|
else
|
2011-10-29 13:13:58 +00:00
|
|
|
dprint(MIDEBUG_POLL, "nconn added to idx %d\n", idx);
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PIT_mISDNmain:
|
|
|
|
res = read(mIsock, buf, 4096);
|
|
|
|
dprint(MIDEBUG_POLL, "Read %d from mIsock (%d)\n", res, mIsock);
|
|
|
|
break;
|
|
|
|
case PIT_Application:
|
|
|
|
if (mainpoll[i].revents == POLLHUP) {
|
|
|
|
dprint(MIDEBUG_POLL, "socket %d closed\n", mainpoll[i].fd);
|
|
|
|
ReleaseApplication(pollinfo[i].data);
|
|
|
|
res = del_mainpoll(mainpoll[i].fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* no break to handle POLLIN */
|
|
|
|
case PIT_NewConn:
|
|
|
|
if (mainpoll[i].revents == POLLHUP) {
|
|
|
|
dprint(MIDEBUG_POLL, "socket %d closed\n", mainpoll[i].fd);
|
|
|
|
close(mainpoll[i].fd);
|
|
|
|
res = del_mainpoll(mainpoll[i].fd);
|
|
|
|
if (res < 0) {
|
|
|
|
eprint("Cannot delete fd=%d from mainpoll result %d\n",
|
2011-10-29 13:13:58 +00:00
|
|
|
mainpoll[i].fd, res);
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
res = main_recv(mainpoll[i].fd, i);
|
|
|
|
if (res == -ECONNABORTED || res == -ECONNRESET) {
|
|
|
|
if (pollinfo[i].type == PIT_Application)
|
|
|
|
ReleaseApplication(pollinfo[i].data);
|
|
|
|
fd = mainpoll[i].fd;
|
|
|
|
dprint(MIDEBUG_POLL, "read 0 socket %d closed\n", fd);
|
|
|
|
close(mainpoll[i].fd);
|
|
|
|
res = del_mainpoll(mainpoll[i].fd);
|
|
|
|
if (res < 0) {
|
2011-10-29 13:13:58 +00:00
|
|
|
eprint("Cannot delete fd=%d from mainpoll result %d\n", fd, res);
|
2011-10-20 16:34:37 +00:00
|
|
|
} else
|
|
|
|
dprint(MIDEBUG_POLL, "Deleted fd=%d from mainpoll\n", fd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
wprint("Unexpected poll event %x on fd %d type %d\n", mainpoll[i].revents,
|
2011-10-29 13:13:58 +00:00
|
|
|
mainpoll[i].fd, pollinfo[i].type);
|
|
|
|
break;
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
2011-10-29 13:13:58 +00:00
|
|
|
ret--;
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
if (ret == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-10-29 13:13:58 +00:00
|
|
|
return error;
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int my_lib_debug(const char *file, int line, const char *func, int level, const char *fmt, va_list va)
|
|
|
|
{
|
|
|
|
int ret, l;
|
|
|
|
char date[64], log[1024];
|
|
|
|
struct tm tm;
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
ret = vsnprintf(log, 256, fmt, va);
|
|
|
|
l = gettimeofday(&tv, NULL);
|
|
|
|
if (tv.tv_usec > 999999) {
|
|
|
|
tv.tv_sec++;
|
|
|
|
tv.tv_usec -= 1000000;
|
|
|
|
}
|
|
|
|
localtime_r(&tv.tv_sec, &tm);
|
|
|
|
l = strftime(date, sizeof(date), "%b %e %T", &tm);
|
|
|
|
snprintf(&date[l], sizeof(date) - l, ".%06d", (int)tv.tv_usec);
|
|
|
|
|
|
|
|
if (DebugFile) {
|
|
|
|
fprintf(DebugFile, "%s %20s:%4d %22s():%s", date, file, line, func, log);
|
|
|
|
fflush(DebugFile);
|
|
|
|
} else if (level > MISDN_LIBDEBUG_WARN)
|
|
|
|
fprintf(stdout, "%s %20s:%4d %22s():%s", date, file, line, func, log);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "%s %20s:%4d %22s():%s", date, file, line, func, log);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-28 10:58:42 +00:00
|
|
|
static int my_capilib_dbg(const char *file, int line, const char *func, const char *fmt, va_list va)
|
|
|
|
{
|
|
|
|
return my_lib_debug(file, line, func, 1, fmt, va);
|
|
|
|
}
|
|
|
|
|
2011-10-20 16:34:37 +00:00
|
|
|
static struct mi_ext_fn_s l3dbg = {
|
|
|
|
.prt_debug = my_lib_debug,
|
|
|
|
};
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int ret, i, j, nb, c, exitcode = 1, ver, libdebug;
|
|
|
|
struct sockaddr_un mcaddr;
|
|
|
|
struct pController *pc;
|
|
|
|
|
|
|
|
config_file = def_config;
|
|
|
|
ret = opt_parse(argc, argv);
|
|
|
|
if (ret)
|
|
|
|
exit(1);
|
|
|
|
|
|
|
|
if (DebugFileName) {
|
|
|
|
DebugFile = fopen(DebugFileName, "w");
|
|
|
|
if (!DebugFile) {
|
|
|
|
fprintf(stderr, "Cannot open %s - %s\n", DebugFileName, strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
libdebug = (debugmask & 0xff) << 24;
|
|
|
|
if (debugmask & 0x100)
|
|
|
|
libdebug |= 0xfffff;
|
|
|
|
|
2011-10-28 10:58:42 +00:00
|
|
|
register_dbg_vprintf(my_capilib_dbg);
|
2011-10-20 16:34:37 +00:00
|
|
|
ver = init_layer3(4, &l3dbg);
|
|
|
|
mISDN_set_debug_level(libdebug);
|
|
|
|
iprint("Init mISDN lib version %x, debug = %x (%x)\n", ver, debugmask, libdebug);
|
2011-10-29 13:13:58 +00:00
|
|
|
|
2011-10-20 16:34:37 +00:00
|
|
|
/* open mISDN */
|
|
|
|
mIsock = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
|
|
|
|
if (mIsock < 0) {
|
|
|
|
fprintf(stderr, "mISDNv2 not installed - %s\n", strerror(errno));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get number of stacks */
|
|
|
|
ret = ioctl(mIsock, IMGETCOUNT, &mI_count);
|
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "mISDNv2 IMGETCOUNT error - %s\n", strerror(errno));
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
if (mI_count < 1) {
|
|
|
|
fprintf(stderr, "mISDNv2 no controllers found\n");
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
mI_Controller = calloc(mI_count, sizeof(*mI_Controller));
|
|
|
|
if (!mI_Controller) {
|
|
|
|
fprintf(stderr, "no memory to allocate %d controller struct (a %zd)\n", mI_count, sizeof(*mI_Controller));
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
for (i = 0; i < mI_count; i++) {
|
|
|
|
pc = &mI_Controller[i];
|
|
|
|
pc->mNr = i;
|
|
|
|
pc->devinfo.id = i;
|
2011-10-29 13:13:58 +00:00
|
|
|
pc->enable = 1; /* default all controllers are enabled */
|
2011-10-20 16:34:37 +00:00
|
|
|
pc->L3Proto = L3_PROTOCOL_DSS1_USER;
|
|
|
|
pc->L3Flags = 0;
|
|
|
|
ret = ioctl(mIsock, IMGETDEVINFO, &pc->devinfo);
|
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "mISDNv2 IMGETDEVINFO error controller %d - %s\n", i + 1, strerror(errno));
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
c = 0;
|
|
|
|
nb = 0;
|
2011-10-29 13:13:58 +00:00
|
|
|
while (c <= MISDN_MAX_CHANNEL + 1) {
|
2011-10-20 16:34:37 +00:00
|
|
|
if (c <= MISDN_MAX_CHANNEL && test_channelmap(c, pc->devinfo.channelmap)) {
|
|
|
|
nb++;
|
|
|
|
pc->BImax = c;
|
|
|
|
}
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
pc->BImax++;
|
|
|
|
pc->BInstances = calloc(pc->BImax, sizeof(*pc->BInstances));
|
|
|
|
if (!pc->BInstances) {
|
|
|
|
fprintf(stderr, "no memory to allocate %d Bchannel instances for controller %d\n", pc->BImax, i + 1);
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
for (j = 0; j < pc->BImax; j++) {
|
|
|
|
pc->BInstances[j].nr = j;
|
|
|
|
pc->BInstances[j].pc = pc;
|
|
|
|
pc->BInstances[j].fd = -1;
|
2011-11-25 15:36:12 +00:00
|
|
|
sem_init(&pc->BInstances[j].wait, 0, 0);
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
pc->profile.ncontroller = i + 1;
|
|
|
|
pc->profile.nbchannel = nb;
|
2011-10-29 13:13:58 +00:00
|
|
|
pc->profile.goptions = 1; /* internal controller */
|
2011-10-20 16:34:37 +00:00
|
|
|
pc->profile.support1 = 0;
|
|
|
|
pc->profile.support2 = 0;
|
|
|
|
pc->profile.support3 = 0x01;
|
|
|
|
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK))) {
|
|
|
|
pc->profile.support1 |= 0x02;
|
|
|
|
pc->profile.support2 |= 0x02;
|
2011-11-25 15:36:12 +00:00
|
|
|
#ifdef USE_SOFTFAX
|
|
|
|
pc->profile.support1 |= 0x10;
|
|
|
|
pc->profile.support2 |= 0x10;
|
|
|
|
pc->profile.support3 |= 0x30;
|
|
|
|
#endif
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK))) {
|
|
|
|
pc->profile.support1 |= 0x01;
|
|
|
|
}
|
|
|
|
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK))) {
|
|
|
|
pc->profile.support2 |= 0x01;
|
|
|
|
}
|
|
|
|
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_L2DTMF & ISDN_P_B_MASK))) {
|
|
|
|
pc->profile.goptions |= 0x08;
|
|
|
|
}
|
|
|
|
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK))) {
|
|
|
|
pc->profile.goptions |= 0x08;
|
|
|
|
}
|
|
|
|
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_T30_FAX & ISDN_P_B_MASK))) {
|
|
|
|
pc->profile.support1 |= 0x10;
|
|
|
|
pc->profile.support2 |= 0x10;
|
|
|
|
pc->profile.support3 |= 0x10;
|
|
|
|
}
|
|
|
|
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_MODEM_ASYNC & ISDN_P_B_MASK))) {
|
|
|
|
pc->profile.support1 |= 0x180;
|
|
|
|
pc->profile.support3 |= 0x80;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i = read_config_file(config_file);
|
|
|
|
if (i < 0)
|
|
|
|
goto errout;
|
|
|
|
retry_Csock:
|
|
|
|
mCsock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
|
|
|
if (mCsock < 0) {
|
|
|
|
fprintf(stderr, "cannot create socket - %s\n", strerror(errno));
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
mcaddr.sun_family = AF_UNIX;
|
|
|
|
sprintf(mcaddr.sun_path, MISDN_CAPI_SOCKET_PATH);
|
2011-10-29 13:13:58 +00:00
|
|
|
ret = bind(mCsock, (struct sockaddr *)&mcaddr, sizeof(mcaddr));
|
2011-10-20 16:34:37 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "cannot bind socket to %s - %s\n", mcaddr.sun_path, strerror(errno));
|
2011-10-29 13:13:58 +00:00
|
|
|
if (errno == EADDRINUSE) { /* old socket file exist */
|
|
|
|
ret = connect(mCsock, (struct sockaddr *)&mcaddr, sizeof(mcaddr));
|
2011-10-20 16:34:37 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
/* seems the socket file is not in use */
|
|
|
|
ret = unlink(MISDN_CAPI_SOCKET_PATH);
|
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "cannot remove old socket file %s - %s\n",
|
|
|
|
MISDN_CAPI_SOCKET_PATH, strerror(errno));
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
close(mCsock);
|
|
|
|
goto retry_Csock;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "mISDNcapid is already running - only one instance can be used\n");
|
|
|
|
goto errout;
|
|
|
|
}
|
2011-10-29 13:13:58 +00:00
|
|
|
|
2011-10-20 16:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (listen(mCsock, 15)) {
|
|
|
|
fprintf(stderr, "cannot set listen mode for socket %s - %s\n", mcaddr.sun_path, strerror(errno));
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
fprintf(stderr, "debug=%#x do_daemon=%d config=%s\n", debugmask, do_daemon, config_file);
|
|
|
|
if (do_daemon) {
|
|
|
|
ret = daemon(0, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "cannot run as daemon\n");
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
init_listen();
|
|
|
|
init_lPLCI_fsm();
|
|
|
|
init_ncci_fsm();
|
2011-11-25 15:36:12 +00:00
|
|
|
#ifdef USE_SOFTFAX
|
|
|
|
create_lin2alaw_table();
|
|
|
|
g3_gen_tables();
|
|
|
|
#endif
|
2011-10-20 16:34:37 +00:00
|
|
|
exitcode = main_loop();
|
|
|
|
free_ncci_fsm();
|
|
|
|
free_lPLCI_fsm();
|
|
|
|
free_listen();
|
|
|
|
errout:
|
|
|
|
cleanup_layer3();
|
|
|
|
if (mCsock > -1)
|
|
|
|
close(mCsock);
|
|
|
|
close(mIsock);
|
|
|
|
if (mI_Controller)
|
|
|
|
free(mI_Controller);
|
|
|
|
unlink(MISDN_CAPI_SOCKET_PATH);
|
|
|
|
return exitcode;
|
|
|
|
}
|