387 lines
9.9 KiB
C
387 lines
9.9 KiB
C
/* mlayer3.c
|
|
*
|
|
* Author Karsten Keil <kkeil@linux-pingi.de>
|
|
*
|
|
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
|
|
* Copyright 2010 by Karsten Keil <kkeil@linux-pingi.de>
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
|
* version 2.1 as published by the Free Software Foundation.
|
|
*
|
|
* This code 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 LESSER GENERAL PUBLIC LICENSE for more details.
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <mISDN/mlayer3.h>
|
|
#include <mISDN/mbuffer.h>
|
|
#include <errno.h>
|
|
#include "helper.h"
|
|
#include "layer3.h"
|
|
#include "debug.h"
|
|
#include <mISDN/af_isdn.h>
|
|
#include "../../libmisdn/socket.h"
|
|
|
|
static int __init_done = 0;
|
|
|
|
static int user_space_misdn = 0;
|
|
|
|
struct mi_ext_fn_s *mi_extern_func;
|
|
|
|
unsigned int
|
|
init_layer3(int nr, struct mi_ext_fn_s *fn, int _user_space_misdn)
|
|
{
|
|
user_space_misdn = _user_space_misdn;
|
|
init_mbuffer(nr);
|
|
mISDNl3New();
|
|
__init_done = 1;
|
|
mI_debug_mask = 0;
|
|
if (fn)
|
|
mi_extern_func = fn;
|
|
else
|
|
mi_extern_func = NULL;
|
|
return MISDN_LIB_INTERFACE;
|
|
}
|
|
|
|
void
|
|
cleanup_layer3(void)
|
|
{
|
|
cleanup_mbuffer();
|
|
mISDNl3Free();
|
|
__init_done = 0;
|
|
}
|
|
|
|
/* for now, maybe get this via some register prortocol in future */
|
|
extern struct l3protocol dss1user;
|
|
extern struct l3protocol dss1net;
|
|
/*
|
|
* open a layer3 stack
|
|
* parameter1 - device id
|
|
* parameter2 - protocol
|
|
* parameter3 - layer3 additional properties
|
|
* parameter4 - callback function to deliver messages
|
|
* parameter5 - pointer for private application use
|
|
*/
|
|
struct mlayer3 *
|
|
open_layer3(unsigned int dev, unsigned int proto, unsigned int prop, mlayer3_cb_t *f, void *p)
|
|
{
|
|
struct _layer3 *l3 = NULL;
|
|
int fd = 0, ret;
|
|
struct mISDNversion ver;
|
|
int set = 1;
|
|
void *mui;
|
|
u_int cnt;
|
|
|
|
if (__init_done == 0) {
|
|
eprint("You should call init_layer3(nr of message cache entres) first\n");
|
|
init_layer3(10, NULL, user_space_misdn);
|
|
}
|
|
if (user_space_misdn) {
|
|
ret = mISDN_base_create(&mui, ISDN_P_BASE);
|
|
if (ret < 0) {
|
|
eprint("could not user space mISDN: %s\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
ret = mISDN_base_ioctl(mui, IMGETVERSION, &ver);
|
|
} else {
|
|
fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
|
|
if (fd < 0) {
|
|
eprint("could not open socket: %s\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
ret = ioctl(fd, IMGETVERSION, &ver);
|
|
}
|
|
if (ret < 0) {
|
|
eprint("could not send IOCTL IMGETVERSION %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
|
|
if (ver.release & MISDN_GIT_RELEASE)
|
|
iprint("mISDN kernel version %d.%02d.%d (git.misdn.eu) found\n", ver.major, ver.minor, ver.release & ~MISDN_GIT_RELEASE);
|
|
else
|
|
iprint("mISDN kernel version %d.%02d.%d found\n", ver.major, ver.minor, ver.release);
|
|
iprint("mISDN user version %d.%02d.%d found\n", MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
|
|
iprint("mISDN library interface version %d release %d\n", MISDN_LIB_VERSION, MISDN_LIB_RELEASE);
|
|
|
|
if (ver.major != MISDN_MAJOR_VERSION) {
|
|
eprint("VERSION incompatible please update\n");
|
|
goto fail;
|
|
}
|
|
/* handle version backward compatibility specific stuff here */
|
|
|
|
if (mui)
|
|
ret = mISDN_base_ioctl(mui, IMGETCOUNT, &cnt);
|
|
if (fd)
|
|
ret = ioctl(fd, IMGETCOUNT, &cnt);
|
|
if (ret < 0) {
|
|
eprint("could not send IOCTL IMGETCOUNT %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
if (cnt <= dev) {
|
|
eprint("Given device ID '%d' does not exist, there are '%d' devices.\n", dev, cnt);
|
|
}
|
|
|
|
l3 = calloc(1, sizeof(struct _layer3));
|
|
if (!l3) {
|
|
goto fail;
|
|
}
|
|
l3->ml3.devinfo = calloc(1, sizeof(*l3->ml3.devinfo));
|
|
if (!l3->ml3.devinfo) {
|
|
goto fail;
|
|
}
|
|
l3->ml3.options = prop;
|
|
l3->ml3.from_layer3 = f;
|
|
l3->ml3.priv = p;
|
|
|
|
init_l3(l3);
|
|
l3->ml3.devinfo->id = dev;
|
|
if (mui)
|
|
ret = mISDN_base_ioctl(mui, IMGETDEVINFO, l3->ml3.devinfo);
|
|
if (fd)
|
|
ret = ioctl(fd, IMGETDEVINFO, l3->ml3.devinfo);
|
|
if (ret < 0) {
|
|
eprint("could not send IOCTL IMGETDEVINFO %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
if (mui) {
|
|
mISDN_base_release(mui);
|
|
mui = NULL;
|
|
}
|
|
if (fd) {
|
|
close(fd);
|
|
fd = 0;
|
|
}
|
|
l3->ml3.nr_bchannel = l3->ml3.devinfo->nrbchan;
|
|
if (!(l3->ml3.devinfo->Dprotocols & (1 << ISDN_P_TE_E1))
|
|
&& !(l3->ml3.devinfo->Dprotocols & (1 << ISDN_P_NT_E1)))
|
|
test_and_set_bit(FLG_BASICRATE, &l3->ml3.options);
|
|
switch(proto) {
|
|
case L3_PROTOCOL_DSS1_USER:
|
|
if (!(l3->ml3.devinfo->Dprotocols & (1 << ISDN_P_TE_S0))
|
|
&& !(l3->ml3.devinfo->Dprotocols & (1 << ISDN_P_TE_E1))) {
|
|
eprint("protocol L3_PROTOCOL_DSS1_USER device do not support ISDN_P_TE_S0 / ISDN_P_TE_E1\n");
|
|
goto fail;
|
|
}
|
|
if (user_space_misdn) {
|
|
ret = mISDN_data_create(&mui, ISDN_P_LAPD_TE);
|
|
if (ret < 0) {
|
|
eprint("could not open ISDN_P_LAPD_TE protocol %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
} else {
|
|
fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE);
|
|
if (fd < 0) {
|
|
eprint("could not open ISDN_P_LAPD_TE protocol %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
}
|
|
dss1user.init(l3);
|
|
break;
|
|
case L3_PROTOCOL_DSS1_NET:
|
|
if (!(l3->ml3.devinfo->Dprotocols & (1 << ISDN_P_NT_S0))
|
|
&& !(l3->ml3.devinfo->Dprotocols & (1 << ISDN_P_NT_E1))) {
|
|
eprint("protocol L3_PROTOCOL_DSS1_NET device do not support ISDN_P_NT_S0 / ISDN_P_NT_E1\n");
|
|
goto fail;
|
|
}
|
|
if (user_space_misdn) {
|
|
ret = mISDN_data_create(&mui, ISDN_P_LAPD_NT);
|
|
if (ret < 0) {
|
|
eprint("could not open ISDN_P_LAPD_NT protocol %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
} else {
|
|
fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
|
|
if (fd < 0) {
|
|
eprint("could not open ISDN_P_LAPD_NT protocol %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
}
|
|
dss1net.init(l3);
|
|
break;
|
|
default:
|
|
eprint("protocol %x not supported\n", proto);
|
|
goto fail;
|
|
}
|
|
|
|
l3->l2master.l2addr.family = AF_ISDN;
|
|
l3->l2master.l2addr.dev = dev;
|
|
l3->l2master.l2addr.channel = 0;
|
|
l3->l2master.l2addr.sapi = 0;
|
|
if (test_bit(MISDN_FLG_PTP, &l3->ml3.options))
|
|
l3->l2master.l2addr.tei = 0;
|
|
else
|
|
l3->l2master.l2addr.tei = 127;
|
|
if (mui)
|
|
ret = mISDN_data_bind(mui, (struct sockaddr *)&l3->l2master.l2addr, sizeof(l3->l2master.l2addr));
|
|
if (fd)
|
|
ret = bind(fd, (struct sockaddr *)&l3->l2master.l2addr, sizeof(l3->l2master.l2addr));
|
|
if (ret < 0) {
|
|
eprint("could not bind socket for device %d:%s\n", dev, strerror(errno));
|
|
goto fail;
|
|
}
|
|
if (test_bit(MISDN_FLG_L2_CLEAN, &l3->ml3.options)
|
|
&& proto == L3_PROTOCOL_DSS1_NET) {
|
|
if (mui)
|
|
ret = mISDN_data_ioctl(mui, IMCLEAR_L2, &set);
|
|
if (fd)
|
|
ret = ioctl(fd, IMCLEAR_L2, &set);
|
|
if (ret < 0) {
|
|
eprint("could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
}
|
|
if (test_bit(MISDN_FLG_L1_HOLD, &l3->ml3.options)) {
|
|
if (mui)
|
|
ret = mISDN_data_ioctl(mui, IMHOLD_L1, &set);
|
|
if (fd)
|
|
ret = ioctl(fd, IMHOLD_L1, &set);
|
|
if (ret < 0) {
|
|
eprint("could not send IOCTL IMHOLD_L1 %s\n", strerror(errno));
|
|
goto fail;
|
|
}
|
|
}
|
|
l3->l2inst = mui;
|
|
l3->l2sock = fd;
|
|
if (!user_space_misdn) {
|
|
l3->tbase.tdev = open("/dev/mISDNtimer", O_RDWR);
|
|
if (l3->tbase.tdev < 0) {
|
|
eprint("could not open /dev/mISDNtimer %s\n", strerror(errno));
|
|
eprint("It seems that you don't use udev filesystem. You may use this workarround:\n\n");
|
|
eprint("Do 'cat /proc/misc' and see the number in front of 'mISDNtimer'.\n");
|
|
eprint("Do 'mknod /dev/mISDNtimer c 10 xx', where xx is the number you saw.\n");
|
|
eprint("Note: This number changes if you load modules in different order, that use misc device.\n");
|
|
goto fail;
|
|
}
|
|
if (l3->l2sock < l3->tbase.tdev)
|
|
l3->maxfd = l3->tbase.tdev;
|
|
else
|
|
l3->maxfd = l3->l2sock;
|
|
}
|
|
if (!user_space_misdn) {
|
|
ret = l3_start(l3);
|
|
if (ret < 0) {
|
|
eprint("could not start layer3 thread for device %d\n", dev);
|
|
close(l3->tbase.tdev);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
return(&l3->ml3);
|
|
fail:
|
|
if (mui)
|
|
mISDN_base_release(mui);
|
|
if (fd)
|
|
close(fd);
|
|
if (l3) {
|
|
release_l3(l3);
|
|
if (l3->ml3.devinfo)
|
|
free(l3->ml3.devinfo);
|
|
free(l3);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* close a layer3 stack
|
|
* parameter1 - stack struct
|
|
*/
|
|
void
|
|
close_layer3(struct mlayer3 *ml3)
|
|
{
|
|
struct _layer3 *l3;
|
|
|
|
l3 = container_of(ml3, struct _layer3, ml3);
|
|
if (l3->l2sock)
|
|
l3_stop(l3);
|
|
if (l3->l2inst)
|
|
mISDN_base_release(l3->l2inst);
|
|
if (l3->l2sock)
|
|
close(l3->l2sock);
|
|
if (!user_space_misdn)
|
|
close(l3->tbase.tdev);
|
|
release_l3(l3);
|
|
if (ml3->devinfo)
|
|
free(ml3->devinfo);
|
|
free(l3);
|
|
}
|
|
|
|
int
|
|
work_layer3(struct mlayer3 *ml3)
|
|
{
|
|
struct _layer3 *l3;
|
|
|
|
l3 = container_of(ml3, struct _layer3, ml3);
|
|
return layer3_work(l3);
|
|
}
|
|
|
|
int
|
|
mISDN_set_pcm_slots(struct mlayer3 *ml3, int channel, int tx, int rx)
|
|
{
|
|
/* need to be reimplemented */
|
|
#ifdef MISDN_CTRL_SET_PCM_SLOTS
|
|
struct _layer3 *l3;
|
|
struct mISDN_ctrl_req ctrlrq;
|
|
int ret = 0;
|
|
|
|
ctrlrq.op = MISDN_CTRL_SET_PCM_SLOTS;
|
|
ctrlrq.channel = 0; /* via D-channel */
|
|
ctrlrq.p1 = channel;
|
|
ctrlrq.p2 = tx;
|
|
ctrlrq.p3 = rx;
|
|
l3 = container_of(ml3, struct _layer3, ml3);
|
|
if (l3->l2inst)
|
|
ret = mISDN_data_ioctl(l3->l2inst, IMCTRLREQ, &ctrlrq);
|
|
if (l3->l2sock)
|
|
ret = ioctl(l3->l2sock, IMCTRLREQ, &ctrlrq);
|
|
if (ret < 0)
|
|
eprint("could not send IOCTL IMCTRLREQ %s\n", strerror(errno));
|
|
#else
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
eprint("%s not supported in this version\n", __func__);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
mISDN_get_pcm_slots(struct mlayer3 *ml3, int channel, int *txp, int *rxp)
|
|
{
|
|
#ifdef MISDN_CTRL_GET_PCM_SLOTS
|
|
struct _layer3 *l3;
|
|
struct mISDN_ctrl_req ctrlrq;
|
|
int ret = 0;
|
|
|
|
ctrlrq.op = MISDN_CTRL_GET_PCM_SLOTS;
|
|
ctrlrq.channel = 0; /* via D-channel */
|
|
ctrlrq.p1 = channel;
|
|
l3 = container_of(ml3, struct _layer3, ml3);
|
|
if (l3->l2inst)
|
|
ret = mISDN_data_ioctl(l3->l2inst, IMCTRLREQ, &ctrlrq);
|
|
if (l3->l2sock)
|
|
ret = ioctl(l3->l2sock, IMCTRLREQ, &ctrlrq);
|
|
if (ret < 0)
|
|
eprint("could not send IOCTL IMCTRLREQ %s\n", strerror(errno));
|
|
else {
|
|
if (txp)
|
|
*txp = ctrlrq.p2;
|
|
if (rxp)
|
|
*rxp = ctrlrq.p3;
|
|
}
|
|
#else
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
eprint("%s not supported in this version\n", __func__);
|
|
#endif
|
|
return ret;
|
|
}
|