Compare commits

...

38 Commits

Author SHA1 Message Date
Andreas Eversberg 09852230d6 Audio rework, new jitter buffer
Jitter buffer is now based on packets, not on samples. The frames are
dejittered in received form. After reading from jitter buffer, they are
decoded in correct order. If a frame is missing, it is concealed by
repeating audio.
2024-03-29 21:43:16 +01:00
Andreas Eversberg ff307204a0 Updated libs 2024-03-26 22:02:29 +01:00
Dennis Grunert 56b8bf97d7 Add "morsegerman" parameter; fix german tone pattern timings
- Parameter "morsegerman" for option "local-tones" will generate morse-a dialtone (pre-1979).
- Fixed german tone pattern timings according to 1TR110-1 Chapter 8
  * Busy tone: 480ms on / 480ms off
  * Hangup tone: 480ms on / 480ms off
  * Old german ringing: 1000ms on / 4000ms off
  * Old german busy/hangup tone: 125ms on / 475ms off
2024-03-26 22:02:29 +01:00
Dennis Grunert 5ed88bacae Change IE_METERING timing information from deciseconds to seconds and milliseconds 2024-03-26 22:02:29 +01:00
Dennis Grunert 04aa799e25 Fix: AOC-D unit information length; encode AOC-E according to real traces; Add option to disable seconds in time/date IE
- Fix: AOC-D unit information length was set to 1 despite its actual length
- Removed optional BillingID from AOC-E data
- Cross-checked AOC-D and AOC-E IEs to other systems (Audiocodes Mediant 800; traces found in public sources)
- Tested with ISDN TEs: Siemens Profiset 51, T-Concept P522, Philips SOPHO ErgoLine, sphairon NT1PLUS-split; K1297 ISDN protocol tester
- Attach AOC-E to DISCONNECT or RELEASE; transmit AOC-E even if zero units have been charged
- Send AOC-D with zero units to indicate free of charge call
- New option "time-no-seconds" disables sending current seconds within the date/time IE
2024-03-26 22:02:29 +01:00
Andreas Eversberg c596d25ff6 Updated libs 2024-03-26 22:02:29 +01:00
Dennis Grunert 654a3c57f9 AOC-S: Changed typeOfCharging to stepFunction
The durationCurrency typeOfCharging=stepFunction is more suitable to a per-unit metering than continuousCharging. stepFunction implies start of the first unit at call connect - for normal calls without additional unit charges for call setup, chargedItem CallSetup is set to FreeOfCharge.
Source: ETS 300 178
Tested with Bluephone and Clubtelefon 5 ISDN.
2024-03-26 22:02:29 +01:00
Dennis Grunert f8d41e8cb0 AOC-S: Use variable time unit length (1...3 Bytes). Fixes wrong time calculation on Clubtelefon 5 ISDN 2024-03-26 22:02:29 +01:00
Dennis Grunert a192a3ae97 AOC-S time scale adjusted 2024-03-26 22:02:29 +01:00
Dennis Grunert 55bf04c9c8 AOC information as separate facility messages for compatibility (CONNECT-attached AOC-D IE ignored by NT1plus) 2024-03-26 22:02:29 +01:00
Dennis Grunert 45177fa41c Handling of unknown facility indications from terminals 2024-03-26 22:02:29 +01:00
Dennis Grunert b7887e39cf Changed AOC-D encoding value of TypeOfChargingInfo from Total to SubTotal
encodeAOCDChargingUnitOperation() set the value of TypeOfChargingInfo to Total, which results in ISDN phones not correctly displaying increasing units. Setting it to SubTotal fixed the issue.
2024-03-26 22:02:28 +01:00
Dennis Grunert 57896efb5f AOC-D and AOC-S facility generation from CC metering information
If metering details via IE_METERING are received from osmo-cc, AOC-S and/or AOC-D facility IEs are attached to PROCEEDING/ALERTING/CONNECT messages, and AOC-D facility messages are generated during the call. This feature can be enabled by setting 'aocs' and/or 'aocd' within the mISDN endpoint configuration.
2024-03-26 22:02:28 +01:00
Andreas Eversberg 6ab6a91030 Move from local to external osmo* libraries
src/libdebug -> libosmocore
src/libselect -> libosmocore
src/libtimer -> libosmocore
src/libosmocc -> libosmo-cc
src/libg711 -> libosmo-cc
2024-03-26 22:02:28 +01:00
Andreas Eversberg 805ed34740 Updated libs 2024-01-07 18:07:42 +01:00
Andreas Eversberg fe60d15a20 Updated libs 2023-08-19 15:27:53 +02:00
Andreas Eversberg b4f5e950fc Fix channel selection for PTMP/NT-Mode
Channel selection in that mode must be forced, so that the TE nay not
select a different channel. In that mode the TE cannot know what
channel is allocated, so it cannot select a different channel.

Eumex PBX rejects calls without this fix.
2023-08-19 10:55:19 +02:00
Andreas Eversberg 3d385fd095 Fix brigde daemon forking; no more 'defunct' child process 2023-03-12 15:58:12 +01:00
Andreas Eversberg 72043327b9 Send two B-channel frames when first frame is received
The size of both frames match the receive size.
2023-03-04 18:47:19 +01:00
Andreas Eversberg de611e513d Debugging of libmisdn shows file and line now 2023-01-23 19:17:24 +01:00
Andreas Eversberg 8c10ee4a26 Replace polling by select and timer events 2023-01-23 18:36:36 +01:00
Andreas Eversberg 3e970cbc48 Updated libs 2023-01-22 19:52:38 +01:00
Andreas Eversberg 6b3911fc26 Add confirm message to ph_driver 2023-01-22 19:51:01 +01:00
Andreas Eversberg 2d24c1e12b fixed minor comment 2023-01-22 19:50:20 +01:00
Andreas Eversberg c72e5070ef Add comfort noise when call is on hold
This is required so that the RTP stream does not interrupt.
2022-10-30 16:58:44 +01:00
Andreas Eversberg e8a91e12f9 Updated libs 2022-10-30 16:58:38 +01:00
Andreas Eversberg 42f4e4a243 Fixed suspend/resume feature 2022-10-30 16:57:16 +01:00
Andreas Eversberg e6aa538562 Remove DSP kernel module dependency
* Use raw bchannel instead of DSP feature
* Removed DSP features
* Use new jitter buffer and control TX FIFO load by this application
* Added bridging daemon to assign time slots for bridging
2022-10-30 16:57:12 +01:00
Andreas Eversberg 9bbca1fb9e Add user space tone generation code 2022-09-16 18:24:26 +02:00
Andreas Eversberg be40b44515 Removed ph_socket, it is now a lib 2022-09-16 18:24:24 +02:00
Andreas Eversberg bc888d85cc Add clearmode and handle bearer capability according to Osmo-CC spec 2022-09-16 17:19:02 +02:00
Andreas Eversberg 8c0419b70e Add kernel (hardware) bridging 2022-09-16 17:18:58 +02:00
Andreas Eversberg 63371cc055 Make this application capable of using kernel or user space mISDN 2022-09-16 17:18:56 +02:00
Andreas Eversberg b85d9d6580 libmisdnuser: Fixed compiler warning 2022-09-16 17:18:52 +02:00
Andreas Eversberg 64b5653b05 Add mISDNuser to this project 2022-09-16 17:18:49 +02:00
Andreas Eversberg 8a81901d9c Fixed mISDN (kernel code) to use MPH_ instead of PH_ towards layer 3 2022-09-16 17:18:44 +02:00
Andreas Eversberg 46a7a51d4a Add port of mISDN kernel driver for use in user space 2022-09-16 17:18:42 +02:00
Andreas Eversberg 68874b3b22 Updated Libs 2022-09-16 17:18:37 +02:00
122 changed files with 32120 additions and 9289 deletions

8
.gitignore vendored
View File

@ -33,11 +33,11 @@ Doxyfile
.*.sw?
src/libdebug/libdebug.a
src/libg711/libg711.a
src/liblogging/liblogging.a
src/libjitter/libjitter.a
src/liboptions/liboptions.a
src/libosmocc/libosmocc.a
src/libsample/libsample.a
src/libtimer/libtimer.a
src/libph_socket/libph_socket.a
src/libmisdn/libmisdn.a
src/libmisdnuser/libmisdnuser.a
src/isdn/osmo-cc-misdn-endpoint

View File

@ -74,19 +74,22 @@ AC_SUBST(SYMBOL_VISIBILITY)
dnl Generate the output
AM_CONFIG_HEADER(config.h)
AC_CHECK_HEADERS([mISDN/mbuffer.h], , [AC_MSG_FAILURE(Missing mISDN user library. Please install mISDNuser!)])
#AC_CHECK_HEADERS([mISDN/mbuffer.h], , [AC_MSG_FAILURE(Missing mISDN user library. Please install mISDNuser!)])
AC_CHECK_LIB([m], [main])
AC_CHECK_LIB([pthread], [main])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCC, libosmocc >= 2.0.0)
AC_OUTPUT(
src/liboptions/Makefile
src/libdebug/Makefile
src/liblogging/Makefile
src/libsample/Makefile
src/libtimer/Makefile
src/libjitter/Makefile
src/libosmocc/Makefile
src/libg711/Makefile
src/libph_socket/Makefile
src/libmisdn/Makefile
src/libmisdnuser/Makefile
src/isdn/Makefile
src/Makefile
Makefile)

View File

@ -2,11 +2,11 @@ AUTOMAKE_OPTIONS = foreign
SUBDIRS = \
liboptions \
libdebug \
liblogging \
libsample \
libtimer \
libjitter \
libosmocc \
libg711 \
libph_socket \
libmisdn \
libmisdnuser \
isdn

View File

@ -1,4 +1,4 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) -I$(srcdir)/../libmisdnuser/include
bin_PROGRAMS = \
osmo-cc-misdn-endpoint
@ -7,16 +7,22 @@ osmo_cc_misdn_endpoint_SOURCES = \
ie.c \
dss1.c \
isdn.c \
tones.c \
bridge.c \
ph_driver.c \
main.c
osmo_cc_misdn_endpoint_LDADD = \
$(COMMON_LA) \
../libdebug/libdebug.a \
../liboptions/liboptions.a \
../libsample/libsample.a \
../libtimer/libtimer.a \
../libjitter/libjitter.a \
../libosmocc/libosmocc.a \
../libg711/libg711.a \
-lmisdn
../libph_socket/libph_socket.a \
../libmisdn/libmisdn.a \
../libmisdnuser/libmisdnuser.a \
../liblogging/liblogging.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCC_LIBS)
# -lmisdn

891
src/isdn/bridge.c Normal file
View File

@ -0,0 +1,891 @@
/* HFC PCM bridging server and client
*
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* 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 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/>.
*/
/*
* How does it work:
*
* The time slot assignment is done by a server process that runs on a Unix
* socket. The server is implemented in this application and is started by
* the first instance of this application. Each instance of this application
* connects to the server. If all instances of this applications disconnect
* (exit) from the server, it will terminate itself.
*
* If a call is made, the client need to check if it can bridge:
*
* - There must be no 3pty call, this would not allow bridging.
* Note: Transmitting tones will override bridge inside HFC card, so no
* disconnect is required.
* - The RTP port of the originator (interface -> osmo-cc) must be known.
* - The terminator (osmo-cc -> interface) knows that the originator's RTP IP
* is on the local machine.
*
* The client will then tell the server about card number, port of the card
* and channel of the port, together with the originator's RTP port. If there
* is another client with the same originator's RTP port, PCM time slots will
* be assigned to both clients. Then the clients will connect to the time
* slots that are given by the server. If the clients are on different cards,
* only one time slot will be assigned, because PCM bus has two lines (banks)
* for both directions. If the clients are on the same card, two time slots
* will be assigned, one for each direction. This is caused by a hardware
* limitation.
*
* If the client terminates the call or the call can no longer be bridged, it
* telles the server that it cannot bridge. The server will unassign the time
* slots to both clients, if they were assigned. Then the clients will
* disconnect from time slots, if connected.
*
* If the client terminates socket connection to the server (crash), the
* server will remove all assigned slots that were allocated to this clients.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ifaddrs.h>
#include <errno.h>
#include "../liblogging/logging.h"
#include <mISDN/mbuffer.h>
#include "isdn.h"
#include "bridge.h"
#define SOCKET_NAME "isdn_hfc_bridge_service_bla_blub_peng"
#define SOCKET_VERSION 0x4fc4fc01
/* message from client to server */
struct msg_channel_info {
/* version magic */
uint32_t version;
/* physical identity */
int card;
int port;
int channel;
/* rtp stream identity */
uint16_t orig_rtp_port;
/* can we bridge? */
int can_bridge;
};
/* message from server to client */
struct msg_bridge_order {
/* version magic */
uint32_t version;
/* physical identity */
int card;
int port;
int channel;
/* pcm connection */
int pcm_bridge;
int rx_slot;
int tx_slot;
int rx_bank;
int tx_bank;
};
static int num_slots;
/*
* server
*/
struct conn {
struct conn *next;
struct osmo_fd ofd;
int rx_index;
struct msg_channel_info
rx_msg;
};
struct channel {
struct channel *next;
/* client realation */
int socket;
/* physical identity */
int card;
int port;
int channel;
/* rtp stream identity */
uint16_t orig_rtp_port;
/* pcm connection */
int pcm_bridge;
int rx_slot;
int tx_slot;
int rx_bank;
int tx_bank;
};
static struct osmo_fd server_ofd = { .fd = -1 };
static struct osmo_timer_list server_timer;
static struct conn *conn_list = NULL;
static struct channel *channel_list = NULL;
static void free_channel(struct channel *c)
{
struct channel **c_p;
c_p = &channel_list;
while (*c_p) {
if (*c_p == c)
break;
c_p = &((*c_p)->next);
}
*c_p = c->next;
free(c);
}
static void free_all_channels(void)
{
struct channel *c, *c2;
c = channel_list;
while (c) {
c2 = c;
c = c->next;
free(c2);
}
channel_list = NULL;
}
static void free_connection(struct conn *c)
{
struct conn **c_p;
c_p = &conn_list;
while (*c_p) {
if (*c_p == c)
break;
c_p = &((*c_p)->next);
}
*c_p = c->next;
free(c);
}
static void free_all_connections(void)
{
struct conn *c, *c2;
c = conn_list;
while (c) {
LOGP(DISDN, LOGL_INFO, "Free pending bridge connection.\n");
c2 = c;
osmo_fd_unregister(&c->ofd);
close(c->ofd.fd);
c = c->next;
free(c2);
}
conn_list = NULL;
}
static int bridge_socket_server_cb(struct osmo_fd *ofd, unsigned int when);
/* function to open the server socket */
static int open_server_socket(int daemon)
{
struct sockaddr_un sock_address;
int rc;
memset(&sock_address, 0, sizeof(sock_address));
sock_address.sun_family = AF_UNIX;
strcpy(sock_address.sun_path + 1, SOCKET_NAME);
rc = socket(PF_UNIX, SOCK_STREAM, 0);
if (rc < 0) {
LOGP(DISDN, LOGL_ERROR, "Failed to create UNIX socket.\n");
return rc;
}
server_ofd.fd = rc;
server_ofd.cb = bridge_socket_server_cb;
server_ofd.data = NULL;
server_ofd.when = OSMO_FD_READ;
osmo_fd_register(&server_ofd);
rc = bind(server_ofd.fd, (struct sockaddr *)(&sock_address), sizeof(struct sockaddr_un));
if (rc < 0) {
if (!daemon || errno != EADDRINUSE)
LOGP(DISDN, LOGL_INFO, "Failed to bind UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno));
osmo_fd_unregister(&server_ofd);
close(server_ofd.fd);
return -EADDRINUSE;
}
rc = listen(server_ofd.fd, 10);
if (rc < 0) {
LOGP(DISDN, LOGL_INFO, "Failed to listen to UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno));
osmo_fd_unregister(&server_ofd);
close(server_ofd.fd);
return rc;
}
return 0;
}
static int bridge_socket_conn_cb(struct osmo_fd *ofd, unsigned int when);
static int bridge_socket_server_cb(struct osmo_fd __attribute__((unused)) *ofd, unsigned int when)
{
struct sockaddr_un sock_address;
socklen_t sock_len = sizeof(sock_address);
struct conn *c;
int rc;
if ((when & OSMO_FD_READ)) {
rc = accept(server_ofd.fd, (struct sockaddr *)&sock_address, &sock_len);
if (rc > 0) {
LOGP(DISDN, LOGL_INFO, "Connection from bridge socket client.\n");
/* create connection */
c = calloc(1, sizeof(*c));
if (!c) {
LOGP(DISDN, LOGL_ERROR, "No mem!\n");
abort();
}
c->next = conn_list;
conn_list = c;
c->ofd.fd = rc;
c->ofd.cb = bridge_socket_conn_cb;
c->ofd.data = c;
c->ofd.when = OSMO_FD_READ;
osmo_fd_register(&c->ofd);
/* reset rx buffer */
c->rx_index = 0;
}
}
return 0;
}
static void rx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge);
/* close server socket and remove all channels that are associated with it */
void close_server_connection(struct osmo_fd *ofd)
{
struct channel *c;
/* safely remove all channels by calling rx_channel_info, which also removes bridges from remote channels */
do {
c = channel_list;
while (c) {
if (c->socket == ofd->fd)
break;
c = c->next;
}
if (c) {
c->socket = -1;
rx_channel_info(-1, c->card, c->port, c->channel, c->orig_rtp_port, 0);
}
} while (c);
osmo_fd_unregister(ofd);
close(ofd->fd);
}
/* read message from server socket */
static int bridge_socket_conn_cb(struct osmo_fd *ofd, unsigned int when)
{
struct conn *c = ofd->data;
int rc;
if ((when & OSMO_FD_READ)) {
rc = recv(c->ofd.fd, ((uint8_t *)&c->rx_msg) + c->rx_index, sizeof(c->rx_msg) - c->rx_index, 0);
if (rc > 0) {
c->rx_index += rc;
if (c->rx_index == sizeof(c->rx_msg)) {
LOGP(DISDN, LOGL_DEBUG, "Message from bridge socket client.\n");
/* different version ? */
if (c->rx_msg.version != SOCKET_VERSION) {
LOGP(DISDN, LOGL_ERROR, "Bridge client uses different version than bridge server. Please update all applications to use same server version\n");
return 0;
}
/* process message and reset buffer index */
rx_channel_info(c->ofd.fd, c->rx_msg.card, c->rx_msg.port, c->rx_msg.channel, c->rx_msg.orig_rtp_port, c->rx_msg.can_bridge);
c->rx_index = 0;
return 0;
}
} else if (rc == 0 || errno != EAGAIN) {
LOGP(DISDN, LOGL_DEBUG, "Close from bridge socket client.\n");
close_server_connection(&c->ofd);
/* destroy connection */
free_connection(c);
}
}
return 0;
}
static void sighandler(int sigset)
{
LOGP(DISDN, LOGL_DEBUG, "Signal %d received.\n", sigset);
}
static int quit = 0;
static void server_exit_timeout(void __attribute__((unused)) *data)
{
if (!conn_list) {
LOGP(DISDN, LOGL_DEBUG, "All clients gone, exitting.\n");
quit = 1;
return;
}
osmo_timer_schedule(&server_timer, 0,300000);
}
static void server_show_timeout(void __attribute__((unused)) *data)
{
struct channel *ch = channel_list;
if (ch)
printf("Connections:\n");
while (ch) {
printf("Card=%d Port=%d Channel=%d Bridge=%d\n", ch->card, ch->port, ch->channel, ch->pcm_bridge);
ch = ch->next;
}
osmo_timer_schedule(&server_timer, 1,0);
}
/* open socket, wait for connections and read incoming messages */
void bridge_socket_server_child(int slots, int daemon)
{
int rc;
num_slots = slots;
rc = open_server_socket(daemon);
if (rc < 0) {
if (rc == -EADDRINUSE)
LOGP(DISDN, LOGL_INFO, "Bridging socket is already running by a different instance.\n");
return;
}
LOGP(DISDN, LOGL_DEBUG, "Created bridging socket server.\n");
if (daemon) {
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
}
if (daemon) {
osmo_timer_setup(&server_timer, server_exit_timeout, NULL);
osmo_timer_schedule(&server_timer, 0,300000);
} else {
osmo_timer_setup(&server_timer, server_show_timeout, NULL);
osmo_timer_schedule(&server_timer, 1,0);
}
while(!quit) {
osmo_select_main(0);
}
osmo_timer_del(&server_timer);
free_all_channels();
free_all_connections();
osmo_fd_unregister(&server_ofd);
close(server_ofd.fd);
}
/* create socket server process and return 0 on success, return -errno on failure */
int bridge_socket_server(int slots)
{
pid_t pid;
pid = fork();
if (pid < 0) {
LOGP(DISDN, LOGL_ERROR, "fork() failed: errno=%d\n", errno);
return -EINVAL;
}
if (pid == 0) {
/* child */
pid = fork();
if (pid < 0) {
LOGP(DISDN, LOGL_ERROR, "fork() failed: errno=%d\n", errno);
exit(0);
}
if (pid == 0) {
/* grandchild */
bridge_socket_server_child(slots, 1);
}
/* exit child */
exit(0);
}
wait(NULL);
usleep(100000);
return 0;
}
void tx_bridge_order(int socket, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank);
/* receive channel info client */
static void rx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge)
{
struct channel *c, *o, *t;
/* first check if channel already exists */
c = channel_list;
while (c) {
if (c->card == card && c->port == port && c->channel == channel)
break;
c = c->next;
}
if (c) {
/* we are already can bridge */
if (can_bridge) {
LOGP(DISDN, LOGL_DEBUG, "There is no change in bridge, do nothing.\n");
return;
}
/* we must remove bridge, if remote channel exists */
o = channel_list;
while (o) {
if (o != c && o->orig_rtp_port == c->orig_rtp_port)
break;
o = o->next;
}
/* remote channel exists, so remove bridge */
if (o) {
if (o->pcm_bridge && o->socket > 0) {
LOGP(DISDN, LOGL_DEBUG, "We cannot bridge anymore, remove remote channel's bridge.\n");
tx_bridge_order(o->socket, o->card, o->port, o->channel, 0, 0, 0, 0, 0);
o->pcm_bridge = 0;
}
}
/* now we remove our channel */
if (c->pcm_bridge && c->socket > 0) {
LOGP(DISDN, LOGL_DEBUG, "We cannot bridge anymore, remove our bridge.\n");
tx_bridge_order(c->socket, c->card, c->port, c->channel, 0, 0, 0, 0, 0);
c->pcm_bridge = 0;
}
LOGP(DISDN, LOGL_DEBUG, "Freeing our channel.\n");
free_channel(c);
return;
}
/* if channel does not exists and we cannot bridge, do nothing */
if (!can_bridge) {
LOGP(DISDN, LOGL_DEBUG, "There is no change in bridge, do nothing.\n");
return;
}
/* channel does not exist, so we create it */
/* create connection */
LOGP(DISDN, LOGL_DEBUG, "Creating our channel.\n");
c = calloc(1, sizeof(*c));
if (!c) {
LOGP(DISDN, LOGL_ERROR, "No mem!\n");
abort();
}
c->next = channel_list;
channel_list = c;
c->socket = socket;
c->card = card;
c->port = port;
c->channel = channel;
c->orig_rtp_port = orig_rtp_port;
c->pcm_bridge = 0;
/* if there is a remote channel, select pcm slots and send bridge order */
o = channel_list;
while (o) {
if (o != c && o->orig_rtp_port == c->orig_rtp_port)
break;
o = o->next;
}
/* no other channel, so we do not need to send a bridge order */
if (!o) {
LOGP(DISDN, LOGL_DEBUG, "We can bridge, but there is no remote that can bridge (yet)..\n");
return;
}
LOGP(DISDN, LOGL_DEBUG, "We can bridge, and remote can bridge, so we assign time slots.\n");
/* make a list of allocated time slots */
uint8_t pcm_slot[num_slots];
memset(pcm_slot, 0, sizeof(pcm_slot));
t = channel_list;
while (t) {
if (t->pcm_bridge) {
pcm_slot[t->tx_slot] = 1;
pcm_slot[t->rx_slot] = 1;
}
t = t->next;
}
/* hunt for free slot */
if (c->card != o->card) {
int s;
/* different card, so we need one slot */
for (s = 0; s < num_slots; s++) {
if (!pcm_slot[s])
break;
}
if (s < num_slots) {
/* cross banks */
c->pcm_bridge = 1;
c->rx_slot = s;
c->tx_slot = s;
c->rx_bank = 0;
c->tx_bank = 1;
o->pcm_bridge = 1;
o->rx_slot = s;
o->tx_slot = s;
o->rx_bank = 1;
o->tx_bank = 0;
} else {
LOGP(DISDN, LOGL_NOTICE, "No single free slots, cannot set bridge.\n");
return;
}
} else {
int s, s1, s2;
/* same card, so we need two slot */
s1 = s2 = -1;
for (s = 0; s < num_slots; s++) {
if (!pcm_slot[s]) {
if (s1 < 0)
s1 = s;
else if (s2 < 0) {
s2 = s;
break;
}
}
}
if (s < num_slots) {
/* cross slots */
c->pcm_bridge = 1;
c->rx_slot = s1;
c->tx_slot = s2;
c->rx_bank = 0;
c->tx_bank = 0;
o->pcm_bridge = 1;
o->rx_slot = s2;
o->tx_slot = s1;
o->rx_bank = 0;
o->tx_bank = 0;
} else {
LOGP(DISDN, LOGL_NOTICE, "No two free slots, cannot set bridge.\n");
return;
}
}
/* bridge order to both ends */
tx_bridge_order(c->socket, c->card, c->port, c->channel, c->pcm_bridge, c->rx_slot, c->tx_slot, c->rx_bank, c->tx_bank);
tx_bridge_order(o->socket, o->card, o->port, o->channel, o->pcm_bridge, o->rx_slot, o->tx_slot, o->rx_bank, o->tx_bank);
}
/* send bridge order towards client */
void tx_bridge_order(int socket, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank)
{
struct msg_bridge_order msg;
int __attribute__((unused)) rc;
memset(&msg, 0, sizeof(msg));
msg.version = SOCKET_VERSION;
msg.card = card;
msg.port = port;
msg.channel = channel;
msg.pcm_bridge = pcm_bridge;
msg.rx_slot = rx_slot;
msg.tx_slot = tx_slot;
msg.rx_bank = rx_bank;
msg.tx_bank = tx_bank;
rc = send(socket, ((uint8_t *)&msg), sizeof(msg), 0);
}
/*
* client
*/
static struct osmo_fd client_ofd = { .fd = -1 };
int client_rx_index = 0;
struct msg_bridge_order client_rx_msg;
static int bridge_socket_client_cb(struct osmo_fd *ofd, unsigned int when);
/* function to open the client socket */
int bridge_socket_client(isdn_t *isdn_ep)
{
struct sockaddr_un sock_address;
int rc;
memset(&sock_address, 0, sizeof(sock_address));
sock_address.sun_family = AF_UNIX;
strcpy(sock_address.sun_path + 1, SOCKET_NAME);
rc = socket(PF_UNIX, SOCK_STREAM, 0);
if (rc < 0) {
LOGP(DISDN, LOGL_ERROR, "Failed to create UNIX socket.\n");
return rc;
}
client_ofd.fd = rc;
client_ofd.cb = bridge_socket_client_cb;
client_ofd.data = isdn_ep;
client_ofd.when = OSMO_FD_READ;
osmo_fd_register(&client_ofd);
rc = connect(client_ofd.fd, (struct sockaddr *)(&sock_address), sizeof(struct sockaddr_un));
if (rc < 0) {
LOGP(DISDN, LOGL_DEBUG, "Failed to connect to UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno));
osmo_fd_unregister(&client_ofd);
close(client_ofd.fd);
client_ofd.fd = -1;
return -errno;
}
LOGP(DISDN, LOGL_DEBUG, "Created bridging socket client.\n");
return 0;
}
void rx_bridge_order(isdn_t *isdn_ep, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank);
/* read message from client socket */
static int bridge_socket_client_cb(struct osmo_fd *ofd, unsigned int when)
{
isdn_t *isdn_ep = ofd->data;
int rc;
if ((when & OSMO_FD_READ)) {
/* read message until complete */
rc = recv(client_ofd.fd, ((uint8_t *)&client_rx_msg) + client_rx_index, sizeof(client_rx_msg) - client_rx_index, 0);
if (rc > 0) {
client_rx_index += rc;
if (client_rx_index == sizeof(client_rx_msg)) {
LOGP(DISDN, LOGL_DEBUG, "Message from bridge socket server.\n");
/* different version ? */
if (client_rx_msg.version != SOCKET_VERSION) {
LOGP(DISDN, LOGL_ERROR, "Bridge server uses different version than bridge client. Please update all applications to use same server version\n");
return 0;
}
/* process message and reset buffer index */
rx_bridge_order(isdn_ep, client_rx_msg.card, client_rx_msg.port, client_rx_msg.channel, client_rx_msg.pcm_bridge, client_rx_msg.rx_slot, client_rx_msg.tx_slot, client_rx_msg.rx_bank, client_rx_msg.tx_bank);
client_rx_index = 0;
}
} else if (rc == 0 || errno != EAGAIN) {
LOGP(DISDN, LOGL_DEBUG, "Close from bridge socket client.\n");
osmo_fd_unregister(&client_ofd);
close(client_ofd.fd);
client_ofd.fd = -1;
}
}
return 0;
}
/* check if ip address is one of the local interfaces */
int check_local_address(sa_family_t family, struct sockaddr *addr)
{
struct ifaddrs *ifaddr, *ifa;
int ret = 0;
int rc;
rc = getifaddrs(&ifaddr);
if (rc < 0) {
LOGP(DISDN, LOGL_ERROR, "Failed to read list of interface addresses.\n");
return 0;
}
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr)
continue;
if (family != ifa->ifa_addr->sa_family)
continue;
if (family == AF_INET) {
if (!memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
&((struct sockaddr_in *)addr)->sin_addr,
sizeof(struct in_addr))) {
ret = 1;
break;
}
}
if (family == AF_INET6) {
if (!memcmp(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,
&((struct sockaddr_in6 *)addr)->sin6_addr,
sizeof(struct in6_addr))) {
ret = 1;
break;
}
}
}
freeifaddrs(ifaddr);
return ret;
}
void rx_bridge_order(isdn_t *isdn_ep, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank)
{
call_t *call;
if (!isdn_ep->bridge_possible || isdn_ep->bridge_cardnum != card || isdn_ep->bridge_portnum != port) {
LOGP(DISDN, LOGL_ERROR, "Client received bridge order that does not match our port/port, please fix!\n");
}
/* hunt for call */
call = isdn_ep->call_list;
while (call) {
if (call->b_channel == channel)
break;
call = call->next;
}
if (!call) {
LOGP(DISDN, LOGL_DEBUG, "Client received bridge order that does not belong to an active call, ignoring!\n");
return;
}
if (pcm_bridge)
LOGP(DISDN, LOGL_DEBUG, "Client received bridge connect order to connect to PCM slots (%d,%d,%d,%d).\n", rx_slot, tx_slot, rx_bank, tx_bank);
else
LOGP(DISDN, LOGL_DEBUG, "Client received bridge disconnect order to disconnect to PCM slots.\n");
bchannel_bridge(call, pcm_bridge, rx_slot, tx_slot, rx_bank, tx_bank);
}
static void tx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge);
void bridge_socket_client_update(call_t *call, int enable)
{
struct osmo_cc_session_media *media;
struct sockaddr_storage sa;
struct sockaddr_in6 *sa6;
struct sockaddr_in *sa4;
sa_family_t family = 0; //make GCC happy
uint16_t orig_rtp_port = 0;
int can_bridge = 0;
int rc;
/* no client */
if (client_ofd.fd <= 0)
return;
LOGP(DISDN, LOGL_INFO, "We got called with enable=%d\n", enable);
/* do we have HFC card ? */
if (!call->isdn_ep->bridge_possible && enable) {
LOGP(DISDN, LOGL_INFO, "Our card is not an HFC card, cannot use hardware bridging.\n");
return;
}
if (!call->b_channel) {
LOGP(DISDN, LOGL_DEBUG, "Cannot check bridge, no bchannel currently assigned.\n");
return;
}
/* check if essential structures are set */
if (!call->cc_session || !call->cc_session->media_list) {
LOGP(DISDN, LOGL_DEBUG, "Codec/media not set, not ready for bridging.\n");
return;
}
media = call->cc_session->media_list;
/* check if codec negotiation is complete */
if (!media->description.port_local || (call->direction == DIRECTION_TERMINATOR && !media->description.port_remote)) {
LOGP(DISDN, LOGL_DEBUG, "Codec negotiation is not complete, cannot bridge yet.\n");
return;
}
if (call->direction == DIRECTION_TERMINATOR) {
/* is the remote interface equal to the one of our interfaces? */
switch (media->connection_data_remote.addrtype) {
case osmo_cc_session_addrtype_ipv4:
family = AF_INET;
memset(&sa, 0, sizeof(sa));
sa4 = (struct sockaddr_in *)&sa;
sa4->sin_family = family;
rc = inet_pton(AF_INET, media->connection_data_remote.address, &sa4->sin_addr);
if (rc < 1) {
LOGP(DISDN, LOGL_ERROR, "inet_pton failed, please fix!\n");
return;
}
break;
case osmo_cc_session_addrtype_ipv6:
family = AF_INET6;
memset(&sa, 0, sizeof(sa));
sa6 = (struct sockaddr_in6 *)&sa;
sa6->sin6_family = family;
rc = inet_pton(AF_INET6, media->connection_data_remote.address, &sa6->sin6_addr);
if (rc < 1) {
LOGP(DISDN, LOGL_ERROR, "inet_pton failed, please fix!\n");
return;
}
break;
case osmo_cc_session_addrtype_unknown:
LOGP(DISDN, LOGL_DEBUG, "Unsupported address type '%s'.\n", media->connection_data_remote.addrtype_name);
return;
}
rc = check_local_address(family, (struct sockaddr *)&sa);
if (rc < 1) {
LOGP(DISDN, LOGL_DEBUG, "Remote RTP peer is not on this machine, cannot use hardware bridging.\n");
return;
}
} else {
LOGP(DISDN, LOGL_DEBUG, "We are originator, we may use hardware bridging.\n");
}
/* check if 3PTY */
if (call->conference_3pty) {
LOGP(DISDN, LOGL_DEBUG, "Whe have 3PTY conference, cannot bridge while this is going on.\n");
goto send;
}
can_bridge = enable;
send:
/* only send on change */
if (can_bridge && !call->can_bridge) {
LOGP(DISDN, LOGL_INFO, "We tell the server we can do hardware bridging.\n");
} else
if (!can_bridge && call->can_bridge) {
LOGP(DISDN, LOGL_INFO, "We tell the server we cannot do hardware bridging anymore.\n");
} else
return;
call->can_bridge = can_bridge;
/* get port */
if (call->direction == DIRECTION_ORIGINATOR)
orig_rtp_port = media->description.port_local;
else
orig_rtp_port = media->description.port_remote;
tx_channel_info(client_ofd.fd, call->isdn_ep->bridge_cardnum, call->isdn_ep->bridge_portnum, call->b_channel, orig_rtp_port, can_bridge);
}
static void tx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge)
{
struct msg_channel_info msg;
int __attribute__((unused)) rc;
memset(&msg, 0, sizeof(msg));
msg.version = SOCKET_VERSION;
msg.card = card;
msg.port = port;
msg.channel = channel;
msg.orig_rtp_port = orig_rtp_port;
msg.can_bridge = can_bridge;
rc = send(socket, ((uint8_t *)&msg), sizeof(msg), 0);
}

7
src/isdn/bridge.h Normal file
View File

@ -0,0 +1,7 @@
int bridge_socket_server(int slots);
void bridge_socket_server_child(int slots, int daemon);
int bridge_socket_client(isdn_t *isdn_ep);
void bridge_socket_client_update(call_t *call, int enable);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,13 @@
#include <pthread.h>
#include "../libtimer/timer.h"
#include "../libosmocc/endpoint.h"
#include "../libosmocc/helper.h"
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
#include <osmocom/cc/endpoint.h>
#include <osmocom/cc/helper.h>
#include "../libsample/sample.h"
#include "../libjitter/jitter.h"
#include "../libph_socket/ph_socket.h"
#include "tones.h"
#define B_MODE_TRANSPARENT 0
#define B_MODE_HDLC 1
@ -23,6 +26,10 @@
#define TONES_TYPE_AMERICAN 1
#define TONES_TYPE_GERMAN 2
#define TONES_TYPE_OLDGERMAN 3
#define TONES_TYPE_MORSEGERMAN 4
#define DIRECTION_ORIGINATOR 0
#define DIRECTION_TERMINATOR 1
enum isdn_state {
ISDN_STATE_IDLE = 0, /* no call */
@ -60,6 +67,14 @@ struct select_channel {
int channel;
};
struct isdn;
struct isdn_b_sock {
int index;
struct isdn *isdn_ep;
struct osmo_fd ofd;
};
struct call_list;
typedef struct isdn {
@ -74,11 +89,10 @@ typedef struct isdn {
int serving_location; /* who we serve when sending causes towards interface */
const char *timeouts;
int tx_delay;
int tx_gain;
int rx_gain;
const char *pipeline;
int dtmf;
int local_tones;
/* metering/AOC-D/AOC-S */
int aocd;
int aocs;
/* osmo-cc */
struct osmo_cc_endpoint cc_ep;
@ -91,26 +105,40 @@ typedef struct isdn {
int pri;
int l1hold;
int l2hold;
int socket;
int l2sock;
int time_no_sec;
void *l2inst;
ph_socket_t *ph_socket;
pthread_mutex_t upqueue_lock;
struct mqueue upqueue;
int upqueue_initialized;
int upqueue_pipe[2];
struct osmo_fd upqueue_ofd;
struct mlayer3 *ml3;
int los, ais, rdi, slip_rx, slip_tx;
int l1link; /* current state */
int l2link; /* current state */
struct timer l2establish_timer;
struct osmo_timer_list l2establish_timer;
int b_num;
int b_reserved;
int b_mode[128];
int b_state[128];
int b_sock[128];
struct isdn_b_sock b_sock[128];
struct call_list *b_call[128];
struct timer b_timer[128];
struct osmo_timer_list b_timer[128];
struct b_timer_inst b_timer_inst[128];
uint8_t b_buffer[128][160];
int b_buffer_pos[128];
unsigned char l2mask[16]; /* 128 bits for each tei */
/* bridging */
int bridge_possible;
int bridge_cardnum;
int bridge_portnum;
/* clock for tone on hold */
struct osmo_timer_list clock_timer;
double last_time_clock;
} isdn_t;
typedef struct call_list {
@ -120,18 +148,9 @@ typedef struct call_list {
/* mISDN states */
uint32_t l3_pid;
uint16_t l3_ces;
int tx_gain;
int rx_gain;
int mute;
int txdata;
int tx_delay;
int echo;
int conf;
int tone;
int rxoff;
int dtmf;
int dtmf_threshold;
const char *pipeline;
/* tone states */
struct isdn_tone isdn_tone;
/* osmo-cc states */
uint32_t cc_callref;
@ -148,8 +167,12 @@ typedef struct call_list {
int b_exclusive;
int b_reserve;
int b_mode;
int b_transmitting;
double b_rx_time;
int data_call;
/* call states */
int direction; /* originator or terminator of call */
enum isdn_state state;
int any_dialing; /* if any digit was dialed, we track this for dial tone */
int channel_negotiated;
@ -163,13 +186,30 @@ typedef struct call_list {
int conference_3pty; /* if call is the active call in a 3pty conference */
int park_len;
uint8_t park_callid[8];
/* metering/AOC-D/AOC-S */
struct osmo_timer_list metering_unit_timer;
int metering_info_received;
struct timeval metering_unit_period;
uint16_t metering_connect_units;
uint16_t metering_total_units;
/* jitter buffer for 3pty call */
jitter_t dejitter;
/* bridge states */
int can_bridge; /* last state sent to the server */
int bridge_enabled; /* last state received by the server */
int bridge_bank_tx;
int bridge_slot_tx;
int bridge_bank_rx;
int bridge_slot_rx;
/* jitter buffer for tx and 3pty call */
jitter_t tx_dejitter;
uint16_t conf_rtp_sequence; /* use for tx_dejitter during conference */
uint32_t conf_rtp_timestamp;
jitter_t conf_dejitter;
} call_t;
int check_mISDN_dsp(void);
void init_law(char law);
/* channel selection */
int hunt_bchannel_in(isdn_t *isdn_ep, int channel, int exclusive);
@ -180,22 +220,22 @@ int open_bchannel_out(call_t *call, unsigned int cmd, int channel, int exclusive
/* isdn instance */
isdn_t *isdn_create(void);
void isdn_destroy(isdn_t *isdn_ep);
int isdn_initialize(isdn_t *isdn_ep, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int tx_gain, int rx_gain, const char *pipeline, int dtmf, int local_tones, int serving_location);
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs, int time_no_sec);
int isdn_open(isdn_t *isdn_ep);
void isdn_close(isdn_t *isdn_ep);
void isdn_add_msn(isdn_t *isdn_ep, const char *msn);
int isdn_dchannel_work(isdn_t *isdn_ep);
void isdn_bchannel_work(isdn_t *isdn_ep);
void isdn_rtp_work(isdn_t *isdn_ep);
/* call instance */
call_t *call_create(isdn_t *isdn_ep, int channel, int exclusive, int mode);
call_t *call_create(isdn_t *isdn_ep, int direction, int channel, int exclusive, int mode);
void call_create_jitter(call_t *call);
void call_destroy(call_t *call);
/* channel allocation and handling */
void bchannel_tone(call_t *call, int tone);
void bchannel_bridge(call_t *call, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank);
void bchannel_event(isdn_t *isdn_ep, int index, int event);
int seize_bchannel(call_t *call, int channel, int exclusive);
void drop_bchannel(call_t *call);
void bchannel_send(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len);
void rtp_receive(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
void enable_hold_clock(isdn_t *isdn_ep);

View File

@ -24,33 +24,42 @@
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "../libdebug/debug.h"
#include <sched.h>
#include <math.h>
#include "../liblogging/logging.h"
#include "../liboptions/options.h"
#include "../libg711/g711.h"
#include <osmocom/cc/g711.h>
#include <mISDN/mbuffer.h>
#include "isdn.h"
#include "dss1.h"
#include "bridge.h"
#include "../libmisdn/core.h"
#include "ph_driver.h"
isdn_t *isdn_ep = NULL;
int num_kanal = 1;
static char law = 'a';
static const char *portname = "0";
static const char *portname = NULL;
static int misdn_kernel = 0, misdn_user = 0;
static int ntmode = 0;
static int ptp = 0;
static int layer1hold = 0;
static int layer2hold = 0;
static int aocd = 0;
static int aocs = 0;
static int time_no_sec = 0;
static const char *channel_out = NULL;
static const char *channel_in = NULL;
static const char *timeouts = NULL;
static int tx_delay = 0;
static int tx_gain = 0;
static int rx_gain = 0;
static const char *pipeline = NULL;
static int dtmf = 1;
static int local_tones = 0;
static int debug_mISDN = 0;
static int serving_location = 1; /* private network serving local user */
static int use_hfc_bridging = 1;
static int bridging_server_only = 0;
static int pcm_slots = 32;
static int rt_prio = 1;
#define MAX_CC_ARGS 1024
static int cc_argc = 0;
static const char *cc_argv[MAX_CC_ARGS];
@ -68,11 +77,14 @@ static void print_help()
printf(" --config [~/]<path to config file>\n");
printf(" Give a config file to use. If it starts with '~/', path is at home dir.\n");
printf(" Each line in config file is one option, '-' or '--' must not be given!\n");
debug_print_help();
logging_print_help();
printf(" --ulaw\n");
printf(" Use U-LAW for b-channel coding instead of alaw.\n");
printf(" -p --port <portnr> | <portname>\n");
printf(" Number or name of misdn port (see misdn_info). (Default = %s)\n", portname);
printf(" Number or name of misdn port (see misdn_info).\n");
printf(" -s --socket <path>\n");
printf(" Path to UNIX socket that provides layer 1 connection to an ISDN\n");
printf(" interface.\n");
printf(" -n --nt\n");
printf(" The given port is configured as NT-mode, instead of TE-mode.\n");
printf(" -0 --ptp\n");
@ -92,11 +104,14 @@ static void print_help()
printf(" Channel selection list for all outgoing calls to the interface.\n");
printf(" A free channels is searched in order of appearance.\n");
printf(" force - Forces the selected port with no acceptable alternative.\n");
printf(" -> this will be automatically set for multipoint NT-mode ports\n");
printf(" -> this must be used for multipoint NT-mode ports\n");
printf(" <number>[,...] - List of channels to search.\n");
printf(" free - Select any free channel\n");
printf(" any - On outgoing calls, signal 'any channel acceptable'. (see DSS1)\n");
printf(" no - Signal 'no channel available' aka 'call waiting'. (see DSS1)\n");
printf(" Default for multipoint NT-Mode: 'force,free,no'\n");
printf(" Default for point-to-point NT-Mode: 'force,free'\n");
printf(" Default for TE-Mode: 'any'\n");
printf(" --channel-in [<number>][,...][,free]\n");
printf(" Give list of channels to select for calls from ISDN\n");
printf(" Channel selection list for all incoming calls from the interface.\n");
@ -104,6 +119,7 @@ static void print_help()
printf(" If any channel was requested, the first free channel found is selected.\n");
printf(" <number>[,...] - List of channels to accept.\n");
printf(" free - Accept any free channel\n");
printf(" Default: 'free'\n");
printf(" --timeouts <setup>,<overlap>,<proceeding>,<alerting>,<disconnect>\n");
printf(" Alter ISDN protocol times.\n");
@ -111,23 +127,26 @@ static void print_help()
printf(" --tx-delay <ms>\n");
printf(" Give a delay in milliseconds. This is required for modem/fax. Audio\n");
printf(" toward ISDN interface is buffered with the given delay.\n");
printf(" This feature turns off the dejittering.\n");
printf(" --tx-gain <dB>\n");
printf(" Changes gain of audio towards ISDN interface. Give Gain in steps of\n");
printf(" 6 dB. (-48 .. 48)\n");
printf(" --rx-gain <dB>\n");
printf(" Changes gain of audio coming from ISDN interface. Give Gain in steps\n");
printf(" of 6 dB. (-48 .. 48)\n");
printf(" --pipeline <string>\n");
printf(" mISDN allows to use echo cancellation modules. See mISDN documentation.\n");
printf(" --dtmf 1 | 0\n");
printf(" Turns DTMF detection on or off (default is %d).\n", dtmf);
printf(" -T --local-tones german | oldgerman | american\n");
printf(" This feature alters dejittering strategy.\n");
printf(" --aocd\n");
printf(" Send AOC-D charging information\n");
printf(" --aocs\n");
printf(" Send AOC-S charging information\n");
printf(" --time-no-seconds\n");
printf(" Send date/time IE without seconds\n");
printf(" -T --local-tones german | oldgerman | morsegerman | american\n");
printf(" Send locally generated tones, if not provided by remote interface.\n");
printf(" -D --debug-misdn\n");
printf(" Enables mISDN stack debugging.\n");
printf(" --serving-location (see Q.931)\n");
printf(" 0 = user, 1 = private network serving local user (default=%d)\n", serving_location);
printf(" -B --bridging 0 | 1\n");
printf(" Enable or disable hardware bridging with HFC cards. (default = %d)\n", use_hfc_bridging);
printf(" --pcm-slots 32 | 64 | 128\n");
printf(" The number of slots must match the configured PCM bus size.\n");
printf(" (default = %d)\n", pcm_slots);
printf(" -r --realtime <prio>\n");
printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio);
printf(" -C --cc \"<osmo-cc arg>\" [--cc ...]\n");
printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n");
}
@ -142,6 +161,11 @@ static void print_help()
#define OPT_PIPELINE 263
#define OPT_DTMF 264
#define OPT_SERVING 265
#define OPT_PCM_SLOTS 266
#define OPT_BR_ONLY 267
#define OPT_AOCD 268
#define OPT_AOCS 269
#define OPT_TIME_NO_SEC 270
static void add_options(void)
{
@ -149,6 +173,7 @@ static void add_options(void)
option_add('v', "verbose", 1);
option_add(OPT_ULAW, "ulaw", 0);
option_add('p', "port", 1);
option_add('s', "socket", 1);
option_add('n', "nt", 0);
option_add('0', "ptp", 0);
option_add('M', "msn", 1);
@ -158,13 +183,16 @@ static void add_options(void)
option_add(OPT_CHANNEL_IN, "channel-in", 1);
option_add(OPT_TIMEOUTS, "timeouts", 1);
option_add(OPT_TX_DELAY, "tx-delay", 1);
option_add(OPT_TX_GAIN, "tx-gain", 1);
option_add(OPT_RX_GAIN, "rx-gain", 1);
option_add(OPT_PIPELINE, "pipeline", 1);
option_add(OPT_DTMF, "dtmf", 1);
option_add('T', "local-tones", 1);
option_add(OPT_AOCD, "aocd", 0);
option_add(OPT_AOCS, "aocs", 0);
option_add(OPT_TIME_NO_SEC, "time-no-seconds", 0);
option_add('D', "debug-misdn", 0);
option_add(OPT_SERVING, "serving-location", 0);
option_add(OPT_SERVING, "serving-location", 1);
option_add('B', "bridging", 1);
option_add(OPT_PCM_SLOTS, "pcm-slots", 1);
option_add(OPT_BR_ONLY, "bridging-server-only", 0);
option_add('r', "realtime", 1);
option_add('C', "cc", 1);
}
@ -178,11 +206,9 @@ static int handle_options(int short_option, int argi, char **argv)
print_help();
return 0;
case 'v':
if (!strcasecmp(argv[argi], "list")) {
debug_list_cat();
rc = parse_logging_opt(argv[argi]);
if (rc > 0)
return 0;
}
rc = parse_debug_opt(argv[argi]);
if (rc < 0) {
fprintf(stderr, "Failed to parse debug option, please use -h for help.\n");
return rc;
@ -193,6 +219,11 @@ static int handle_options(int short_option, int argi, char **argv)
break;
case 'p':
portname = options_strdup(argv[argi]);
misdn_kernel = 1;
break;
case 's':
portname = options_strdup(argv[argi]);
misdn_user = 1;
break;
case 'n':
ntmode = 1;
@ -221,17 +252,14 @@ static int handle_options(int short_option, int argi, char **argv)
case OPT_TX_DELAY:
tx_delay = atoi(argv[argi]);
break;
case OPT_TX_GAIN:
tx_gain = atoi(argv[argi]) / 6;
case OPT_AOCD:
aocd = 1;
break;
case OPT_RX_GAIN:
rx_gain = atoi(argv[argi]) / 6;
case OPT_AOCS:
aocs = 1;
break;
case OPT_PIPELINE:
pipeline = options_strdup(argv[argi]);
break;
case OPT_DTMF:
dtmf = atoi(argv[argi]);
case OPT_TIME_NO_SEC:
time_no_sec = 1;
break;
case 'T':
if (!strcasecmp(argv[argi], "american"))
@ -240,6 +268,8 @@ static int handle_options(int short_option, int argi, char **argv)
local_tones = TONES_TYPE_GERMAN;
else if (!strcasecmp(argv[argi], "oldgerman"))
local_tones = TONES_TYPE_OLDGERMAN;
else if (!strcasecmp(argv[argi], "morsegerman"))
local_tones = TONES_TYPE_MORSEGERMAN;
else {
fprintf(stderr, "Invalid tones type given!\n");
return -EINVAL;
@ -251,6 +281,18 @@ static int handle_options(int short_option, int argi, char **argv)
case OPT_SERVING:
serving_location = atoi(argv[argi]);
break;
case 'B':
use_hfc_bridging = atoi(argv[argi]);
break;
case OPT_PCM_SLOTS:
pcm_slots = strtoul(argv[argi], NULL, 10);
break;
case OPT_BR_ONLY:
bridging_server_only = 1;
break;
case 'r':
rt_prio = atoi(argv[argi]);
break;
case 'C':
if (!strcasecmp(argv[argi], "help")) {
osmo_cc_help();
@ -281,13 +323,9 @@ void sighandler(int sigset)
static struct mi_ext_fn_s mi_fn;
static int mISDNlib_debug(const char *file, int line, const char *func, int __attribute__((unused)) level, const char *fmt, va_list va)
static int mISDNlib_debug(const char *file, int line, const char __attribute__((unused)) *func, int __attribute__((unused)) level, const char *fmt, va_list va)
{
char text[256];
vsnprintf(text, sizeof(text) - 1, fmt, va);
text[sizeof(text) - 1] = '\0';
// PDEBUG(DDSS1, DEBUG_NOTICE, "libmisdn (file %s, line %d, func %s(), level %d): %s\n", file, line, func, level, text);
_printdebug(file, func, line, DMISDN, DEBUG_NOTICE, NULL, "%s", text);
LOGPSRC(DMISDN, LOGL_NOTICE, file, line, fmt, va);
return 0;
}
@ -295,7 +333,14 @@ static int mISDNlib_debug(const char *file, int line, const char *func, int __at
int main(int argc, char *argv[])
{
int argi, rc;
int misdn_initialized = 0;
int ph_drv_initialized = 0;
int layer3_initialized = 0;
struct ph_socket_driver ph_drv;
const char *p;
struct osmo_timer_list mISDN_timer;
logging_init();
g711_init();
@ -314,47 +359,109 @@ int main(int argc, char *argv[])
if (argi <= 0)
return argi;
/* misdn init and debug */
rc = check_mISDN_dsp();
if (bridging_server_only) {
bridge_socket_server_child(pcm_slots, 1);
return 0;
}
/* start bridge server */
if (use_hfc_bridging) {
bridge_socket_server(pcm_slots);
bridge_socket_client(isdn_ep);
}
if (!misdn_kernel && !misdn_user) {
fprintf(stderr, "You defined no mISDN port or layer 1 socket. You must define either one of them! Use '-h' for help.\n");
goto error;
}
if (misdn_kernel && misdn_user) {
fprintf(stderr, "You defined mISDN port and layer 1 socket. You must define either one of them!\n");
goto error;
}
/* init user space mISDN */
if (misdn_user) {
rc = mISDNInit((debug_mISDN) ? 0xffffffff : 0);
if (rc)
goto error;
misdn_initialized = 1;
rc = init_ph_socket_driver(&ph_drv, isdn_ep, portname, 0, ntmode, (debug_mISDN) ? 0xffffffff : 0);
if (rc)
goto error;
ph_drv_initialized = 1;
}
/* mISDNuser init and debug */
mi_fn.prt_debug = mISDNlib_debug;
init_layer3(4, &mi_fn);
init_layer3(4, &mi_fn, (misdn_user) ? 1 : 0);
layer3_initialized = 1;
mISDN_set_debug_level((debug_mISDN) ? 0xfffffeff : 0);
rc = isdn_initialize(isdn_ep, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, tx_gain, rx_gain, pipeline, dtmf, local_tones, serving_location);
/* change tones to ulaw */
if (law == 'u')
isdn_tone_generate_ulaw_samples();
/* init mix and conceal tables */
init_law(law);
/* init instance */
rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location, aocd, aocs, time_no_sec);
if (rc) {
PDEBUG(DISDN, DEBUG_ERROR, "mISDN initializing failed!\n");
LOGP(DISDN, LOGL_ERROR, "mISDN initializing failed!\n");
goto error;
}
rc = isdn_open(isdn_ep);
if (rc) {
PDEBUG(DISDN, DEBUG_ERROR, "mISDN open failed!\n");
LOGP(DISDN, LOGL_ERROR, "mISDN open failed!\n");
goto error;
}
while ((p = strchr(portname, '/')))
portname = p + 1;
rc = osmo_cc_new(&isdn_ep->cc_ep, OSMO_CC_VERSION, isdn_ep->portname, serving_location, cc_message, NULL, isdn_ep, cc_argc, cc_argv);
if (rc < 0)
goto error;
/* Dummy timer to interrupt osmo_select_main when mISDN timer fires before anything else. */
osmo_timer_setup(&mISDN_timer, NULL, NULL);
/* real time priority */
if (rt_prio > 0) {
struct sched_param schedp;
int rc;
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = rt_prio;
rc = sched_setscheduler(0, SCHED_RR, &schedp);
if (rc)
fprintf(stderr, "Error setting SCHED_RR with prio %d\n", rt_prio);
}
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
while (!quit) {
int w;
process_timer();
isdn_bchannel_work(isdn_ep);
isdn_rtp_work(isdn_ep);
int work;
double misdn_timeout = 0.0;
do {
w = 0;
w |= osmo_cc_handle();
w |= isdn_dchannel_work(isdn_ep);
} while (w);
usleep(1000);
work = 0;
work |= osmo_cc_handle();
if (misdn_user) {
/* run workers of mISDN stacks in user space */
work |= mISDN_work();
work |= work_layer3(isdn_ep->ml3, &misdn_timeout);
}
} while (work);
/* wait for event or stop, if mISDN_timer fires */
if (misdn_timeout)
osmo_timer_schedule(&mISDN_timer, floor(misdn_timeout), (misdn_timeout - floor(misdn_timeout)) * 1000000);
osmo_select_main(0);
osmo_timer_del(&mISDN_timer);
}
signal(SIGINT, SIG_DFL);
@ -363,6 +470,15 @@ int main(int argc, char *argv[])
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
/* reset real time prio */
if (rt_prio > 0) {
struct sched_param schedp;
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &schedp);
}
error:
if (isdn_ep) {
osmo_cc_delete(&isdn_ep->cc_ep);
@ -372,6 +488,12 @@ error:
if (layer3_initialized)
cleanup_layer3();
if (ph_drv_initialized)
exit_ph_socket_driver(&ph_drv);
if (misdn_initialized)
mISDN_cleanup();
options_free();
return 0;

325
src/isdn/ph_driver.c Normal file
View File

@ -0,0 +1,325 @@
/* ph-socket driver for user space mISDN
*
* (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
* 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 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 <stdio.h>
#include <stdint.h>
#include <time.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
#include "../libph_socket/ph_socket.h"
#include "ph_driver.h"
#define __MISDNL1L2__
#include "../libmisdn/mISDNhw.h"
static inline u_int
get_sapi_tei(u_char *p)
{
u_int sapi, tei;
sapi = *p >> 2;
tei = p[1] >> 1;
return sapi | (tei << 8);
}
/* message from mISDN stack to PH-socket */
static int d_msg_down(struct mISDNchannel *ch, struct sk_buff *skb)
{
struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
struct dchannel *dch = container_of(dev, struct dchannel, dev);
struct ph_socket_driver *drv = dch->hw;
struct mISDNhead *hh = mISDN_HEAD_P(skb);
switch (hh->prim) {
case PH_DATA_REQ:
printk(KERN_DEBUG "PH-DATA-REQ to interface (channel=%d, len=%d)\n", drv->dch->slot, skb->len);
ph_socket_tx_msg(&drv->ph_socket, drv->dch->slot, PH_PRIM_DATA_REQ, skb->data, skb->len);
skb_trim(skb, 0);
printk(KERN_DEBUG "PH-DATA-CNF from interface\n");
queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
skb = NULL;
break;
case PH_ACTIVATE_REQ:
printk(KERN_DEBUG "PH-ACTIVATE_REQ to interface\n");
if (drv->enabled)
ph_socket_tx_msg(&drv->ph_socket, drv->dch->slot, PH_PRIM_ACT_REQ, NULL, 0);
else
_queue_data(&drv->dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
break;
case PH_DEACTIVATE_REQ:
printk(KERN_DEBUG "PH-DEACTIVATE_REQ to interface\n");
ph_socket_tx_msg(&drv->ph_socket, drv->dch->slot, PH_PRIM_DACT_REQ, NULL, 0);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/* open sub function of d_ctrl */
static int open_dchannel(struct ph_socket_driver __attribute__((unused)) *drv, struct dchannel *dch, struct channel_req *rq)
{
if (dch->debug & DEBUG_HW_OPEN)
printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
dch->dev.id, __builtin_return_address(0));
if (rq->protocol == ISDN_P_NONE)
return -EINVAL;
if ((dch->dev.D.protocol != ISDN_P_NONE) &&
(dch->dev.D.protocol != rq->protocol)) {
if (dch->debug & DEBUG_HW_OPEN)
printk(KERN_WARNING "%s: change protocol %x to %x\n",
__func__, dch->dev.D.protocol, rq->protocol);
}
if (dch->dev.D.protocol != rq->protocol)
dch->dev.D.protocol = rq->protocol;
if (test_bit(FLG_ACTIVE, &dch->Flags)) {
_queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
0, NULL, GFP_KERNEL);
}
rq->ch = &dch->dev.D;
return 0;
}
/* channel sub function of d_ctrl */
static int channel_dctrl(struct dchannel __attribute__((unused)) *dch, struct mISDN_ctrl_req *cq)
{
int ret = 0;
switch (cq->op) {
default:
printk(KERN_WARNING "%s: unknown Op %x\n",
__func__, cq->op);
ret = -EINVAL;
break;
}
return ret;
}
/* control from mISDN stack to this driver */
static int d_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
{
struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
struct dchannel *dch = container_of(dev, struct dchannel, dev);
struct ph_socket_driver *drv = dch->hw;
struct channel_req *rq;
int err = 0;
if (dch->debug & DEBUG_HW)
printk(KERN_DEBUG "%s: cmd:%x %p\n",
__func__, cmd, arg);
switch (cmd) {
case OPEN_CHANNEL:
rq = arg;
switch (rq->protocol) {
case ISDN_P_TE_S0:
case ISDN_P_NT_S0:
if (drv->pri) {
err = -EINVAL;
break;
}
err = open_dchannel(drv, dch, rq);
break;
case ISDN_P_TE_E1:
case ISDN_P_NT_E1:
if (!drv->pri) {
err = -EINVAL;
break;
}
err = open_dchannel(drv, dch, rq);
break;
default:
;
//err = open_bchannel(hc, dch, rq);
}
break;
case CLOSE_CHANNEL:
if (dch->debug & DEBUG_HW_OPEN)
printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
__func__, dch->dev.id,
__builtin_return_address(0));
break;
case CONTROL_CHANNEL:
err = channel_dctrl(dch, arg);
break;
default:
if (dch->debug & DEBUG_HW)
printk(KERN_DEBUG "%s: unknown command %x\n",
__func__, cmd);
err = -EINVAL;
}
return err;
}
static void ph_socket_rx_msg(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length);
/* init instance of PH-socket driver */
int init_ph_socket_driver(struct ph_socket_driver *drv, void *priv, const char *socket_name, int pri, int nt, uint32_t debug)
{
int rc = 0;
struct dchannel *dch;
memset(drv, 0, sizeof(*drv));
drv->priv = priv;
drv->pri = pri;
drv->nt = nt;
/* allocate dchannel structure */
dch = kzalloc(sizeof(*dch), GFP_KERNEL);
if (!dch) {
rc = -ENOMEM;
goto error;
}
/* populate dchannel structure */
dch->debug = debug;
mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, NULL);
dch->hw = drv;
if (pri) {
if (nt)
dch->dev.Dprotocols = (1 << ISDN_P_NT_E1);
else
dch->dev.Dprotocols = (1 << ISDN_P_TE_E1);
dch->dev.nrbchan = 30;
} else {
if (nt)
dch->dev.Dprotocols = (1 << ISDN_P_NT_S0);
else
dch->dev.Dprotocols = (1 << ISDN_P_TE_S0);
dch->dev.nrbchan = 2;
}
dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
dch->dev.D.send = d_msg_down;
dch->dev.D.ctrl = d_ctrl;
dch->slot = (pri) ? 16 : 3;
dch->dev.nrbchan = 0;
/* register dchannel to mISDN */
rc = mISDN_register_device(&dch->dev, NULL, socket_name);
if (rc) {
goto error;
}
/* link */
drv->dch = dch;
/* socket client, must be initialized here, because driver will receive PH_PRIM_CTRL_IND message during init */
rc = ph_socket_init(&drv->ph_socket, ph_socket_rx_msg, drv, socket_name, 0);
if (rc < 0)
goto error;
return 0;
error:
if (dch) {
kfree(dch);
dch = NULL;
}
exit_ph_socket_driver(drv);
return rc;
}
/* destroy instance of PH-socket driver */
void exit_ph_socket_driver(struct ph_socket_driver *drv)
{
/* unregister dchannel structure and free */
if (drv->dch) {
mISDN_unregister_device(&drv->dch->dev);
mISDN_freedchannel(drv->dch);
kfree(drv->dch);
drv->dch = NULL;
}
/* close socket */
ph_socket_exit(&drv->ph_socket);
}
/* message from PH-socket to mISDN */
static void ph_socket_rx_msg(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length)
{
struct ph_socket_driver *drv = (struct ph_socket_driver *)s->priv;
/* stack not complete */
if (!drv || !drv->dch)
return;
switch (prim) {
case PH_PRIM_DATA_IND:
if (drv->dch->slot == channel) {
printk(KERN_DEBUG "PH-DATA-IND from interface (channel %d, len=%d)\n", channel, length);
if (length < 2) {
printk(KERN_ERR "%s: Message too short!\n", __func__);
break;
}
_queue_data(&drv->dch->dev.D, PH_DATA_IND, get_sapi_tei(data), length, data, GFP_ATOMIC);
break;
}
bchannel_ph_sock_receive(drv->priv, channel, prim, data, length);
break;
case PH_PRIM_ACT_IND:
if (drv->dch->slot == channel) {
printk(KERN_DEBUG "PH-ACTIVATE-IND from interface (dchannel)\n");
drv->activated = 1;
_queue_data(&drv->dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
break;
}
printk(KERN_DEBUG "PH-ACTIVATE-IND from interface (bchannel)\n");
bchannel_ph_sock_receive(drv->priv, channel, prim, data, length);
break;
case PH_PRIM_DACT_IND:
if (drv->dch->slot == channel) {
printk(KERN_DEBUG "PH-DEACTIVATE-IND from interface (dchannel)\n");
drv->activated = 0;
_queue_data(&drv->dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
break;
}
printk(KERN_DEBUG "PH-DEACTIVATE-IND from interface (bchannel)\n");
bchannel_ph_sock_receive(drv->priv, channel, prim, data, length);
break;
case PH_PRIM_CTRL_IND:
if (length >= 1) {
switch (data[0]) {
case PH_CTRL_UNBLOCK:
printk(KERN_DEBUG "PH-SOCKET Interface available\n");
drv->enabled = 1;
break;
case PH_CTRL_BLOCK:
printk(KERN_DEBUG "PH-SOCKET Interface unavailable\n");
drv->enabled = 0;
if (drv->activated) {
drv->activated = 0;
_queue_data(&drv->dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
}
break;
}
}
break;
case PH_PRIM_DATA_CNF:
break;
default:
printk(KERN_ERR "%s: Rejecting unknown message 0x%02x from PH-socket!\n", __func__, prim);
}
}

14
src/isdn/ph_driver.h Normal file
View File

@ -0,0 +1,14 @@
struct ph_socket_driver {
void *priv;
int pri, nt;
ph_socket_t ph_socket;
struct dchannel *dch;
int enabled, activated;
};
int init_ph_socket_driver(struct ph_socket_driver *drv, void *priv, const char *socket_name, int pri, int nt, uint32_t debug);
void exit_ph_socket_driver(struct ph_socket_driver *drv);
void bchannel_ph_sock_receive(void *priv, int channel, uint8_t prim, uint8_t *data, int length);

470
src/isdn/tones.c Normal file
View File

@ -0,0 +1,470 @@
/*
* Tone generation
*
* Copyright Andreas Eversberg (jolly@eversberg.eu)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stddef.h>
#include "../liblogging/logging.h"
#include <osmocom/cc/g711.h>
#include <mISDN/mbuffer.h>
#include "tones.h"
#define DATA_S sample_silence
#define SIZE_S (&sizeof_silence)
#define DATA_GA sample_german_all
#define SIZE_GA (&sizeof_german_all)
#define DATA_GO sample_german_old
#define SIZE_GO (&sizeof_german_old)
#define DATA_DT sample_american_dialtone
#define SIZE_DT (&sizeof_american_dialtone)
#define DATA_RI sample_american_ringing
#define SIZE_RI (&sizeof_american_ringing)
#define DATA_BU sample_american_busy
#define SIZE_BU (&sizeof_american_busy)
#define DATA_S1 sample_special1
#define SIZE_S1 (&sizeof_special1)
#define DATA_S2 sample_special2
#define SIZE_S2 (&sizeof_special2)
#define DATA_S3 sample_special3
#define SIZE_S3 (&sizeof_special3)
/***************/
/* tones loops */
/***************/
/* all tones are alaw encoded */
/* the last sample+1 is in phase with the first sample. the error is low */
static uint8_t sample_german_all[] = {
0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
0xdc, 0xfc, 0x6c,
0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
0xdc, 0xfc, 0x6c,
0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
0xdc, 0xfc, 0x6c,
0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
0xdc, 0xfc, 0x6c,
};
static int sizeof_german_all = sizeof(sample_german_all);
static uint8_t sample_german_old[] = {
0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
0x8c,
0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
0x8c,
0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
0x8c,
0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
0x8c,
};
static int sizeof_german_old = sizeof(sample_german_old);
static uint8_t sample_american_dialtone[] = {
0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c,
0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d,
0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0,
0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67,
0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67,
0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef,
0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8,
0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61,
0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e,
0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30,
0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d,
0x6d, 0x91, 0x19,
};
static int sizeof_american_dialtone = sizeof(sample_american_dialtone);
static uint8_t sample_american_ringing[] = {
0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90,
0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed,
0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c,
0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d,
0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec,
0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11,
0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00,
0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39,
0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6,
0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3,
0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b,
0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f,
0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56,
0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59,
0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30,
0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d,
0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c,
0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd,
0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc,
0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d,
0x4d, 0xbd, 0x0d, 0xad, 0xe1,
};
static int sizeof_american_ringing = sizeof(sample_american_ringing);
static uint8_t sample_american_busy[] = {
0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66,
0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96,
0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57,
0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f,
0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40,
0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d,
0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c,
0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d,
0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40,
0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7,
0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a,
0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7,
0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40,
0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d,
0x4d, 0x4d, 0x6d, 0x01,
};
static int sizeof_american_busy = sizeof(sample_american_busy);
static uint8_t sample_special1[] = {
0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d,
0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd,
0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd,
0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd,
0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed,
0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41,
0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7,
0x6d, 0xbd, 0x2d,
};
static int sizeof_special1 = sizeof(sample_special1);
static uint8_t sample_special2[] = {
0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
};
static int sizeof_special2 = sizeof(sample_special2);
static uint8_t sample_special3[] = {
0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
};
static int sizeof_special3 = sizeof(sample_special3);
static uint8_t sample_silence[] = {
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
};
static int sizeof_silence = sizeof(sample_silence);
struct tones_samples {
int *len;
uint8_t *data;
};
static struct
tones_samples samples[] = {
{&sizeof_german_all, sample_german_all},
{&sizeof_german_old, sample_german_old},
{&sizeof_american_dialtone, sample_american_dialtone},
{&sizeof_american_ringing, sample_american_ringing},
{&sizeof_american_busy, sample_american_busy},
{&sizeof_special1, sample_special1},
{&sizeof_special2, sample_special2},
{&sizeof_special3, sample_special3},
{NULL, NULL},
};
/***********************************
* generate ulaw from alaw samples *
***********************************/
void isdn_tone_generate_ulaw_samples(void)
{
int i;
int16_t *audio;
int audio_len;
uint8_t *data;
int len;
i = 0;
while (samples[i].len) {
g711_decode_alaw_flipped(samples[i].data, *samples[i].len, (uint8_t **)&audio, &audio_len, NULL);
g711_encode_ulaw_flipped((uint8_t *)audio, audio_len, &data, &len, NULL);
memcpy(samples[i].data, data, len);
free(audio);
free(data);
i++;
}
}
/****************************
* tone sequence definition *
****************************/
static struct pattern {
int tone;
uint8_t *data[10];
int *siz[10];
int seq[10];
} pattern[] = {
{TONE_GERMAN_DIALTONE,
{DATA_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDDIALTONE,
{DATA_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{7956, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_MORSEDIALTONE,
{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{1600, 2400, 5600, 6400, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_DIALTONE,
{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8008, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_DIALPBX,
{DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL,
NULL},
{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL,
NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_GERMAN_OLDDIALPBX,
{DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL,
NULL},
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL,
NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_AMERICAN_DIALPBX,
{DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, NULL, NULL, NULL,
NULL},
{SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, NULL, NULL, NULL,
NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_GERMAN_RINGING,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDRINGING,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_RINGING,
{DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_RINGPBX,
{DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDRINGPBX,
{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_RINGPBX,
{DATA_RI, DATA_S, DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_BUSY,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{3840, 3840, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDBUSY,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 3800, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_BUSY,
{DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_BU, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_HANGUP,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{3840, 3840, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDHANGUP,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 3800, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_HANGUP,
{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_SPECIAL_INFO,
{DATA_S1, DATA_S2, DATA_S3, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_GASSENBESETZT,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_AUFSCHALTTON,
{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} },
{0,
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};
/******************
* copy tone data *
******************/
/* the count will be changed and may begin from 0 each pattern period.
* the clue is to precalculate the pointers and legths to use only one
* memcpy per function call, or two memcpy if the tone sequence changes.
*
* pattern - the type of the pattern
* count - the sample from the beginning of the pattern (phase)
* len - the number of bytes
*/
void isdn_tone_copy(struct isdn_tone *t, uint8_t *data, int len)
{
int index, count, start, num;
struct pattern *pat;
/* if we have no tone, we do not overwrite data */
if (!t->tone)
return;
/* process pattern */
pat = (struct pattern *)t->pattern;
/* points to the current pattern */
index = t->index; /* gives current sequence index */
count = t->count; /* gives current sample */
/* copy sample */
while (len) {
/* find sample to start with */
while (42) {
/* wrap around */
if (!pat->seq[index]) {
count = 0;
index = 0;
}
/* check if we are currently playing this tone */
if (count < pat->seq[index])
break;
count -= pat->seq[index];
index++;
}
/* calculate start and number of samples */
start = count % (*(pat->siz[index]));
num = len;
if (num + count > pat->seq[index])
num = pat->seq[index] - count;
if (num + start > (*(pat->siz[index])))
num = (*(pat->siz[index])) - start;
/* copy memory */
memcpy(data, pat->data[index] + start, num);
/* reduce length */
data += num;
count += num;
len -= num;
}
t->index = index;
t->count = count;
}
/********************
* set/release tone *
********************/
int isdn_tone_set(struct isdn_tone *t, int tone)
{
struct pattern *pat;
int i;
/* we turn off the tone */
if (!tone) {
t->tone = 0;
return 0;
}
pat = NULL;
i = 0;
while (pattern[i].tone) {
if (pattern[i].tone == tone) {
pat = &pattern[i];
break;
}
i++;
}
if (!pat) {
LOGP(DISDN, LOGL_ERROR, "given tone 0x%x is invalid\n", tone);
return -EINVAL;
}
LOGP(DISDN, LOGL_DEBUG, "playing given tone 0x%x\n", tone);
t->tone = tone;
t->pattern = pat;
t->index = 0;
t->count = 0;
return 0;
}

13
src/isdn/tones.h Normal file
View File

@ -0,0 +1,13 @@
struct isdn_tone {
int tone;
void *pattern;
int count;
int index;
};
void isdn_tone_generate_ulaw_samples(void);
void isdn_tone_copy(struct isdn_tone *t, uint8_t *data, int len);
int isdn_tone_set(struct isdn_tone *t, int tone);

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libdebug.a
libdebug_a_SOURCES = \
debug.c

View File

@ -1,313 +0,0 @@
/* Simple debug functions for level and category filtering
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* 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 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 <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "debug.h"
const char *debug_level[] = {
"debug ",
"info ",
"notice ",
"error ",
NULL,
};
struct debug_cat {
const char *name;
const char *color;
} debug_cat[] = {
{ "options", "\033[0;33m" },
{ "sender", "\033[1;33m" },
{ "sound", "\033[0;35m" },
{ "dsp", "\033[0;31m" },
{ "anetz", "\033[1;34m" },
{ "bnetz", "\033[1;34m" },
{ "cnetz", "\033[1;34m" },
{ "nmt", "\033[1;34m" },
{ "amps", "\033[1;34m" },
{ "r2000", "\033[1;34m" },
{ "imts", "\033[1;34m" },
{ "mpt1327", "\033[1;34m" },
{ "jollycom", "\033[1;34m" },
{ "eurosignal", "\033[1;34m" },
{ "pocsag", "\033[1;34m" },
{ "frame", "\033[0;36m" },
{ "call", "\033[0;37m" },
{ "cc", "\033[1;32m" },
{ "database", "\033[0;33m" },
{ "transaction", "\033[0;32m" },
{ "dms", "\033[0;33m" },
{ "sms", "\033[1;37m" },
{ "sdr", "\033[1;31m" },
{ "uhd", "\033[1;35m" },
{ "soapy", "\033[1;35m" },
{ "wave", "\033[1;33m" },
{ "radio", "\033[1;34m" },
{ "am791x", "\033[0;31m" },
{ "uart", "\033[0;32m" },
{ "device", "\033[0;33m" },
{ "datenklo", "\033[1;34m" },
{ "zeit", "\033[1;34m" },
{ "sim layer 1", "\033[0;31m" },
{ "sim layer 2", "\033[0;33m" },
{ "sim ICL layer", "\033[0;36m" },
{ "sim layer 7", "\033[0;37m" },
{ "mtp layer 2", "\033[1;33m" },
{ "mtp layer 3", "\033[1;36m" },
{ "MuP", "\033[1;37m" },
{ "router", "\033[1;35m" },
{ "stderr", "\033[1;37m" },
{ "ss5", "\033[1;34m" },
{ "isdn", "\033[1;35m" },
{ "misdn", "\033[0;34m" },
{ "dss1", "\033[1;34m" },
{ "sip", "\033[1;35m" },
{ "telephone", "\033[1;34m" },
{ NULL, NULL }
};
int debuglevel = DEBUG_INFO;
int debug_date = 0;
uint64_t debug_mask = ~0;
extern int num_kanal;
void (*clear_console_text)(void) = NULL;
void (*print_console_text)(void) = NULL;
int debug_limit_scroll = 0;
static int lock_initialized = 0;
static pthread_mutex_t debug_mutex;
void get_win_size(int *w, int *h)
{
struct winsize win;
int rc;
rc = ioctl(0, TIOCGWINSZ, &win);
if (rc) {
*w = 80;
*h = 25;
return;
}
*h = win.ws_row;
*w = win.ws_col;
}
void _printdebug(const char *file, const char __attribute__((unused)) *function, int line, int cat, int level, const char *kanal, const char *fmt, ...)
{
char buffer[4096], *b = buffer;
int s = sizeof(buffer) - 1;
const char *p;
va_list args;
int w, h;
int rc;
if (debuglevel > level)
return;
if (!(debug_mask & ((uint64_t)1 << cat)))
return;
if (!lock_initialized) {
rc = pthread_mutex_init(&debug_mutex, NULL);
if (rc == 0)
lock_initialized = 1;
}
if (lock_initialized)
pthread_mutex_lock(&debug_mutex);
buffer[sizeof(buffer) - 1] = '\0';
/* if kanal is used, prefix the channel number */
if (num_kanal > 1 && kanal) {
sprintf(buffer, "(chan %s) ", kanal);
b = strchr(buffer, '\0');
s -= strlen(buffer);
}
va_start(args, fmt);
vsnprintf(b, s, fmt, args);
va_end(args);
while ((p = strchr(file, '/')))
file = p + 1;
if (clear_console_text)
clear_console_text();
if (debug_limit_scroll) {
get_win_size(&w, &h);
printf("\0337\033[%d;%dr\0338", debug_limit_scroll + 1, h);
}
if (debug_date) {
struct timeval tv;
struct tm *tm;
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 10000.0));
}
printf("%s%s:%4d %s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_level[level], buffer);
if (debug_limit_scroll)
printf("\0337\033[%d;%dr\0338", 1, h);
if (print_console_text)
print_console_text();
fflush(stdout);
if (lock_initialized)
pthread_mutex_unlock(&debug_mutex);
}
const char *debug_amplitude(double level)
{
static char text[42];
strcpy(text, " : ");
if (level > 1.0)
level = 1.0;
if (level < -1.0)
level = -1.0;
text[20 + (int)(level * 20)] = '*';
return text;
}
#define level2db(level) (20 * log10(level))
const char *debug_db(double level_db)
{
static char text[128];
int l;
strcpy(text, ": . : . : . : . : . : . : . : . | . : . : . : . : . : . : . : . :");
if (level_db <= 0.0)
return text;
l = (int)round(level2db(level_db));
if (l > 48)
return text;
if (l < -48)
return text;
text[l + 48] = '*';
return text;
}
void debug_print_help(void)
{
printf(" -v --verbose <level> | <level>,<category>[,<category>[,...]] | list\n");
printf(" Use 'list' to get a list of all levels and categories\n");
printf(" Verbose level: digit of debug level (default = '%d')\n", debuglevel);
printf(" Verbose level+category: level digit followed by one or more categories\n");
printf(" -> If no category is specified, all categories are selected\n");
printf(" -v --verbose date\n");
printf(" Show date with debug output\n");
}
void debug_list_cat(void)
{
int i;
printf("Give number of debug level:\n");
for (i = 0; debug_level[i]; i++)
printf(" %d = %s\n", i, debug_level[i]);
printf("\n");
printf("Give name(s) of debug category:\n");
for (i = 0; debug_cat[i].name; i++)
printf(" %s%s\033[0;39m\n", debug_cat[i].color, debug_cat[i].name);
printf("\n");
}
int parse_debug_opt(const char *optarg)
{
int i, max_level = 0;
char *dup, *dstring, *p;
if (!strcasecmp(optarg, "date")) {
debug_date = 1;
return 0;
}
for (i = 0; debug_level[i]; i++)
max_level = i;
dup = dstring = strdup(optarg);
p = strsep(&dstring, ",");
for (i = 0; i < p[i]; i++) {
if (p[i] < '0' || p[i] > '9') {
fprintf(stderr, "Only digits are allowed for debug level!\n");
free(dup);
return -EINVAL;
}
}
debuglevel = atoi(p);
if (debuglevel > max_level) {
fprintf(stderr, "Debug level too high, use 'list' to show available levels!\n");
free(dup);
return -EINVAL;
}
if (dstring)
debug_mask = 0;
while((p = strsep(&dstring, ","))) {
for (i = 0; debug_cat[i].name; i++) {
if (!strcasecmp(p, debug_cat[i].name))
break;
}
if (!debug_cat[i].name) {
fprintf(stderr, "Given debug category '%s' unknown, use 'list' to show available categories!\n", p);
free(dup);
return -EINVAL;
}
debug_mask |= ((uint64_t)1 << i);
}
free(dup);
return 0;
}
const char *debug_hex(const uint8_t *data, int len)
{
static char *text = NULL;
char *p;
int i;
if (text)
free(text);
p = text = calloc(1, len * 3 + 1);
for (i = 0; i < len; i++) {
sprintf(p, "%02x ", *data++);
p += 3;
}
if (text[0])
p[-1] = '\0';
return text;
}

View File

@ -1,76 +0,0 @@
#define DEBUG_DEBUG 0 /* debug info, not for normal use */
#define DEBUG_INFO 1 /* all info about process */
#define DEBUG_NOTICE 2 /* something unexpected happens */
#define DEBUG_ERROR 3 /* there is an error with this software */
#define DOPTIONS 0
#define DSENDER 1
#define DSOUND 2
#define DDSP 3
#define DANETZ 4
#define DBNETZ 5
#define DCNETZ 6
#define DNMT 7
#define DAMPS 8
#define DR2000 9
#define DIMTS 10
#define DMPT1327 11
#define DJOLLY 12
#define DEURO 13
#define DPOCSAG 14
#define DFRAME 15
#define DCALL 16
#define DCC 17
#define DDB 18
#define DTRANS 19
#define DDMS 20
#define DSMS 21
#define DSDR 22
#define DUHD 23
#define DSOAPY 24
#define DWAVE 25
#define DRADIO 26
#define DAM791X 27
#define DUART 28
#define DDEVICE 29
#define DDATENKLO 30
#define DZEIT 31
#define DSIM1 32
#define DSIM2 33
#define DSIMI 34
#define DSIM7 35
#define DMTP2 36
#define DMTP3 37
#define DMUP 38
#define DROUTER 39
#define DSTDERR 40
#define DSS5 41
#define DISDN 42
#define DMISDN 43
#define DDSS1 44
#define DSIP 45
#define DTEL 46
void get_win_size(int *w, int *h);
#define PDEBUG(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, NULL, fmt, ## arg)
#define PDEBUG_CHAN(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, CHAN, fmt, ## arg)
void _printdebug(const char *file, const char *function, int line, int cat, int level, const char *chan_str, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 7, 8)));
const char *debug_amplitude(double level);
const char *debug_db(double level_db);
void debug_print_help(void);
void debug_list_cat(void);
int parse_debug_opt(const char *opt);
extern int debuglevel;
extern void (*clear_console_text)(void);
extern void (*print_console_text)(void);
extern int debug_limit_scroll;
const char *debug_hex(const uint8_t *data, int len);

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libg711.a
libg711_a_SOURCES = \
g711.c

View File

@ -1,537 +0,0 @@
/*****************************************************************************\
** **
** PBX4Linux **
** **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg (GPL) **
** **
** audio conversions for alaw and ulaw **
** **
\*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/* ulaw -> signed 16-bit */
static int16_t g711_ulaw_flipped_to_linear[256] =
{
0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0xffff,
0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
};
/* alaw -> signed 16-bit */
static int16_t g711_alaw_flipped_to_linear[256] =
{
0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
};
/* Xlaw -> signed 16-bit */
static int16_t g711_alaw_to_linear[256];
static int16_t g711_ulaw_to_linear[256];
/* signed 16-bit -> Xlaw */
static uint8_t g711_linear_to_alaw_flipped[65536];
static uint8_t g711_linear_to_ulaw_flipped[65536];
static uint8_t g711_linear_to_alaw[65536];
static uint8_t g711_linear_to_ulaw[65536];
/* transcode */
static uint8_t g711_alaw_to_ulaw[256];
static uint8_t g711_ulaw_to_alaw[256];
static uint8_t g711_alaw_flipped_to_ulaw[256];
static uint8_t g711_ulaw_flipped_to_alaw[256];
static uint8_t g711_alaw_to_ulaw_flipped[256];
static uint8_t g711_ulaw_to_alaw_flipped[256];
/* table is used to generate linear_to_alaw */
static int16_t g711_alaw_relations[] =
{
0x8684, 0x55, 0x8a84, 0xd5, 0x8e84, 0x15, 0x9284, 0x95,
0x9684, 0x75, 0x9a84, 0xf5, 0x9e84, 0x35, 0xa284, 0xb5,
0xa684, 0x45, 0xaa84, 0xc5, 0xae84, 0x05, 0xb284, 0x85,
0xb684, 0x65, 0xba84, 0xe5, 0xbe84, 0x25, 0xc184, 0xa5,
0xc384, 0x5d, 0xc584, 0xdd, 0xc784, 0x1d, 0xc984, 0x9d,
0xcb84, 0x7d, 0xcd84, 0xfd, 0xcf84, 0x3d, 0xd184, 0xbd,
0xd384, 0x4d, 0xd584, 0xcd, 0xd784, 0x0d, 0xd984, 0x8d,
0xdb84, 0x6d, 0xdd84, 0xed, 0xdf84, 0x2d, 0xe104, 0xad,
0xe204, 0x51, 0xe304, 0xd1, 0xe404, 0x11, 0xe504, 0x91,
0xe604, 0x71, 0xe704, 0xf1, 0xe804, 0x31, 0xe904, 0xb1,
0xea04, 0x41, 0xeb04, 0xc1, 0xec04, 0x01, 0xed04, 0x81,
0xee04, 0x61, 0xef04, 0xe1, 0xf004, 0x21, 0xf0c4, 0x59,
0xf0c4, 0xa1, 0xf144, 0xd9, 0xf1c4, 0x19, 0xf244, 0x99,
0xf2c4, 0x79, 0xf344, 0xf9, 0xf3c4, 0x39, 0xf444, 0xb9,
0xf4c4, 0x49, 0xf544, 0xc9, 0xf5c4, 0x09, 0xf644, 0x89,
0xf6c4, 0x69, 0xf744, 0xe9, 0xf7c4, 0x29, 0xf844, 0x57,
0xf844, 0xa9, 0xf8a4, 0xd7, 0xf8e4, 0x17, 0xf924, 0x97,
0xf964, 0x77, 0xf9a4, 0xf7, 0xf9e4, 0x37, 0xfa24, 0xb7,
0xfa64, 0x47, 0xfaa4, 0xc7, 0xfae4, 0x07, 0xfb24, 0x87,
0xfb64, 0x67, 0xfba4, 0xe7, 0xfbe4, 0x27, 0xfc24, 0x5f,
0xfc24, 0xa7, 0xfc64, 0x1f, 0xfc64, 0xdf, 0xfc94, 0x9f,
0xfcb4, 0x7f, 0xfcd4, 0xff, 0xfcf4, 0x3f, 0xfd14, 0xbf,
0xfd34, 0x4f, 0xfd54, 0xcf, 0xfd74, 0x0f, 0xfd94, 0x8f,
0xfdb4, 0x6f, 0xfdd4, 0xef, 0xfdf4, 0x2f, 0xfe14, 0x53,
0xfe14, 0xaf, 0xfe34, 0x13, 0xfe34, 0xd3, 0xfe54, 0x73,
0xfe54, 0x93, 0xfe74, 0x33, 0xfe74, 0xf3, 0xfe8c, 0xb3,
0xfe9c, 0x43, 0xfeac, 0xc3, 0xfebc, 0x03, 0xfecc, 0x83,
0xfedc, 0x63, 0xfeec, 0xe3, 0xfefc, 0x23, 0xff0c, 0xa3,
0xff1c, 0x5b, 0xff2c, 0xdb, 0xff3c, 0x1b, 0xff4c, 0x9b,
0xff5c, 0x7b, 0xff6c, 0xfb, 0xff7c, 0x3b, 0xff88, 0xbb,
0xff98, 0x4b, 0xffa8, 0xcb, 0xffb8, 0x0b, 0xffc8, 0x8b,
0xffd8, 0x6b, 0xffe8, 0xeb, 0xfff8, 0x2b, 0xfff8, 0xab,
0x0008, 0x2a, 0x0008, 0xaa, 0x0018, 0xea, 0x0028, 0x6a,
0x0038, 0x8a, 0x0048, 0x0a, 0x0058, 0xca, 0x0068, 0x4a,
0x0078, 0xba, 0x0084, 0x3a, 0x0094, 0xfa, 0x00a4, 0x7a,
0x00b4, 0x9a, 0x00c4, 0x1a, 0x00d4, 0xda, 0x00e4, 0x5a,
0x00f4, 0xa2, 0x0104, 0x22, 0x0114, 0xe2, 0x0124, 0x62,
0x0134, 0x82, 0x0144, 0x02, 0x0154, 0xc2, 0x0164, 0x42,
0x0174, 0xb2, 0x018c, 0x32, 0x018c, 0xf2, 0x01ac, 0x72,
0x01ac, 0x92, 0x01cc, 0x12, 0x01cc, 0xd2, 0x01ec, 0x52,
0x01ec, 0xae, 0x020c, 0x2e, 0x022c, 0xee, 0x024c, 0x6e,
0x026c, 0x8e, 0x028c, 0x0e, 0x02ac, 0xce, 0x02cc, 0x4e,
0x02ec, 0xbe, 0x030c, 0x3e, 0x032c, 0xfe, 0x034c, 0x7e,
0x036c, 0x9e, 0x039c, 0x1e, 0x039c, 0xde, 0x03dc, 0x5e,
0x03dc, 0xa6, 0x041c, 0x26, 0x045c, 0xe6, 0x049c, 0x66,
0x04dc, 0x86, 0x051c, 0x06, 0x055c, 0xc6, 0x059c, 0x46,
0x05dc, 0xb6, 0x061c, 0x36, 0x065c, 0xf6, 0x069c, 0x76,
0x06dc, 0x96, 0x071c, 0x16, 0x075c, 0xd6, 0x07bc, 0x56,
0x07bc, 0xa8, 0x083c, 0x28, 0x08bc, 0xe8, 0x093c, 0x68,
0x09bc, 0x88, 0x0a3c, 0x08, 0x0abc, 0xc8, 0x0b3c, 0x48,
0x0bbc, 0xb8, 0x0c3c, 0x38, 0x0cbc, 0xf8, 0x0d3c, 0x78,
0x0dbc, 0x98, 0x0e3c, 0x18, 0x0ebc, 0xd8, 0x0f3c, 0x58,
0x0f3c, 0xa0, 0x0ffc, 0x20, 0x10fc, 0xe0, 0x11fc, 0x60,
0x12fc, 0x80, 0x13fc, 0x00, 0x14fc, 0xc0, 0x15fc, 0x40,
0x16fc, 0xb0, 0x17fc, 0x30, 0x18fc, 0xf0, 0x19fc, 0x70,
0x1afc, 0x90, 0x1bfc, 0x10, 0x1cfc, 0xd0, 0x1dfc, 0x50,
0x1efc, 0xac, 0x207c, 0x2c, 0x227c, 0xec, 0x247c, 0x6c,
0x267c, 0x8c, 0x287c, 0x0c, 0x2a7c, 0xcc, 0x2c7c, 0x4c,
0x2e7c, 0xbc, 0x307c, 0x3c, 0x327c, 0xfc, 0x347c, 0x7c,
0x367c, 0x9c, 0x387c, 0x1c, 0x3a7c, 0xdc, 0x3c7c, 0x5c,
0x3e7c, 0xa4, 0x417c, 0x24, 0x457c, 0xe4, 0x497c, 0x64,
0x4d7c, 0x84, 0x517c, 0x04, 0x557c, 0xc4, 0x597c, 0x44,
0x5d7c, 0xb4, 0x617c, 0x34, 0x657c, 0xf4, 0x697c, 0x74,
0x6d7c, 0x94, 0x717c, 0x14, 0x757c, 0xd4, 0x797c, 0x54
};
uint8_t g711_flip[256];
static int g711_initialized = 0;
/* generate tables
*/
void g711_init(void)
{
int i, j;
/* flip tables */
for (i = 0; i < 256; i++) {
g711_flip[i]
= ((i & 1) << 7)
+ ((i & 2) << 5)
+ ((i & 4) << 3)
+ ((i & 8) << 1)
+ ((i & 16) >> 1)
+ ((i & 32) >> 3)
+ ((i & 64) >> 5)
+ ((i & 128) >> 7);
g711_alaw_to_linear[i] = g711_alaw_flipped_to_linear[g711_flip[i]];
g711_ulaw_to_linear[i] = g711_ulaw_flipped_to_linear[g711_flip[i]];
}
/* linear to alaw tables */
i = j = 0;
while(i < 65536) {
if (i - 32768 > g711_alaw_relations[j << 1])
j++;
if (j > 255)
j = 255;
g711_linear_to_alaw_flipped[(i - 32768) & 0xffff] = g711_alaw_relations[(j << 1) | 1];
g711_linear_to_alaw[(i - 32768) & 0xffff] = g711_flip[g711_alaw_relations[(j << 1) | 1]];
i++;
}
/* linear to ulaw tables */
i = j = 0;
while(i < 32768) {
if (i - 32768 > g711_ulaw_flipped_to_linear[j])
j++;
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
g711_linear_to_ulaw[(i - 32768) & 0xffff] = g711_flip[j];
i++;
}
j = 255;
while(i < 65536) {
if (i - 32768 > g711_alaw_flipped_to_linear[j])
j--;
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
g711_linear_to_ulaw[(i - 32768) & 0xffff] = g711_flip[j];
i++;
}
/* transcode */
for (i = 0; i < 256; i++) {
g711_alaw_to_ulaw[i] = g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[i]];
g711_ulaw_to_alaw[i] = g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[i]];
g711_alaw_flipped_to_ulaw[i] = g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[g711_flip[i]]];
g711_ulaw_flipped_to_alaw[i] = g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[g711_flip[i]]];
g711_alaw_to_ulaw_flipped[i] = g711_flip[g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[i]]];
g711_ulaw_to_alaw_flipped[i] = g711_flip[g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[i]]];
}
g711_initialized = 1;
}
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
int len = src_len / 2, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_linear_to_alaw_flipped[(uint16_t)src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
int len = src_len / 2, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_linear_to_ulaw_flipped[(uint16_t)src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data;
int16_t *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_flipped_to_linear[src[i]];
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data;
int16_t *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_flipped_to_linear[src[i]];
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
int len = src_len / 2, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_linear_to_alaw[(uint16_t)src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
int len = src_len / 2, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_linear_to_ulaw[(uint16_t)src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data;
int16_t *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_to_linear[src[i]];
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data;
int16_t *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len * 2);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_to_linear[src[i]];
*dst_data = (uint8_t *)dst;
*dst_len = len * 2;
}
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_to_ulaw[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_flipped_to_ulaw[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_alaw_to_ulaw_flipped[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_to_alaw[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_flipped_to_alaw[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_ulaw_to_alaw_flipped[src[i]];
*dst_data = dst;
*dst_len = len;
}
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
if (!g711_initialized) {
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
abort();
}
dst = malloc(len);
if (!dst)
return;
for (i = 0; i < len; i++)
dst[i] = g711_flip[src[i]];
*dst_data = dst;
*dst_len = len;
}

View File

@ -1,17 +0,0 @@
void g711_init(void);
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);

View File

@ -1,6 +1,6 @@
/* Jitter buffering functions
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
@ -17,109 +17,536 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* How does it work:
*
* Storing:
*
* Each saved frame is sorted into the list of packages by their timestamp.
*
* The first packet will be stored with a timestamp offset of minimum jitter
* window size or half of the target size, depending on the adaptive jitter
* buffer flag.
*
* Packets with the same timestamp are dropped.
*
* Early packts that exceed maximum jitter window size cause jitter
* window to shift into the future.
*
* Late packets cause jitter window to shift into the past (allowing more
* delay). Minimum jitter window size is added also, to prevent subsequent
* packets from beeing late too.
*
* If adaptive jitter buffer is used, a delay that exceed the target size
* is reduced to the target size.
*
* If ssrc changes, the buffer is reset, but not locked again.
*
*
* Loading:
*
* jitter_offset() will return the number of samples between the jitter buffer's head and the first packet afterwards. Packets that already passed the jitter buffer's head are ignored. If no frame is ahead the jitter buffer's head, a negative value is returned.
*
* jitter_load() will remove and return the frame at the jitter buffer's head. Packet that already passed the jitter buffer's head are deleted. If no frame matches the jitter buffer's head, NULL is returned.
*
* jitter_advance() will advance the jitter buffer's head by the given number of samples.
*
* jitter_load_samples() will read decoded samples from jitter buffer's frames.
* This means that that the decoder of each frame must generate samples of equal type and size.
* If there is a gap between jitter buffer's head and the next frame, the samples are taken from the last frame.
* The conceal function is called in this case, to extrapolate the missing samples.
* If no conceal function is given, the last frame is repeated.
* If there is no gap between jitter buffer's head and the next frame, the frame is decoded and the samples are taken from that frame.
* After that the jitter buffer's head is advanced by the number of samples read.
*
* *TBD*
*
*
* Unlocking:
*
* If the buffer is created or reset, the buffer is locked, so no packets are
* stored. When the loading routine is called, the buffer is unlocked. This
* prevents from filling the buffer before loading is performed, which would
* cause high delay.
*
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../liblogging/logging.h"
#include "jitter.h"
#define INITIAL_DELAY_INTERVAL 0.5
#define REPEAT_DELAY_INTERVAL 3.0
/* uncomment to enable heavy debugging */
//#define HEAVY_DEBUG
//#define VISUAL_DEBUG
static int unnamed_count = 1;
/* create jitter buffer */
int jitter_create(jitter_t *jitter, int length)
int jitter_create(jitter_t *jb, const char *name, double samplerate, double target_window_duration, double max_window_duration, uint32_t window_flags)
{
memset(jitter, 0, sizeof(*jitter));
jitter->spl = malloc(length * sizeof(sample_t));
if (!jitter->spl) {
PDEBUG(DDSP, DEBUG_ERROR, "No memory for jitter buffer.\n");
return -ENOMEM;
}
jitter->len = length;
int rc = 0;
jitter_reset(jitter);
memset(jb, 0, sizeof(*jb));
return 0;
}
void jitter_reset(jitter_t *jitter)
{
memset(jitter->spl, 0, jitter->len * sizeof(sample_t));
/* put write pointer ahead by half of the buffer length */
jitter->inptr = jitter->len / 2;
}
void jitter_destroy(jitter_t *jitter)
{
if (jitter->spl) {
free(jitter->spl);
jitter->spl = NULL;
}
}
/* store audio in jitterbuffer
*
* stop if buffer is completely filled
*/
void jitter_save(jitter_t *jb, sample_t *samples, int length)
{
sample_t *spl;
int inptr, outptr, len, space;
int i;
spl = jb->spl;
inptr = jb->inptr;
outptr = jb->outptr;
len = jb->len;
space = (outptr - inptr + len - 1) % len;
if (space < length)
length = space;
for (i = 0; i < length; i++) {
spl[inptr++] = *samples++;
if (inptr == len)
inptr = 0;
}
jb->inptr = inptr;
}
/* get audio from jitterbuffer
*/
void jitter_load(jitter_t *jb, sample_t *samples, int length)
{
sample_t *spl;
int inptr, outptr, len, fill;
int i, ii;
spl = jb->spl;
inptr = jb->inptr;
outptr = jb->outptr;
len = jb->len;
fill = (inptr - outptr + len) % len;
if (fill < length)
ii = fill;
/* optionally give a string to be show with the debug */
if (name && *name)
snprintf(jb->name, sizeof(jb->name) - 1, "(%s) ", name);
else
ii = length;
snprintf(jb->name, sizeof(jb->name) - 1, "(unnamed %d) ", unnamed_count++);
/* fill what we got */
for (i = 0; i < ii; i++) {
*samples++ = spl[outptr++];
if (outptr == len)
outptr = 0;
}
/* on underrun, fill with silence */
for (; i < length; i++) {
*samples++ = 0;
jb->sample_duration = 1.0 / samplerate;
jb->samples_20ms = samplerate / 50;
jb->target_window_size = (int)ceil(target_window_duration / jb->sample_duration);
jb->max_window_size = (int)ceil(max_window_duration / jb->sample_duration);
jb->window_flags = window_flags;
jitter_reset(jb);
LOGP(DJITTER, LOGL_INFO, "%s Created jitter buffer. (samperate=%.0f, target_window=%.0fms, max_window=%.0fms, flag:latency=%s flag:repeat=%s)\n",
jb->name,
samplerate,
(double)jb->target_window_size * jb->sample_duration * 1000.0,
(double)jb->max_window_size * jb->sample_duration * 1000.0,
(window_flags & JITTER_FLAG_LATENCY) ? "true" : "false",
(window_flags & JITTER_FLAG_REPEAT) ? "true" : "false");
return rc;
}
jb->outptr = outptr;
}
void jitter_clear(jitter_t *jb)
/* reset jitter buffer */
void jitter_reset(jitter_t *jb)
{
jb->inptr = jb->outptr = 0;
jitter_frame_t *jf, *temp;
LOGP(DJITTER, LOGL_INFO, "%s Reset jitter buffer.\n", jb->name);
/* jitter buffer locked */
jb->unlocked = false;
/* window becomes invalid */
jb->window_valid = false;
/* remove all pending frames */
jf = jb->frame_list;
while(jf) {
temp = jf;
jf = jf->next;
free(temp);
}
jb->frame_list = NULL;
/* remove current sample buffer */
free(jb->spl_buf);
jb->spl_buf = NULL;
jb->spl_valid = false;
}
void jitter_destroy(jitter_t *jb)
{
jitter_reset(jb);
LOGP(DJITTER, LOGL_INFO, "%s Destroying jitter buffer.\n", jb->name);
}
jitter_frame_t *jitter_frame_alloc(void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void *decoder_priv, uint8_t *data, int size, uint8_t marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
{
jitter_frame_t *jf;
jf = malloc(sizeof(*jf) + size);
if (!jf) {
LOGP(DJITTER, LOGL_ERROR, "No memory for frame.\n");
return NULL;
}
memset(jf, 0, sizeof(*jf)); // note: clear header only
jf->decoder = decoder;
jf->decoder_priv = decoder_priv;
memcpy(jf->data, data, size);
jf->size = size;
jf->marker = marker;
jf->sequence = sequence;
jf->timestamp = timestamp;
jf->ssrc = ssrc;
return jf;
}
void jitter_frame_free(jitter_frame_t *jf)
{
free(jf);
}
void jitter_frame_get(jitter_frame_t *jf, void (**decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void **decoder_priv, uint8_t **data, int *size, uint8_t *marker, uint16_t *sequence, uint32_t *timestamp, uint32_t *ssrc)
{
if (decoder)
*decoder = jf->decoder;
if (decoder_priv)
*decoder_priv = jf->decoder_priv;
if (data)
*data = jf->data;
if (size)
*size = jf->size;
if (marker)
*marker = jf->marker;
if (sequence)
*sequence = jf->sequence;
if (timestamp)
*timestamp = jf->timestamp;
if (ssrc)
*ssrc = jf->ssrc;
}
/* Store frame in jitterbuffer
*
* Use sequence number to order frames.
* Use timestamp to handle delay.
*/
void jitter_save(jitter_t *jb, jitter_frame_t *jf)
{
jitter_frame_t **jfp;
int32_t offset_timestamp;
/* ignore frames until the buffer is unlocked by jitter_load() */
if (!jb->unlocked) {
jitter_frame_free(jf);
return;
}
/* first packet (with this ssrc) sets window size to target_window_size */
if (!jb->window_valid || jb->window_ssrc != jf->ssrc) {
if (!jb->window_valid)
LOGP(DJITTER, LOGL_DEBUG, "%s Initial frame after init or reset.\n", jb->name);
else
LOGP(DJITTER, LOGL_DEBUG, "%s SSRC changed.\n", jb->name);
// NOTE: Reset must be called before finding the frame location below, because there will be no frame in list anymore!
jitter_reset(jb);
jb->unlocked = true;
/* when using dynamic jitter buffer, we use half of the target delay */
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
jb->window_timestamp = jf->timestamp - (uint32_t)jb->target_window_size / 2;
} else {
jb->window_timestamp = jf->timestamp - (uint32_t)jb->target_window_size;
}
jb->window_valid = true;
jb->window_ssrc = jf->ssrc;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = INITIAL_DELAY_INTERVAL;
}
/* reduce delay */
if (jb->delay_counter >= jb->delay_interval) {
if (jb->min_delay >= 0)
LOGP(DJITTER, LOGL_DEBUG, "%s Statistics: target_window_delay=%.0fms max_window_delay=%.0fms current min_delay=%.0fms\n",
jb->name,
(double)jb->target_window_size * jb->sample_duration * 1000.0,
(double)jb->max_window_size * jb->sample_duration * 1000.0,
(double)jb->min_delay * jb->sample_duration * 1000.0);
/* delay reduction, if minimum delay is greater than target jitter window size */
if ((jb->window_flags & JITTER_FLAG_LATENCY) && jb->min_delay > jb->target_window_size) {
LOGP(DJITTER, LOGL_DEBUG, "%s Reducing current minimum delay of %.0fms, because maximum delay is greater than target window size of %.0fms.\n",
jb->name,
(double)jb->min_delay * jb->sample_duration * 1000.0,
(double)jb->target_window_size * jb->sample_duration * 1000.0);
/* only reduce delay to half of the target window size */
jb->window_timestamp += jb->min_delay - jb->target_window_size / 2;
}
jb->delay_counter -= jb->delay_interval;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
jb->min_delay = -1;
}
/* find location where to put frame into the list, depending on sequence number */
jfp = &jb->frame_list;
while(*jfp) {
offset_timestamp = (int16_t)(jf->timestamp - (*jfp)->timestamp);
/* found double entry */
if (offset_timestamp == 0) {
LOGP(DJITTER, LOGL_DEBUG, "%s Dropping double packet (timestamp = %u)\n", jb->name, jf->timestamp);
jitter_frame_free(jf);
return;
}
/* offset is negative, so we found the position to insert frame */
if (offset_timestamp < 0)
break;
jfp = &((*jfp)->next);
}
offset_timestamp = jf->timestamp - jb->window_timestamp;
#ifdef HEAVY_DEBUG
LOGP(DJITTER, LOGL_DEBUG, "%s Frame has offset of %.0fms in jitter buffer.\n", jb->name, (double)offset_timestamp * jb->sample_duration * 1000.0);
#endif
/* measure delay */
if (jb->min_delay < 0 || offset_timestamp < jb->min_delay)
jb->min_delay = offset_timestamp;
/* if frame is too early (delay ceases), shift window to the future */
if (offset_timestamp > jb->max_window_size) {
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the end. (offset_sequence(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
/* shift window so it fits to the end of window */
jb->window_timestamp = jf->timestamp - jb->max_window_size;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
} else {
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the target delay. (offset_sequence(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
/* shift window so frame fits to the start of window + target delay */
jb->window_timestamp = jf->timestamp - jb->target_window_size;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
}
}
/* is frame is too late, shift window to the past. */
if (offset_timestamp < 0) {
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add target window size. (offset_sequence(%d) < 0)\n", jb->name, offset_timestamp);
/* shift window so frame fits to the start of window + half of target delay */
jb->window_timestamp = jf->timestamp - jb->target_window_size / 2;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
} else {
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add half target window size. (offset_sequence(%d) < 0)\n", jb->name, offset_timestamp);
/* shift window so frame fits to the start of window + target delay */
jb->window_timestamp = jf->timestamp - jb->target_window_size;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
}
}
/* insert or append frame */
#ifdef HEAVY_DEBUG
#include <time.h>
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
LOGP(DJITTER, LOGL_DEBUG, "%s Store frame. %ld.%04ld\n", jb->name, tv.tv_sec, tv.tv_nsec / 1000000);
#endif
jf->next = *jfp;
*jfp = jf;
}
/* get offset to next chunk, return -1, if there is no */
int32_t jitter_offset(jitter_t *jb)
{
jitter_frame_t *jf;
int16_t offset_timestamp = 0;
/* now unlock jitter buffer */
jb->unlocked = true;
/* get timestamp of chunk that is not in the past */
for (jf = jb->frame_list; jf; jf = jf->next) {
offset_timestamp = jf->timestamp - jb->window_timestamp;
if (offset_timestamp >= 0)
break;
}
return (jf) ? offset_timestamp : -1;
}
/* get next data chunk from jitterbuffer */
jitter_frame_t *jitter_load(jitter_t *jb)
{
jitter_frame_t *jf;
int32_t offset_timestamp;
#ifdef HEAVY_DEBUG
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
LOGP(DJITTER, LOGL_DEBUG, "%s Load frame. %ld.%04ld\n", jb->name, tv.tv_sec, tv.tv_nsec / 1000000);
#endif
/* now unlock jitter buffer */
jb->unlocked = true;
/* get current chunk, free all chunks that are in the past */
while ((jf = jb->frame_list)) {
offset_timestamp = jf->timestamp - jb->window_timestamp;
if (offset_timestamp >= 0)
break;
/* detach and free */
jb->frame_list = jf->next;
jitter_frame_free(jf);
}
/* next frame in the future */
if (jf && jf->timestamp != jb->window_timestamp)
return NULL;
/* detach, and return */
if (jf)
jb->frame_list = jf->next;
return jf;
}
/* advance time stamp of jitter buffer */
void jitter_advance(jitter_t *jb, uint32_t offset)
{
if (!jb->window_valid)
return;
jb->window_timestamp += offset;
/* increment timer to check delay */
jb->delay_counter += jb->sample_duration * (double)offset;
}
/* load samples from jitter buffer
* store in spl_buf until all copied
* conceal, if frame is missing
* ceate silence, if no spl_buf exists in the first place */
void jitter_load_samples(jitter_t *jb, uint8_t *spl, int len, size_t sample_size, void (*conceal)(uint8_t *spl, int len, void *priv), void *conceal_priv)
{
jitter_frame_t *jf;
int32_t offset;
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void *decoder_priv;
uint8_t *payload;
int payload_len;
int tocopy;
#ifdef VISUAL_DEBUG
int32_t offset_timestamp;
char debug[jb->max_window_size + 32];
int last = 0;
memset(debug, ' ', sizeof(debug));
for (jf = jb->frame_list; jf; jf = jf->next) {
offset_timestamp = jf->timestamp - jb->window_timestamp;
if (offset_timestamp < 0)
continue;
offset_timestamp = (int)((double)offset_timestamp * jb->sample_duration * 1000.0);
debug[offset_timestamp] = '0' + jf->sequence % 10;
last = offset_timestamp + 1;
}
debug[last] = '\0';
LOGP(DJITTER, LOGL_DEBUG, "%s:%s\n", jb->name, debug);
#endif
next_chunk:
/* nothing more to return */
if (!len)
return;
copy_chunk:
/* consume from buffer, if valid */
if (jb->spl_buf && jb->spl_valid) {
tocopy = jb->spl_len - jb->spl_pos;
if (tocopy > len)
tocopy = len;
#ifdef HEAVY_DEBUG
LOGP(DJITTER, LOGL_DEBUG, "%s loading %d samples: from valid sample buffer.\n", jb->name, tocopy);
#endif
/* advance jitter buffer */
jitter_advance(jb, tocopy);
memcpy(spl, jb->spl_buf + jb->spl_pos * sample_size, tocopy * sample_size);
spl += tocopy * sample_size;
len -= tocopy;
jb->spl_pos += tocopy;
if (jb->spl_pos == jb->spl_len) {
jb->spl_pos = 0;
jb->spl_valid = false;
}
goto next_chunk;
}
/* get offset to next frame in jitter buffer */
offset = jitter_offset(jb);
/* jitter buffer is empty, so we must conceal all samples we have */
if (offset < 0)
offset = len;
/* if we have an offset, we need to conceal the samples */
if (offset > 0) {
/* only process as much samples as need */
if (offset > len)
offset = len;
#ifdef HEAVY_DEBUG
LOGP(DJITTER, LOGL_DEBUG, "%s concealing %d samples: from invalid sample buffer.\n", jb->name, offset);
#endif
/* advance jitter buffer */
jitter_advance(jb, offset);
/* if there is no buffer, allocate 20ms, filled with 0 */
if (!jb->spl_buf) {
jb->spl_len = jb->samples_20ms;
jb->spl_buf = calloc(jb->spl_len, sample_size);
}
/* do until all samples are processed */
while (offset) {
tocopy = jb->spl_len - jb->spl_pos;
if (tocopy > offset)
tocopy = offset;
if (conceal)
conceal(jb->spl_buf + jb->spl_pos * sample_size, tocopy, conceal_priv);
memcpy(spl, jb->spl_buf + jb->spl_pos * sample_size, tocopy * sample_size);
spl += tocopy * sample_size;
len -= tocopy;
jb->spl_pos += tocopy;
if (jb->spl_pos == jb->spl_len)
jb->spl_pos = 0;
offset -= tocopy;
}
goto next_chunk;
}
/* load from jitter buffer (it should work, because offset equals 0 */
jf = jitter_load(jb);
if (!jf) {
LOGP(DJITTER, LOGL_ERROR, "%s Failed to get frame from jitter buffer, please fix!\n", jb->name);
jitter_reset(jb);
return;
}
#ifdef HEAVY_DEBUG
LOGP(DJITTER, LOGL_DEBUG, "%s loading new frame to sample buffer.\n", jb->name);
#endif
/* get data from frame */
jitter_frame_get(jf, &decoder, &decoder_priv, &payload, &payload_len, NULL, NULL, NULL, NULL);
/* free previous buffer */
free(jb->spl_buf);
jb->spl_buf = NULL;
jb->spl_pos = 0;
/* decode */
if (decoder) {
decoder(payload, payload_len, &jb->spl_buf, &jb->spl_len, decoder_priv);
if (!jb->spl_buf) {
jitter_frame_free(jf);
return;
}
} else {
/* no decoder, so just copy as it is */
jb->spl_buf = malloc(payload_len);
if (!jb->spl_buf) {
jitter_frame_free(jf);
return;
}
memcpy(jb->spl_buf, payload, payload_len);
jb->spl_len = payload_len;
}
jb->spl_len /= sample_size;
jb->spl_valid = true;
/* free jiter frame */
jitter_frame_free(jf);
goto copy_chunk;
}
void jitter_conceal_s16(uint8_t *_spl, int len, void __attribute__((unused)) *priv)
{
int16_t *spl = (int16_t *)_spl;
while (len) {
*spl++ /= 1.5;
len--;
}
}

View File

@ -1,14 +1,67 @@
#define JITTER_FLAG_NONE 0 // no flags at all
#define JITTER_FLAG_LATENCY (1 << 0) // keep latency close to target_window_duration
#define JITTER_FLAG_REPEAT (1 << 1) // repeat audio to extrapolate gaps
/* window settings for low latency audio and extrapolation of gaps */
#define JITTER_AUDIO 0.060, 1.000, JITTER_FLAG_LATENCY | JITTER_FLAG_REPEAT
/* window settings for analog data (fax/modem) or digial data (HDLC) */
#define JITTER_DATA 0.100, 0.200, JITTER_FLAG_NONE
typedef struct jitter_frame {
struct jitter_frame *next;
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void *decoder_priv;
uint8_t marker;
uint16_t sequence;
uint32_t timestamp;
uint32_t ssrc;
int size;
uint8_t data[0];
} jitter_frame_t;
typedef struct jitter {
sample_t *spl; /* pointer to sample buffer */
int len; /* buffer size: number of samples */
int inptr, outptr; /* write pointer and read pointer */
char name[64];
/* frame properties */
double sample_duration; /* duration of a frame (ms) */
int samples_20ms; /* samples to compensate a gap of unknown size */
/* window properties */
bool unlocked; /* jitter buffer will be locked until some reads from it */
uint32_t window_flags; /* flags to alter behaviour of jitter buffer */
int target_window_size; /* target size of window (frames) */
int max_window_size; /* maximum size of window (frames) */
bool window_valid; /* set, if first frame has been received */
uint32_t window_ssrc; /* current sync source of window */
uint32_t window_timestamp; /* lowest timestamp number in window */
/* reduction of delay */
double delay_interval; /* interval for delay measurement (seconds) */
double delay_counter; /* current counter to count interval (seconds) */
int min_delay; /* minimum delay measured during interval (frames) */
/* list of frames */
jitter_frame_t *frame_list;
/* sample buffer (optional) */
uint8_t *spl_buf; /* current samples buffer */
int spl_pos; /* position of in buffer */
int spl_len; /* total buffer size */
bool spl_valid; /* if buffer has valid frame (not repeated) */
} jitter_t;
int jitter_create(jitter_t *jitter, int length);
void jitter_reset(jitter_t *jitter);
void jitter_destroy(jitter_t *jitter);
void jitter_save(jitter_t *jb, sample_t *samples, int length);
void jitter_load(jitter_t *jb, sample_t *samples, int length);
void jitter_clear(jitter_t *jb);
int jitter_create(jitter_t *jb, const char *name, double samplerate, double target_window_duration, double max_window_duration, uint32_t window_flags);
void jitter_reset(jitter_t *jb);
void jitter_destroy(jitter_t *jb);
jitter_frame_t *jitter_frame_alloc(void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void *decoder_priv, uint8_t *data, int size, uint8_t marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc);
void jitter_frame_free(jitter_frame_t *jf);
void jitter_frame_get(jitter_frame_t *jf, void (**decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void **decoder_priv, uint8_t **data, int *size, uint8_t *marker, uint16_t *sequence, uint32_t *timestamp, uint32_t *ssrc);
void jitter_save(jitter_t *jb, jitter_frame_t *jf);
int32_t jitter_offset(jitter_t *jb);
jitter_frame_t *jitter_load(jitter_t *jb);
void jitter_advance(jitter_t *jb, uint32_t offset);
void jitter_load_samples(jitter_t *jb, uint8_t *spl, int len, size_t sample_size, void (*conceal)(uint8_t *spl, int len, void *priv), void *conceal_priv);
void jitter_conceal_s16(uint8_t *_spl, int len, void __attribute__((unused)) *priv);

View File

@ -0,0 +1,8 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = liblogging.a
liblogging_a_SOURCES = \
logging.c \
categories.c

View File

@ -0,0 +1,47 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include "categories.h"
/* All logging categories used by this project. */
struct log_info_cat log_categories[] = {
[DLCC] = {
.name = "DLCC",
.description = "libosmo-cc CC Layer",
.color = "\033[0;37m",
},
[DOPTIONS] = {
.name = "DOPTIONS",
.description = "config options",
.color = "\033[0;33m",
},
[DJITTER] = {
.name = "DJITTER",
.description = "jitter buffer handling",
.color = "\033[0;36m",
},
[DISDN] = {
.name = "DISDN",
.description = "ISDN control",
.color = "\033[1;35m",
},
[DMISDN] = {
.name = "DMISDN",
.description = "mISDN stack",
.color = "\033[0;34m",
},
[DDSS1] = {
.name = "DDSS1",
.description = "ISDN DSS1 protocol",
.color = "\033[1;34m",
},
[DPH] = {
.name = "DPH",
.description = "PH SAP socket interface",
.color = "\033[0;33m",
},
};
size_t log_categories_size = ARRAY_SIZE(log_categories);

View File

@ -0,0 +1,14 @@
enum {
DLCC,
DOPTIONS,
DJITTER,
DISDN,
DMISDN,
DDSS1,
DPH,
};
extern struct log_info_cat log_categories[];
extern size_t log_categories_size;

264
src/liblogging/logging.c Normal file
View File

@ -0,0 +1,264 @@
/* Logging (on segmented part of the window)
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* 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 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 <sys/ioctl.h>
#include <math.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/cc/misc.h>
#include "logging.h"
int loglevel = LOGL_INFO;
static int scroll_window_start = 0;
static int scroll_window_end = 0;
static int scroll_window_height = 0;
void lock_logging(void)
{
log_tgt_mutex_lock();
}
void unlock_logging(void)
{
log_tgt_mutex_unlock();
}
void get_win_size(int *w, int *h)
{
struct winsize win;
int rc;
rc = ioctl(0, TIOCGWINSZ, &win);
if (rc) {
*w = 80;
*h = 25;
return;
}
if (h)
*h = win.ws_row;
if (w)
*w = win.ws_col;
}
void enable_limit_scroll(bool enable)
{
/* Before the window is set, keep scrolling everything. */
if (scroll_window_height == 0)
return;
/* If window is too small. */
if (scroll_window_end - scroll_window_start <= 0)
return;
if (enable) {
printf("\0337\033[%d;%dr\0338", scroll_window_start, scroll_window_end);
} else
printf("\0337\033[%d;%dr\0338", 1, scroll_window_height);
fflush(stdout);
}
void logging_limit_scroll_top(int lines)
{
lock_logging();
get_win_size(NULL, &scroll_window_height);
scroll_window_start = lines + 1;
if (scroll_window_end == 0)
scroll_window_end = scroll_window_height;
enable_limit_scroll(true);
unlock_logging();
}
void logging_limit_scroll_bottom(int lines)
{
int i;
lock_logging();
get_win_size(NULL, &scroll_window_height);
scroll_window_end = scroll_window_height - lines;
if (scroll_window_start == 0)
scroll_window_start = 1;
/* Make space by adding empty lines. */
for (i = scroll_window_end; i < scroll_window_height; i++)
printf("\n");
/* Go up by number of lines to be in window. */
printf("\033[%dA", scroll_window_height - scroll_window_end);
/* Enable window. */
enable_limit_scroll(true);
unlock_logging();
}
const char *debug_amplitude(double level)
{
static char text[42];
strcpy(text, " : ");
if (level > 1.0)
level = 1.0;
if (level < -1.0)
level = -1.0;
text[20 + (int)(level * 20)] = '*';
return text;
}
#define level2db(level) (20 * log10(level))
const char *debug_db(double level_db)
{
static char text[128];
int l;
strcpy(text, ": . : . : . : . : . : . : . : . | . : . : . : . : . : . : . : . :");
if (level_db <= 0.0)
return text;
l = (int)round(level2db(level_db));
if (l > 48)
return text;
if (l < -48)
return text;
text[l + 48] = '*';
return text;
}
void logging_print_help(void)
{
printf(" -v --verbose <level> | <level>,<category>[,<category>[,...]] | list\n");
printf(" Use 'list' to get a list of all levels and categories.\n");
printf(" Verbose level: digit of debug level (default = '%d')\n", loglevel);
printf(" Verbose level+category: level digit followed by one or more categories\n");
printf(" -> If no category is specified, all categories are selected\n");
printf(" -v --verbose date\n");
printf(" Show date with debug output\n");
}
static unsigned char log_levels[] = { LOGL_DEBUG, LOGL_INFO, LOGL_NOTICE, LOGL_ERROR };
static char *log_level_names[] = { "debug", "info", "notice", "error" };
static void list_cat(void)
{
int i;
printf("Give number of debug level:\n");
for (i = 0; i < (int)sizeof(log_levels); i++)
printf(" %d = %s\n", log_levels[i], log_level_names[i]);
printf("\n");
printf("Give name(s) of debug category:\n");
for (i = 0; i < (int)log_categories_size; i++) {
if (!log_categories[i].name)
continue;
printf(" ");
if (log_categories[i].color)
printf("%s", log_categories[i].color);
if (log_categories[i].name)
printf("%s\033[0;39m = %s\n", log_categories[i].name, log_categories[i].description);
}
printf("\n");
}
int parse_logging_opt(const char *optarg)
{
int i;
char *dup, *dstring, *p;
if (!strcasecmp(optarg, "list")) {
list_cat();
return 1;
}
if (!strcasecmp(optarg, "date")) {
log_set_print_timestamp(osmo_stderr_target, 1);
return 0;
}
dup = dstring = strdup(optarg);
p = strsep(&dstring, ",");
for (i = 0; i < p[i]; i++) {
if (p[i] < '0' || p[i] > '9') {
fprintf(stderr, "Only digits are allowed for debug level!\n");
free(dup);
return -EINVAL;
}
}
loglevel = atoi(p);
for (i = 0; i < (int)sizeof(log_levels); i++) {
if (log_levels[i] == loglevel)
break;
}
if (i == (int)sizeof(log_levels)) {
fprintf(stderr, "Logging level does not exist, use '-v list' to show available levels!\n");
free(dup);
return -EINVAL;
}
/* Set loglevel and enable all categories, if dstring is not set. Else set loglevel and disable all categories. */
for (i = 0; i < (int)log_categories_size; i++)
log_set_category_filter(osmo_stderr_target, i, (!dstring), loglevel);
/* Enable each given category. */
while((p = strsep(&dstring, ","))) {
for (i = 0; i < (int)log_categories_size; i++) {
if (!log_category_name(i))
continue;
if (!strcasecmp(p, log_category_name(i)))
break;
}
if (i == (int)log_categories_size) {
fprintf(stderr, "Given logging category '%s' unknown, use '-v list' to show available categories!\n", p);
free(dup);
return -EINVAL;
}
log_set_category_filter(osmo_stderr_target, i, 1, loglevel);
}
free(dup);
return 0;
}
/* Call after configuation above. */
void logging_init(void)
{
int i;
struct log_info log_info = {
.cat = log_categories,
.num_cat = log_categories_size,
};
osmo_cc_set_log_cat(DLCC);
osmo_init_logging2(NULL, &log_info);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
/* Set loglevel and enable all categories. */
for (i = 0; i < (int)log_categories_size; i++)
log_set_category_filter(osmo_stderr_target, i, 1, loglevel);
}

21
src/liblogging/logging.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <osmocom/core/logging.h>
#include "categories.h"
extern int loglevel;
#define LOGP_CHAN(cat, level, fmt, arg...) LOGP(cat, level, "(chan %s) " fmt, CHAN, ## arg)
void get_win_size(int *w, int *h);
void lock_logging(void);
void unlock_logging(void);
void enable_limit_scroll(bool enable);
void logging_limit_scroll_top(int lines);
void logging_limit_scroll_bottom(int lines);
const char *debug_amplitude(double level);
const char *debug_db(double level_db);
void logging_print_help(void);
int parse_logging_opt(const char *optarg);
void logging_init(void);

17
src/libmisdn/Makefile.am Normal file
View File

@ -0,0 +1,17 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libmisdn.a
libmisdn_a_SOURCES = \
printk.c \
layer1.c \
layer2.c \
tei.c \
fsm.c \
stack.c \
socket.c \
core.c \
hwchannel.c
AM_CPPFLAGS += -D__MISDNL1L2__

25
src/libmisdn/bitops.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef M_BITOPS_H
#define M_BITOPS_H
static inline int test_bit(unsigned int nr, unsigned long *addr)
{
return 1UL & (*addr >> nr);
}
static inline int test_and_set_bit(unsigned int nr, unsigned long *addr)
{
unsigned long old = *addr;
*addr |= 1UL << nr;
return 1UL & (old >> nr);
}
static inline int test_and_clear_bit(unsigned int nr, unsigned long *addr)
{
unsigned long old = *addr;
*addr &= ~(1UL << nr);
return 1UL & (old >> nr);
}
#endif

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_CONTAINER_OF_H
#define _LINUX_CONTAINER_OF_H
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#define typeof_member(T, m) typeof(((T*)0)->m)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof(type, member))); })
/**
* container_of_safe - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
* If IS_ERR_OR_NULL(ptr), ptr is returned unchanged.
*/
#define container_of_safe(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
IS_ERR_OR_NULL(__mptr) ? ERR_CAST(__mptr) : \
((type *)(__mptr - offsetof(type, member))); })
#endif /* _LINUX_CONTAINER_OF_H */

432
src/libmisdn/core.c Normal file
View File

@ -0,0 +1,432 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "mISDNif.h"
#include "core.h"
#include <stdio.h>
#include <stdarg.h>
static u_int debug;
static struct mISDN_dev_list dev_list = {
.lock = 0 //__RW_LOCK_UNLOCKED(dev_list.lock)
};
#if 0
MODULE_AUTHOR("Karsten Keil");
MODULE_LICENSE("GPL");
module_param(debug, uint, S_IRUGO | S_IWUSR);
#endif
static uint64_t device_ids;
#define MAX_DEVICE_ID 63
static LIST_HEAD(Bprotocols);
//static DEFINE_RWLOCK(bp_lock);
#if 0
static void mISDN_dev_release(struct device *dev)
{
/* nothing to do: the device is part of its parent's data structure */
}
static ssize_t id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->id);
}
static DEVICE_ATTR_RO(id);
static ssize_t nrbchan_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->nrbchan);
}
static DEVICE_ATTR_RO(nrbchan);
static ssize_t d_protocols_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->Dprotocols);
}
static DEVICE_ATTR_RO(d_protocols);
static ssize_t b_protocols_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
}
static DEVICE_ATTR_RO(b_protocols);
static ssize_t protocol_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->D.protocol);
}
static DEVICE_ATTR_RO(protocol);
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
strcpy(buf, dev_name(dev));
return strlen(buf);
}
static DEVICE_ATTR_RO(name);
#if 0 /* hangs */
static ssize_t name_set(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int err = 0;
char *out = kmalloc(count + 1, GFP_KERNEL);
if (!out)
return -ENOMEM;
memcpy(out, buf, count);
if (count && out[count - 1] == '\n')
out[--count] = 0;
if (count)
err = device_rename(dev, out);
kfree(out);
return (err < 0) ? err : count;
}
static DEVICE_ATTR_RW(name);
#endif
static ssize_t channelmap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
char *bp = buf;
int i;
for (i = 0; i <= mdev->nrbchan; i++)
*bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
return bp - buf;
}
static DEVICE_ATTR_RO(channelmap);
static struct attribute *mISDN_attrs[] = {
&dev_attr_id.attr,
&dev_attr_d_protocols.attr,
&dev_attr_b_protocols.attr,
&dev_attr_protocol.attr,
&dev_attr_channelmap.attr,
&dev_attr_nrbchan.attr,
&dev_attr_name.attr,
NULL,
};
ATTRIBUTE_GROUPS(mISDN);
static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return 0;
if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
return -ENOMEM;
return 0;
}
static void mISDN_class_release(struct class *cls)
{
/* do nothing, it's static */
}
static struct class mISDN_class = {
.name = "mISDN",
.owner = THIS_MODULE,
.dev_uevent = mISDN_uevent,
.dev_groups = mISDN_groups,
.dev_release = mISDN_dev_release,
.class_release = mISDN_class_release,
};
#endif
struct mISDNdevice
*get_mdevice(u_int id)
{
struct mISDNdevice *dev;
m_hlist_for_each_entry(dev, &dev_list.head, list) {
if (dev->id == id)
return dev;
}
return NULL;
}
int
get_mdevice_count(void)
{
int cnt = 0;
struct mISDNdevice *dev;
m_hlist_for_each_entry(dev, &dev_list.head, list) {
cnt++;
}
return cnt;
}
static int
get_free_devid(void)
{
u_int i;
for (i = 0; i <= MAX_DEVICE_ID; i++)
if (!test_and_set_bit(i, (u_long *)&device_ids))
break;
if (i > MAX_DEVICE_ID)
return -EBUSY;
return i;
}
int
mISDN_register_device(struct mISDNdevice *dev,
void __attribute__((unused)) *parent, const char *name)
{
int err;
err = get_free_devid();
if (err < 0)
goto error1;
dev->id = err;
// device_initialize(dev);
if (name && name[0])
dev_set_name(dev, "%s", name);
else
dev_set_name(dev, "mISDN%d", dev->id);
if (debug & DEBUG_CORE)
printk(KERN_DEBUG "mISDN_register %s %d\n",
dev_name(dev), dev->id);
err = create_stack(dev);
if (err)
goto error1;
// dev->dev.class = &mISDN_class;
// dev->dev.platform_data = dev;
// dev->dev.parent = parent;
// dev_set_drvdata(&dev->dev, dev);
m_hlist_add_head(&dev->list, &dev_list.head);
// err = device_add(&dev->dev);
// if (err)
// goto error3;
return 0;
//error3:
// delete_stack(dev);
// return err;
error1:
return err;
}
EXPORT_SYMBOL(mISDN_register_device);
void
mISDN_unregister_device(struct mISDNdevice *dev) {
if (debug & DEBUG_CORE)
printk(KERN_DEBUG "mISDN_unregister %s %d\n",
dev_name(dev), dev->id);
/* sysfs_remove_link(&dev->dev.kobj, "device"); */
m_hlist_del(&dev->list);
// device_del(&dev->dev);
// dev_set_drvdata(&dev->dev, NULL);
test_and_clear_bit(dev->id, (u_long *)&device_ids);
delete_stack(dev);
// put_device(&dev->dev);
}
EXPORT_SYMBOL(mISDN_unregister_device);
u_int
get_all_Bprotocols(void)
{
struct Bprotocol *bp;
u_int m = 0;
read_lock(&bp_lock);
list_for_each_entry(bp, &Bprotocols, list)
m |= bp->Bprotocols;
read_unlock(&bp_lock);
return m;
}
struct Bprotocol *
get_Bprotocol4mask(u_int m)
{
struct Bprotocol *bp;
read_lock(&bp_lock);
list_for_each_entry(bp, &Bprotocols, list)
if (bp->Bprotocols & m) {
read_unlock(&bp_lock);
return bp;
}
read_unlock(&bp_lock);
return NULL;
}
struct Bprotocol *
get_Bprotocol4id(u_int id)
{
u_int m;
if (id < ISDN_P_B_START || id > 63) {
printk(KERN_WARNING "%s id not in range %d\n",
__func__, id);
return NULL;
}
m = 1 << (id & ISDN_P_B_MASK);
return get_Bprotocol4mask(m);
}
int
mISDN_register_Bprotocol(struct Bprotocol *bp)
{
u_long __attribute__((unused)) flags;
struct Bprotocol *old;
if (debug & DEBUG_CORE)
printk(KERN_DEBUG "%s: %s/%x\n", __func__,
bp->name, bp->Bprotocols);
old = get_Bprotocol4mask(bp->Bprotocols);
if (old) {
printk(KERN_WARNING
"register duplicate protocol old %s/%x new %s/%x\n",
old->name, old->Bprotocols, bp->name, bp->Bprotocols);
return -EBUSY;
}
write_lock_irqsave(&bp_lock, flags);
list_add_tail(&bp->list, &Bprotocols);
write_unlock_irqrestore(&bp_lock, flags);
return 0;
}
EXPORT_SYMBOL(mISDN_register_Bprotocol);
void
mISDN_unregister_Bprotocol(struct Bprotocol *bp)
{
u_long __attribute__((unused)) flags = 0;
if (debug & DEBUG_CORE)
printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
bp->Bprotocols);
write_lock_irqsave(&bp_lock, flags);
list_del(&bp->list);
write_unlock_irqrestore(&bp_lock, flags);
}
EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
static const char *msg_no_channel = "<no channel>";
static const char *msg_no_stack = "<no stack>";
static const char *msg_no_stackdev = "<no stack device>";
const char *mISDNDevName4ch(struct mISDNchannel *ch)
{
if (!ch)
return msg_no_channel;
if (!ch->st)
return msg_no_stack;
if (!ch->st->dev)
return msg_no_stackdev;
return dev_name(ch->st->dev);
};
EXPORT_SYMBOL(mISDNDevName4ch);
int
mISDNInit(u_int _debug)
{
int err;
debug = _debug;
printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
// mISDN_init_clock(&debug);
mISDN_initstack(&debug);
#if 0
err = class_register(&mISDN_class);
if (err)
goto error1;
err = mISDN_inittimer(&debug);
if (err)
goto error2;
#endif
err = Isdnl1_Init(&debug);
if (err)
goto error3;
err = Isdnl2_Init(&debug);
if (err)
goto error4;
err = misdn_sock_init(&debug);
if (err)
goto error5;
return 0;
error5:
Isdnl2_cleanup();
error4:
Isdnl1_cleanup();
error3:
#if 0
mISDN_timer_cleanup();
error2:
class_unregister(&mISDN_class);
error1:
#endif
return err;
}
void mISDN_cleanup(void)
{
misdn_sock_cleanup();
Isdnl2_cleanup();
Isdnl1_cleanup();
// mISDN_timer_cleanup();
// class_unregister(&mISDN_class);
printk(KERN_DEBUG "mISDNcore unloaded\n");
}
int mISDN_work(void)
{
struct mISDNdevice *dev;
int work = 0;
m_hlist_for_each_entry(dev, &dev_list.head, list) {
work |= work_stack(dev);
}
return work;
}
//module_init(mISDNInit);
//module_exit(mISDN_cleanup);

77
src/libmisdn/core.h Normal file
View File

@ -0,0 +1,77 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#ifndef mISDN_CORE_H
#define mISDN_CORE_H
#ifdef __MISDNL1L2__
extern struct mISDNdevice *get_mdevice(u_int);
extern int get_mdevice_count(void);
/* stack status flag */
#define mISDN_STACK_ACTION_MASK 0x0000ffff
#define mISDN_STACK_COMMAND_MASK 0x000f0000
#define mISDN_STACK_STATUS_MASK 0xfff00000
/* action bits 0-15 */
#define mISDN_STACK_WORK 0
#define mISDN_STACK_SETUP 1
#define mISDN_STACK_CLEARING 2
#define mISDN_STACK_RESTART 3
#define mISDN_STACK_WAKEUP 4
#define mISDN_STACK_ABORT 15
/* command bits 16-19 */
#define mISDN_STACK_STOPPED 16
#define mISDN_STACK_INIT 17
#define mISDN_STACK_THREADSTART 18
/* status bits 20-31 */
#define mISDN_STACK_BCHANNEL 20
#define mISDN_STACK_ACTIVE 29
#define mISDN_STACK_RUNNING 30
#define mISDN_STACK_KILLED 31
/* manager options */
#define MGR_OPT_USER 24
#define MGR_OPT_NETWORK 25
extern int connect_Bstack(struct mISDNdevice *, struct mISDNchannel *,
u_int, struct sockaddr_mISDN *);
extern int connect_layer1(struct mISDNdevice *, struct mISDNchannel *,
u_int, struct sockaddr_mISDN *);
extern int create_l2entity(struct mISDNdevice *, struct mISDNchannel *,
u_int, struct sockaddr_mISDN *);
extern int create_stack(struct mISDNdevice *);
extern int create_teimanager(struct mISDNdevice *);
extern void delete_teimanager(struct mISDNchannel *);
extern void delete_channel(struct mISDNchannel *);
extern void delete_stack(struct mISDNdevice *);
extern int work_stack(struct mISDNdevice *dev);
extern void mISDN_initstack(u_int *);
extern int misdn_sock_init(u_int *);
extern void misdn_sock_cleanup(void);
extern void add_layer2(struct mISDNchannel *, struct mISDNstack *);
extern void __add_layer2(struct mISDNchannel *, struct mISDNstack *);
extern u_int get_all_Bprotocols(void);
struct Bprotocol *get_Bprotocol4mask(u_int);
struct Bprotocol *get_Bprotocol4id(u_int);
extern int mISDN_inittimer(u_int *);
extern void mISDN_timer_cleanup(void);
extern int Isdnl1_Init(u_int *);
extern void Isdnl1_cleanup(void);
extern int Isdnl2_Init(u_int *);
extern void Isdnl2_cleanup(void);
extern void mISDN_init_clock(u_int *);
#endif
extern int mISDNInit(uint debug);
extern void mISDN_cleanup(void);
extern int mISDN_work(void);
#endif

177
src/libmisdn/fsm.c Normal file
View File

@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* finite state machine implementation
*
* Author Karsten Keil <kkeil@novell.com>
*
* Thanks to Jan den Ouden
* Fritz Elfert
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "layer2.h"
#include "core.h"
#include <stdio.h>
#include <stdarg.h>
#include "fsm.h"
#define FSM_TIMER_DEBUG 0
int
mISDN_FsmNew(struct Fsm *fsm,
struct FsmNode *fnlist, int fncount)
{
int i;
fsm->jumpmatrix =
kzalloc(sizeof(FSMFNPTR) * fsm->state_count * fsm->event_count,
GFP_KERNEL);
if (fsm->jumpmatrix == NULL)
return -ENOMEM;
for (i = 0; i < fncount; i++)
if ((fnlist[i].state >= fsm->state_count) ||
(fnlist[i].event >= fsm->event_count)) {
printk(KERN_ERR
"mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n",
i, (long)fnlist[i].state, (long)fsm->state_count,
(long)fnlist[i].event, (long)fsm->event_count);
} else
fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
return 0;
}
EXPORT_SYMBOL(mISDN_FsmNew);
void
mISDN_FsmFree(struct Fsm *fsm)
{
kfree((void *) fsm->jumpmatrix);
}
EXPORT_SYMBOL(mISDN_FsmFree);
int
mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg)
{
FSMFNPTR r;
if ((fi->state >= fi->fsm->state_count) ||
(event >= fi->fsm->event_count)) {
printk(KERN_ERR
"mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
(long)fi->state, (long)fi->fsm->state_count, event,
(long)fi->fsm->event_count);
return 1;
}
r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
if (r) {
if (fi->debug)
fi->printdebug(fi, "State %s Event %s",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
r(fi, event, arg);
return 0;
} else {
if (fi->debug)
fi->printdebug(fi, "State %s Event %s no action",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
return 1;
}
}
EXPORT_SYMBOL(mISDN_FsmEvent);
void
mISDN_FsmChangeState(struct FsmInst *fi, int newstate)
{
fi->state = newstate;
if (fi->debug)
fi->printdebug(fi, "ChangeState %s",
fi->fsm->strState[newstate]);
}
EXPORT_SYMBOL(mISDN_FsmChangeState);
static void
FsmExpireTimer(void *data)
{
struct FsmTimer *ft = data;
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
#endif
osmo_timer_del(&ft->tl);
mISDN_FsmEvent(ft->fi, ft->event, ft->arg);
}
void
mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
{
ft->fi = fi;
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
#endif
ft->func = FsmExpireTimer;
}
EXPORT_SYMBOL(mISDN_FsmInitTimer);
void
mISDN_FsmDelTimer(struct FsmTimer *ft, int __attribute__((unused)) where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d",
(long) ft, where);
#endif
osmo_timer_del(&ft->tl);
}
EXPORT_SYMBOL(mISDN_FsmDelTimer);
int
mISDN_FsmAddTimer(struct FsmTimer *ft,
int millisec, int event, void *arg, int __attribute__((unused)) where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d",
(long) ft, millisec, where);
#endif
// if (ft->tl.linked) {
if (ft->fi->debug) {
printk(KERN_WARNING
"mISDN_FsmAddTimer: timer already active!\n");
ft->fi->printdebug(ft->fi,
"mISDN_FsmAddTimer already active!");
}
return -1;
// }
ft->event = event;
ft->arg = arg;
osmo_timer_setup(&ft->tl, ft->func, ft);
osmo_timer_schedule(&ft->tl, millisec / 1000, (millisec % 1000) * 1000);
return 0;
}
EXPORT_SYMBOL(mISDN_FsmAddTimer);
void
mISDN_FsmRestartTimer(struct FsmTimer *ft,
int millisec, int event, void *arg, int __attribute__((unused)) where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d",
(long) ft, millisec, where);
#endif
// if (ft->tl.linked)
osmo_timer_del(&ft->tl);
ft->event = event;
ft->arg = arg;
osmo_timer_setup(&ft->tl, ft->func, ft);
osmo_timer_schedule(&ft->tl, millisec / 1000, (millisec % 1000) * 1000);
}
EXPORT_SYMBOL(mISDN_FsmRestartTimer);

59
src/libmisdn/fsm.h Normal file
View File

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Thanks to Jan den Ouden
* Fritz Elfert
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#ifndef _MISDN_FSM_H
#define _MISDN_FSM_H
#include "timer.h"
/* Statemachine */
struct FsmInst;
typedef void (*FSMFNPTR)(struct FsmInst *, int, void *);
struct Fsm {
FSMFNPTR *jumpmatrix;
int state_count, event_count;
char **strEvent, **strState;
};
struct FsmInst {
struct Fsm *fsm;
int state;
int debug;
void *userdata;
int userint;
void (*printdebug) (struct FsmInst *, char *, ...);
};
struct FsmNode {
int state, event;
void (*routine) (struct FsmInst *, int, void *);
};
struct FsmTimer {
struct FsmInst *fi;
struct osmo_timer_list tl;
void *func;
int event;
void *arg;
};
extern int mISDN_FsmNew(struct Fsm *, struct FsmNode *, int);
extern void mISDN_FsmFree(struct Fsm *);
extern int mISDN_FsmEvent(struct FsmInst *, int , void *);
extern void mISDN_FsmChangeState(struct FsmInst *, int);
extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *);
extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int);
extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int);
extern void mISDN_FsmDelTimer(struct FsmTimer *, int);
#endif

523
src/libmisdn/hwchannel.c Normal file
View File

@ -0,0 +1,523 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "mISDNhw.h"
#include <stdio.h>
#include <stdarg.h>
#define INIT_WORK(q, fn)
#define schedule_work(q)
#define flush_work(q)
#if 0
static void
dchannel_bh(struct work_struct *ws)
{
struct dchannel *dch = container_of(ws, struct dchannel, workq);
struct sk_buff *skb;
int err;
if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
while ((skb = skb_dequeue(&dch->rqueue))) {
if (likely(dch->dev.D.peer)) {
err = dch->dev.D.recv(dch->dev.D.peer, skb);
if (err)
dev_kfree_skb(skb);
} else
dev_kfree_skb(skb);
}
}
if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
if (dch->phfunc)
dch->phfunc(dch);
}
}
static void
bchannel_bh(struct work_struct *ws)
{
struct bchannel *bch = container_of(ws, struct bchannel, workq);
struct sk_buff *skb;
int err;
if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
while ((skb = skb_dequeue(&bch->rqueue))) {
bch->rcount--;
if (likely(bch->ch.peer)) {
err = bch->ch.recv(bch->ch.peer, skb);
if (err)
dev_kfree_skb(skb);
} else
dev_kfree_skb(skb);
}
}
}
#endif
int
mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
{
test_and_set_bit(FLG_HDLC, &ch->Flags);
ch->maxlen = maxlen;
ch->hw = NULL;
ch->rx_skb = NULL;
ch->tx_skb = NULL;
ch->tx_idx = 0;
ch->phfunc = phf;
skb_queue_head_init(&ch->squeue);
skb_queue_head_init(&ch->rqueue);
INIT_LIST_HEAD(&ch->dev.bchannels);
INIT_WORK(&ch->workq, dchannel_bh);
return 0;
}
EXPORT_SYMBOL(mISDN_initdchannel);
int
mISDN_initbchannel(struct bchannel *ch, unsigned short maxlen,
unsigned short minlen)
{
ch->Flags = 0;
ch->minlen = minlen;
ch->next_minlen = minlen;
ch->init_minlen = minlen;
ch->maxlen = maxlen;
ch->next_maxlen = maxlen;
ch->init_maxlen = maxlen;
ch->hw = NULL;
ch->rx_skb = NULL;
ch->tx_skb = NULL;
ch->tx_idx = 0;
skb_queue_head_init(&ch->rqueue);
ch->rcount = 0;
ch->next_skb = NULL;
INIT_WORK(&ch->workq, bchannel_bh);
return 0;
}
EXPORT_SYMBOL(mISDN_initbchannel);
int
mISDN_freedchannel(struct dchannel *ch)
{
if (ch->tx_skb) {
dev_kfree_skb(ch->tx_skb);
ch->tx_skb = NULL;
}
if (ch->rx_skb) {
dev_kfree_skb(ch->rx_skb);
ch->rx_skb = NULL;
}
skb_queue_purge(&ch->squeue);
skb_queue_purge(&ch->rqueue);
flush_work(&ch->workq);
return 0;
}
EXPORT_SYMBOL(mISDN_freedchannel);
void
mISDN_clear_bchannel(struct bchannel *ch)
{
if (ch->tx_skb) {
dev_kfree_skb(ch->tx_skb);
ch->tx_skb = NULL;
}
ch->tx_idx = 0;
if (ch->rx_skb) {
dev_kfree_skb(ch->rx_skb);
ch->rx_skb = NULL;
}
if (ch->next_skb) {
dev_kfree_skb(ch->next_skb);
ch->next_skb = NULL;
}
test_and_clear_bit(FLG_TX_BUSY, &ch->Flags);
test_and_clear_bit(FLG_TX_NEXT, &ch->Flags);
test_and_clear_bit(FLG_ACTIVE, &ch->Flags);
test_and_clear_bit(FLG_FILLEMPTY, &ch->Flags);
test_and_clear_bit(FLG_TX_EMPTY, &ch->Flags);
test_and_clear_bit(FLG_RX_OFF, &ch->Flags);
ch->dropcnt = 0;
ch->minlen = ch->init_minlen;
ch->next_minlen = ch->init_minlen;
ch->maxlen = ch->init_maxlen;
ch->next_maxlen = ch->init_maxlen;
skb_queue_purge(&ch->rqueue);
ch->rcount = 0;
}
EXPORT_SYMBOL(mISDN_clear_bchannel);
void
mISDN_freebchannel(struct bchannel *ch)
{
// cancel_work_sync(&ch->workq);
mISDN_clear_bchannel(ch);
}
EXPORT_SYMBOL(mISDN_freebchannel);
int
mISDN_ctrl_bchannel(struct bchannel *bch, struct mISDN_ctrl_req *cq)
{
int ret = 0;
switch (cq->op) {
case MISDN_CTRL_GETOP:
cq->op = MISDN_CTRL_RX_BUFFER | MISDN_CTRL_FILL_EMPTY |
MISDN_CTRL_RX_OFF;
break;
case MISDN_CTRL_FILL_EMPTY:
if (cq->p1) {
memset(bch->fill, cq->p2 & 0xff, MISDN_BCH_FILL_SIZE);
test_and_set_bit(FLG_FILLEMPTY, &bch->Flags);
} else {
test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
}
break;
case MISDN_CTRL_RX_OFF:
/* read back dropped byte count */
cq->p2 = bch->dropcnt;
if (cq->p1)
test_and_set_bit(FLG_RX_OFF, &bch->Flags);
else
test_and_clear_bit(FLG_RX_OFF, &bch->Flags);
bch->dropcnt = 0;
break;
case MISDN_CTRL_RX_BUFFER:
if (cq->p2 > MISDN_CTRL_RX_SIZE_IGNORE)
bch->next_maxlen = cq->p2;
if (cq->p1 > MISDN_CTRL_RX_SIZE_IGNORE)
bch->next_minlen = cq->p1;
/* we return the old values */
cq->p1 = bch->minlen;
cq->p2 = bch->maxlen;
break;
default:
printk(KERN_INFO "mISDN unhandled control %x operation\n", cq->op);
ret = -EINVAL;
break;
}
return ret;
}
EXPORT_SYMBOL(mISDN_ctrl_bchannel);
static inline u_int
get_sapi_tei(u_char *p)
{
u_int sapi, tei;
sapi = *p >> 2;
tei = p[1] >> 1;
return sapi | (tei << 8);
}
void
recv_Dchannel(struct dchannel *dch)
{
struct mISDNhead *hh;
if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
dev_kfree_skb(dch->rx_skb);
dch->rx_skb = NULL;
return;
}
hh = mISDN_HEAD_P(dch->rx_skb);
hh->prim = PH_DATA_IND;
hh->id = get_sapi_tei(dch->rx_skb->data);
skb_queue_tail(&dch->rqueue, dch->rx_skb);
dch->rx_skb = NULL;
schedule_event(dch, FLG_RECVQUEUE);
}
EXPORT_SYMBOL(recv_Dchannel);
void
recv_Echannel(struct dchannel *ech, struct dchannel *dch)
{
struct mISDNhead *hh;
if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */
dev_kfree_skb(ech->rx_skb);
ech->rx_skb = NULL;
return;
}
hh = mISDN_HEAD_P(ech->rx_skb);
hh->prim = PH_DATA_E_IND;
hh->id = get_sapi_tei(ech->rx_skb->data);
skb_queue_tail(&dch->rqueue, ech->rx_skb);
ech->rx_skb = NULL;
schedule_event(dch, FLG_RECVQUEUE);
}
EXPORT_SYMBOL(recv_Echannel);
void
recv_Bchannel(struct bchannel *bch, unsigned int id, bool force)
{
struct mISDNhead *hh;
/* if allocation did fail upper functions still may call us */
if (unlikely(!bch->rx_skb))
return;
if (unlikely(!bch->rx_skb->len)) {
/* we have no data to send - this may happen after recovery
* from overflow or too small allocation.
* We need to free the buffer here */
dev_kfree_skb(bch->rx_skb);
bch->rx_skb = NULL;
} else {
if (test_bit(FLG_TRANSPARENT, &bch->Flags) &&
(bch->rx_skb->len < bch->minlen) && !force)
return;
hh = mISDN_HEAD_P(bch->rx_skb);
hh->prim = PH_DATA_IND;
hh->id = id;
if (bch->rcount >= 64) {
printk(KERN_WARNING
"B%d receive queue overflow - flushing!\n",
bch->nr);
skb_queue_purge(&bch->rqueue);
}
bch->rcount++;
skb_queue_tail(&bch->rqueue, bch->rx_skb);
bch->rx_skb = NULL;
schedule_event(bch, FLG_RECVQUEUE);
}
}
EXPORT_SYMBOL(recv_Bchannel);
void
recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
{
skb_queue_tail(&dch->rqueue, skb);
schedule_event(dch, FLG_RECVQUEUE);
}
EXPORT_SYMBOL(recv_Dchannel_skb);
void
recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
{
if (bch->rcount >= 64) {
printk(KERN_WARNING "B-channel %p receive queue overflow, "
"flushing!\n", bch);
skb_queue_purge(&bch->rqueue);
bch->rcount = 0;
}
bch->rcount++;
skb_queue_tail(&bch->rqueue, skb);
schedule_event(bch, FLG_RECVQUEUE);
}
EXPORT_SYMBOL(recv_Bchannel_skb);
static void
confirm_Dsend(struct dchannel *dch)
{
struct sk_buff *skb;
skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
0, NULL, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "%s: no skb id %x\n", __func__,
mISDN_HEAD_ID(dch->tx_skb));
return;
}
skb_queue_tail(&dch->rqueue, skb);
schedule_event(dch, FLG_RECVQUEUE);
}
int
get_next_dframe(struct dchannel *dch)
{
dch->tx_idx = 0;
dch->tx_skb = skb_dequeue(&dch->squeue);
if (dch->tx_skb) {
confirm_Dsend(dch);
return 1;
}
dch->tx_skb = NULL;
test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
return 0;
}
EXPORT_SYMBOL(get_next_dframe);
static void
confirm_Bsend(struct bchannel *bch)
{
struct sk_buff *skb;
if (bch->rcount >= 64) {
printk(KERN_WARNING "B-channel %p receive queue overflow, "
"flushing!\n", bch);
skb_queue_purge(&bch->rqueue);
bch->rcount = 0;
}
skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
0, NULL, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "%s: no skb id %x\n", __func__,
mISDN_HEAD_ID(bch->tx_skb));
return;
}
bch->rcount++;
skb_queue_tail(&bch->rqueue, skb);
schedule_event(bch, FLG_RECVQUEUE);
}
int
get_next_bframe(struct bchannel *bch)
{
bch->tx_idx = 0;
if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
bch->tx_skb = bch->next_skb;
if (bch->tx_skb) {
bch->next_skb = NULL;
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
/* confirm imediately to allow next data */
confirm_Bsend(bch);
return 1;
} else {
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
printk(KERN_WARNING "B TX_NEXT without skb\n");
}
}
bch->tx_skb = NULL;
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
return 0;
}
EXPORT_SYMBOL(get_next_bframe);
void
queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
{
struct mISDNhead *hh;
if (!skb) {
_queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
} else {
if (ch->peer) {
hh = mISDN_HEAD_P(skb);
hh->prim = pr;
hh->id = id;
if (!ch->recv(ch->peer, skb))
return;
}
dev_kfree_skb(skb);
}
}
EXPORT_SYMBOL(queue_ch_frame);
int
dchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
{
/* check oversize */
if (skb->len <= 0) {
printk(KERN_WARNING "%s: skb too small\n", __func__);
return -EINVAL;
}
if ((int)skb->len > ch->maxlen) {
printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
__func__, skb->len, ch->maxlen);
return -EINVAL;
}
/* HW lock must be obtained */
if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
skb_queue_tail(&ch->squeue, skb);
return 0;
} else {
/* write to fifo */
ch->tx_skb = skb;
ch->tx_idx = 0;
return 1;
}
}
EXPORT_SYMBOL(dchannel_senddata);
int
bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
{
/* check oversize */
if (skb->len <= 0) {
printk(KERN_WARNING "%s: skb too small\n", __func__);
return -EINVAL;
}
if (skb->len > ch->maxlen) {
printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
__func__, skb->len, ch->maxlen);
return -EINVAL;
}
/* HW lock must be obtained */
/* check for pending next_skb */
if (ch->next_skb) {
printk(KERN_WARNING
"%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
__func__, skb->len, ch->next_skb->len);
return -EBUSY;
}
if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
ch->next_skb = skb;
return 0;
} else {
/* write to fifo */
ch->tx_skb = skb;
ch->tx_idx = 0;
confirm_Bsend(ch);
return 1;
}
}
EXPORT_SYMBOL(bchannel_senddata);
/* The function allocates a new receive skb on demand with a size for the
* requirements of the current protocol. It returns the tailroom of the
* receive skb or an error.
*/
int
bchannel_get_rxbuf(struct bchannel *bch, int reqlen)
{
int len;
if (bch->rx_skb) {
len = skb_tailroom(bch->rx_skb);
if (len < reqlen) {
printk(KERN_WARNING "B%d no space for %d (only %d) bytes\n",
bch->nr, reqlen, len);
if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
/* send what we have now and try a new buffer */
recv_Bchannel(bch, 0, true);
} else {
/* on HDLC we have to drop too big frames */
return -EMSGSIZE;
}
} else {
return len;
}
}
/* update current min/max length first */
if (unlikely(bch->maxlen != bch->next_maxlen))
bch->maxlen = bch->next_maxlen;
if (unlikely(bch->minlen != bch->next_minlen))
bch->minlen = bch->next_minlen;
if (unlikely(reqlen > bch->maxlen))
return -EMSGSIZE;
if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
if (reqlen >= bch->minlen) {
len = reqlen;
} else {
len = 2 * bch->minlen;
if (len > bch->maxlen)
len = bch->maxlen;
}
} else {
/* with HDLC we do not know the length yet */
len = bch->maxlen;
}
bch->rx_skb = mI_alloc_skb(len, GFP_ATOMIC);
if (!bch->rx_skb) {
printk(KERN_WARNING "B%d receive no memory for %d bytes\n", bch->nr, len);
len = -ENOMEM;
}
return len;
}
EXPORT_SYMBOL(bchannel_get_rxbuf);

415
src/libmisdn/layer1.c Normal file
View File

@ -0,0 +1,415 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "mISDNhw.h"
#include "core.h"
#include "layer1.h"
#include "fsm.h"
#include <stdio.h>
#include <stdarg.h>
static u_int *debug;
struct layer1 {
u_long Flags;
struct FsmInst l1m;
struct FsmTimer timer3;
struct FsmTimer timerX;
int delay;
int t3_value;
struct dchannel *dch;
dchannel_l1callback *dcb;
};
#define TIMER3_DEFAULT_VALUE 7000
static
struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL};
enum {
ST_L1_F2,
ST_L1_F3,
ST_L1_F4,
ST_L1_F5,
ST_L1_F6,
ST_L1_F7,
ST_L1_F8,
};
#define L1S_STATE_COUNT (ST_L1_F8 + 1)
static char *strL1SState[] =
{
"ST_L1_F2",
"ST_L1_F3",
"ST_L1_F4",
"ST_L1_F5",
"ST_L1_F6",
"ST_L1_F7",
"ST_L1_F8",
};
enum {
EV_PH_ACTIVATE,
EV_PH_DEACTIVATE,
EV_RESET_IND,
EV_DEACT_CNF,
EV_DEACT_IND,
EV_POWER_UP,
EV_ANYSIG_IND,
EV_INFO2_IND,
EV_INFO4_IND,
EV_TIMER_DEACT,
EV_TIMER_ACT,
EV_TIMER3,
};
#define L1_EVENT_COUNT (EV_TIMER3 + 1)
static char *strL1Event[] =
{
"EV_PH_ACTIVATE",
"EV_PH_DEACTIVATE",
"EV_RESET_IND",
"EV_DEACT_CNF",
"EV_DEACT_IND",
"EV_POWER_UP",
"EV_ANYSIG_IND",
"EV_INFO2_IND",
"EV_INFO4_IND",
"EV_TIMER_DEACT",
"EV_TIMER_ACT",
"EV_TIMER3",
};
static void
l1m_debug(struct FsmInst *fi, char *fmt, ...)
{
struct layer1 __attribute__((unused)) *l1 = fi->userdata;
char buffer[4096], *b = buffer;
int s = sizeof(buffer) - 1;
va_list va;
va_start(va, fmt);
vsnprintf(b, s, fmt, va);
va_end(va);
printk(KERN_DEBUG "%s: %s\n", dev_name(&l1->dch->dev), b);
va_end(va);
}
static void
l1_reset(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
mISDN_FsmChangeState(fi, ST_L1_F3);
}
static void
l1_deact_cnf(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L1_F3);
if (test_bit(FLG_L1_ACTIVATING, &l1->Flags))
l1->dcb(l1->dch, HW_POWERUP_REQ);
}
static void
l1_deact_req_s(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L1_F3);
mISDN_FsmRestartTimer(&l1->timerX, 550, EV_TIMER_DEACT, NULL, 2);
test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags);
}
static void
l1_power_up_s(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
mISDN_FsmChangeState(fi, ST_L1_F4);
l1->dcb(l1->dch, INFO3_P8);
} else
mISDN_FsmChangeState(fi, ST_L1_F3);
}
static void
l1_go_F5(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
mISDN_FsmChangeState(fi, ST_L1_F5);
}
static void
l1_go_F8(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
mISDN_FsmChangeState(fi, ST_L1_F8);
}
static void
l1_info2_ind(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L1_F6);
l1->dcb(l1->dch, INFO3_P8);
}
static void
l1_info4_ind(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L1_F7);
l1->dcb(l1->dch, INFO3_P8);
if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags))
mISDN_FsmDelTimer(&l1->timerX, 4);
if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) {
if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags))
mISDN_FsmDelTimer(&l1->timer3, 3);
mISDN_FsmRestartTimer(&l1->timerX, 110, EV_TIMER_ACT, NULL, 2);
test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags);
}
}
static void
l1_timer3(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags);
if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
l1->dcb(l1->dch, HW_D_NOBLOCKED);
l1->dcb(l1->dch, PH_DEACTIVATE_IND);
}
if (l1->l1m.state != ST_L1_F6) {
mISDN_FsmChangeState(fi, ST_L1_F3);
/* do not force anything here, we need send INFO 0 */
}
}
static void
l1_timer_act(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags);
test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags);
l1->dcb(l1->dch, PH_ACTIVATE_IND);
}
static void
l1_timer_deact(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags);
test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags);
if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
l1->dcb(l1->dch, HW_D_NOBLOCKED);
l1->dcb(l1->dch, PH_DEACTIVATE_IND);
l1->dcb(l1->dch, HW_DEACT_REQ);
}
static void
l1_activate_s(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmRestartTimer(&l1->timer3, l1->t3_value, EV_TIMER3, NULL, 2);
test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
/* Tell HW to send INFO 1 */
l1->dcb(l1->dch, HW_RESET_REQ);
}
static void
l1_activate_no(struct FsmInst *fi, int __attribute__((unused)) event, void __attribute__((unused)) *arg)
{
struct layer1 *l1 = fi->userdata;
if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) &&
(!test_bit(FLG_L1_T3RUN, &l1->Flags))) {
test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags);
if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
l1->dcb(l1->dch, HW_D_NOBLOCKED);
l1->dcb(l1->dch, PH_DEACTIVATE_IND);
}
}
static struct FsmNode L1SFnList[] =
{
{ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s},
{ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
{ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
{ST_L1_F3, EV_RESET_IND, l1_reset},
{ST_L1_F4, EV_RESET_IND, l1_reset},
{ST_L1_F5, EV_RESET_IND, l1_reset},
{ST_L1_F6, EV_RESET_IND, l1_reset},
{ST_L1_F7, EV_RESET_IND, l1_reset},
{ST_L1_F8, EV_RESET_IND, l1_reset},
{ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F6, EV_DEACT_IND, l1_deact_req_s},
{ST_L1_F7, EV_DEACT_IND, l1_deact_req_s},
{ST_L1_F8, EV_DEACT_IND, l1_deact_req_s},
{ST_L1_F3, EV_POWER_UP, l1_power_up_s},
{ST_L1_F4, EV_ANYSIG_IND, l1_go_F5},
{ST_L1_F6, EV_ANYSIG_IND, l1_go_F8},
{ST_L1_F7, EV_ANYSIG_IND, l1_go_F8},
{ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F3, EV_TIMER3, l1_timer3},
{ST_L1_F4, EV_TIMER3, l1_timer3},
{ST_L1_F5, EV_TIMER3, l1_timer3},
{ST_L1_F6, EV_TIMER3, l1_timer3},
{ST_L1_F8, EV_TIMER3, l1_timer3},
{ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
{ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
};
static void
release_l1(struct layer1 *l1) {
mISDN_FsmDelTimer(&l1->timerX, 0);
mISDN_FsmDelTimer(&l1->timer3, 0);
if (l1->dch)
l1->dch->l1 = NULL;
module_put(THIS_MODULE);
kfree(l1);
}
int
l1_event(struct layer1 *l1, u_int event)
{
int err = 0;
if (!l1)
return -EINVAL;
switch (event) {
case HW_RESET_IND:
mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL);
break;
case HW_DEACT_IND:
mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL);
break;
case HW_POWERUP_IND:
mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL);
break;
case HW_DEACT_CNF:
mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL);
break;
case ANYSIGNAL:
mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
break;
case LOSTFRAMING:
mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
break;
case INFO2:
mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL);
break;
case INFO4_P8:
mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
break;
case INFO4_P10:
mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
break;
case PH_ACTIVATE_REQ:
if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
l1->dcb(l1->dch, PH_ACTIVATE_IND);
else {
test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags);
mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL);
}
break;
case CLOSE_CHANNEL:
release_l1(l1);
break;
default:
if ((event & ~HW_TIMER3_VMASK) == HW_TIMER3_VALUE) {
int val = event & HW_TIMER3_VMASK;
if (val < 5)
val = 5;
if (val > 30)
val = 30;
l1->t3_value = val;
break;
}
if (*debug & DEBUG_L1)
printk(KERN_DEBUG "%s %x unhandled\n",
__func__, event);
err = -EINVAL;
}
return err;
}
EXPORT_SYMBOL(l1_event);
int
create_l1(struct dchannel *dch, dchannel_l1callback *dcb) {
struct layer1 *nl1;
nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC);
if (!nl1) {
printk(KERN_ERR "kmalloc struct layer1 failed\n");
return -ENOMEM;
}
nl1->l1m.fsm = &l1fsm_s;
nl1->l1m.state = ST_L1_F3;
nl1->Flags = 0;
nl1->t3_value = TIMER3_DEFAULT_VALUE;
nl1->l1m.debug = *debug & DEBUG_L1_FSM;
nl1->l1m.userdata = nl1;
nl1->l1m.userint = 0;
nl1->l1m.printdebug = l1m_debug;
nl1->dch = dch;
nl1->dcb = dcb;
mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer3);
mISDN_FsmInitTimer(&nl1->l1m, &nl1->timerX);
__module_get(THIS_MODULE);
dch->l1 = nl1;
return 0;
}
EXPORT_SYMBOL(create_l1);
int
Isdnl1_Init(u_int *deb)
{
debug = deb;
l1fsm_s.state_count = L1S_STATE_COUNT;
l1fsm_s.event_count = L1_EVENT_COUNT;
l1fsm_s.strEvent = strL1Event;
l1fsm_s.strState = strL1SState;
return mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
}
void
Isdnl1_cleanup(void)
{
mISDN_FsmFree(&l1fsm_s);
}

16
src/libmisdn/layer1.h Normal file
View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* Layer 1 defines
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#define FLG_L1_ACTIVATING 1
#define FLG_L1_ACTIVATED 2
#define FLG_L1_DEACTTIMER 3
#define FLG_L1_ACTTIMER 4
#define FLG_L1_T3RUN 5
#define FLG_L1_PULL_REQ 6
#define FLG_L1_UINT 7
#define FLG_L1_DBLOCKED 8

2266
src/libmisdn/layer2.c Normal file

File diff suppressed because it is too large Load Diff

130
src/libmisdn/layer2.h Normal file
View File

@ -0,0 +1,130 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Layer 2 defines
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "mISDNif.h"
#include "fsm.h"
#define MAX_WINDOW 8
struct manager {
struct mISDNchannel ch;
struct mISDNchannel bcast;
u_long options;
struct list_head layer2;
rwlock_t lock;
struct FsmInst deact;
struct FsmTimer datimer;
struct sk_buff_head sendq;
struct mISDNchannel *up;
u_int nextid;
u_int lastid;
};
struct teimgr {
int ri;
int rcnt;
struct FsmInst tei_m;
struct FsmTimer timer;
int tval, nval;
struct layer2 *l2;
struct manager *mgr;
};
struct laddr {
u_char A;
u_char B;
};
struct layer2 {
struct list_head list;
struct mISDNchannel ch;
u_long flag;
int id;
struct mISDNchannel *up;
signed char sapi;
signed char tei;
struct laddr addr;
u_int maxlen;
struct teimgr *tm;
u_int vs, va, vr;
int rc;
u_int window;
u_int sow;
struct FsmInst l2m;
struct FsmTimer t200, t203;
int T200, N200, T203;
u_int next_id;
u_int down_id;
struct sk_buff *windowar[MAX_WINDOW];
struct sk_buff_head i_queue;
struct sk_buff_head ui_queue;
struct sk_buff_head down_queue;
struct sk_buff_head tmp_queue;
};
enum {
ST_L2_1,
ST_L2_2,
ST_L2_3,
ST_L2_4,
ST_L2_5,
ST_L2_6,
ST_L2_7,
ST_L2_8,
};
#define L2_STATE_COUNT (ST_L2_8 + 1)
extern struct layer2 *create_l2(struct mISDNchannel *, u_int,
u_long, int, int);
extern int tei_l2(struct layer2 *, u_int, u_long arg);
/* from tei.c */
extern int l2_tei(struct layer2 *, u_int, u_long arg);
extern void TEIrelease(struct layer2 *);
extern int TEIInit(u_int *);
extern void TEIFree(void);
#define MAX_L2HEADER_LEN 4
#define RR 0x01
#define RNR 0x05
#define REJ 0x09
#define SABME 0x6f
#define SABM 0x2f
#define DM 0x0f
#define UI 0x03
#define DISC 0x43
#define UA 0x63
#define FRMR 0x87
#define XID 0xaf
#define CMD 0
#define RSP 1
#define LC_FLUSH_WAIT 1
#define FLG_LAPB 0
#define FLG_LAPD 1
#define FLG_ORIG 2
#define FLG_MOD128 3
#define FLG_PEND_REL 4
#define FLG_L3_INIT 5
#define FLG_T200_RUN 6
#define FLG_ACK_PEND 7
#define FLG_REJEXC 8
#define FLG_OWN_BUSY 9
#define FLG_PEER_BUSY 10
#define FLG_DCHAN_BUSY 11
#define FLG_L1_ACTIV 12
#define FLG_ESTAB_PEND 13
#define FLG_PTP 14
#define FLG_FIXED_TEI 15
#define FLG_L2BLOCK 16
#define FLG_L1_NOTREADY 17
#define FLG_LAPD_NET 18

192
src/libmisdn/mISDNhw.h Normal file
View File

@ -0,0 +1,192 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Basic declarations for the mISDN HW channels
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#ifndef MISDNHW_H
#define MISDNHW_H
#include "mISDNif.h"
#include <stdbool.h>
/*
* HW DEBUG 0xHHHHGGGG
* H - hardware driver specific bits
* G - for all drivers
*/
#define DEBUG_HW 0x00000001
#define DEBUG_HW_OPEN 0x00000002
#define DEBUG_HW_DCHANNEL 0x00000100
#define DEBUG_HW_DFIFO 0x00000200
#define DEBUG_HW_BCHANNEL 0x00001000
#define DEBUG_HW_BFIFO 0x00002000
#define MAX_DFRAME_LEN_L1 300
#define MAX_MON_FRAME 32
#define MAX_LOG_SPACE 2048
#define MISDN_COPY_SIZE 32
/* channel->Flags bit field */
#define FLG_TX_BUSY 0 /* tx_buf in use */
#define FLG_TX_NEXT 1 /* next_skb in use */
#define FLG_L1_BUSY 2 /* L1 is permanent busy */
#define FLG_L2_ACTIVATED 3 /* activated from L2 */
#define FLG_OPEN 5 /* channel is in use */
#define FLG_ACTIVE 6 /* channel is activated */
#define FLG_BUSY_TIMER 7
/* channel type */
#define FLG_DCHANNEL 8 /* channel is D-channel */
#define FLG_BCHANNEL 9 /* channel is B-channel */
#define FLG_ECHANNEL 10 /* channel is E-channel */
#define FLG_TRANSPARENT 12 /* channel use transparent data */
#define FLG_HDLC 13 /* channel use hdlc data */
#define FLG_L2DATA 14 /* channel use L2 DATA primitivs */
#define FLG_ORIGIN 15 /* channel is on origin site */
/* channel specific stuff */
#define FLG_FILLEMPTY 16 /* fill fifo on first frame (empty) */
/* arcofi specific */
#define FLG_ARCOFI_TIMER 17
#define FLG_ARCOFI_ERROR 18
/* isar specific */
#define FLG_INITIALIZED 17
#define FLG_DLEETX 18
#define FLG_LASTDLE 19
#define FLG_FIRST 20
#define FLG_LASTDATA 21
#define FLG_NMD_DATA 22
#define FLG_FTI_RUN 23
#define FLG_LL_OK 24
#define FLG_LL_CONN 25
#define FLG_DTMFSEND 26
#define FLG_TX_EMPTY 27
/* stop sending received data upstream */
#define FLG_RX_OFF 28
/* workq events */
#define FLG_RECVQUEUE 30
#define FLG_PHCHANGE 31
#define schedule_event(s, ev) do { \
test_and_set_bit(ev, &((s)->Flags)); \
schedule_work(&((s)->workq)); \
} while (0)
struct dchannel {
struct mISDNdevice dev;
u_long Flags;
// struct work_struct workq;
void (*phfunc) (struct dchannel *);
u_int state;
void *l1;
void *hw;
int slot; /* multiport card channel slot */
// struct osmo_timer_list_list timer;
/* receive data */
struct sk_buff *rx_skb;
int maxlen;
/* send data */
struct sk_buff_head squeue;
struct sk_buff_head rqueue;
struct sk_buff *tx_skb;
int tx_idx;
int debug;
/* statistics */
int err_crc;
int err_tx;
int err_rx;
};
typedef int (dchannel_l1callback)(struct dchannel *, u_int);
extern int create_l1(struct dchannel *, dchannel_l1callback *);
/* private L1 commands */
#define INFO0 0x8002
#define INFO1 0x8102
#define INFO2 0x8202
#define INFO3_P8 0x8302
#define INFO3_P10 0x8402
#define INFO4_P8 0x8502
#define INFO4_P10 0x8602
#define LOSTFRAMING 0x8702
#define ANYSIGNAL 0x8802
#define HW_POWERDOWN 0x8902
#define HW_RESET_REQ 0x8a02
#define HW_POWERUP_REQ 0x8b02
#define HW_DEACT_REQ 0x8c02
#define HW_ACTIVATE_REQ 0x8e02
#define HW_D_NOBLOCKED 0x8f02
#define HW_RESET_IND 0x9002
#define HW_POWERUP_IND 0x9102
#define HW_DEACT_IND 0x9202
#define HW_ACTIVATE_IND 0x9302
#define HW_DEACT_CNF 0x9402
#define HW_TESTLOOP 0x9502
#define HW_TESTRX_RAW 0x9602
#define HW_TESTRX_HDLC 0x9702
#define HW_TESTRX_OFF 0x9802
#define HW_TIMER3_IND 0x9902
#define HW_TIMER3_VALUE 0x9a00
#define HW_TIMER3_VMASK 0x00FF
struct layer1;
extern int l1_event(struct layer1 *, u_int);
#define MISDN_BCH_FILL_SIZE 4
struct bchannel {
struct mISDNchannel ch;
int nr;
u_long Flags;
// struct work_struct workq;
u_int state;
void *hw;
int slot; /* multiport card channel slot */
// struct osmo_timer_list_list timer;
/* receive data */
uint8_t fill[MISDN_BCH_FILL_SIZE];
struct sk_buff *rx_skb;
unsigned short maxlen;
unsigned short init_maxlen; /* initial value */
unsigned short next_maxlen; /* pending value */
unsigned short minlen; /* for transparent data */
unsigned short init_minlen; /* initial value */
unsigned short next_minlen; /* pending value */
/* send data */
struct sk_buff *next_skb;
struct sk_buff *tx_skb;
struct sk_buff_head rqueue;
int rcount;
int tx_idx;
int debug;
/* statistics */
int err_crc;
int err_tx;
int err_rx;
int dropcnt;
};
extern int mISDN_initdchannel(struct dchannel *, int, void *);
extern int mISDN_initbchannel(struct bchannel *, unsigned short,
unsigned short);
extern int mISDN_freedchannel(struct dchannel *);
extern void mISDN_clear_bchannel(struct bchannel *);
extern void mISDN_freebchannel(struct bchannel *);
extern int mISDN_ctrl_bchannel(struct bchannel *, struct mISDN_ctrl_req *);
extern void queue_ch_frame(struct mISDNchannel *, u_int,
int, struct sk_buff *);
extern int dchannel_senddata(struct dchannel *, struct sk_buff *);
extern int bchannel_senddata(struct bchannel *, struct sk_buff *);
extern int bchannel_get_rxbuf(struct bchannel *, int);
extern void recv_Dchannel(struct dchannel *);
extern void recv_Echannel(struct dchannel *, struct dchannel *);
extern void recv_Bchannel(struct bchannel *, unsigned int, bool);
extern void recv_Dchannel_skb(struct dchannel *, struct sk_buff *);
extern void recv_Bchannel_skb(struct bchannel *, struct sk_buff *);
extern int get_next_bframe(struct bchannel *);
extern int get_next_dframe(struct dchannel *);
#endif

657
src/libmisdn/mISDNif.h Normal file
View File

@ -0,0 +1,657 @@
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
#ifndef mISDNIF_H
#define mISDNIF_H
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <bits/sockaddr.h>
#include <sys/types.h>
#include <sys/ioctl.h>
/*
* ABI Version 32 bit
*
* <8 bit> Major version
* - changed if any interface become backwards incompatible
*
* <8 bit> Minor version
* - changed if any interface is extended but backwards compatible
*
* <16 bit> Release number
* - should be incremented on every checkin
*/
#define MISDN_MAJOR_VERSION 1
#define MISDN_MINOR_VERSION 1
#define MISDN_RELEASE 29
/* primitives for information exchange
* generell format
* <16 bit 0 >
* <8 bit command>
* BIT 8 = 1 LAYER private
* BIT 7 = 1 answer
* BIT 6 = 1 DATA
* <8 bit target layer mask>
*
* Layer = 00 is reserved for general commands
Layer = 01 L2 -> HW
Layer = 02 HW -> L2
Layer = 04 L3 -> L2
Layer = 08 L2 -> L3
* Layer = FF is reserved for broadcast commands
*/
#define MISDN_CMDMASK 0xff00
#define MISDN_LAYERMASK 0x00ff
/* generell commands */
#define OPEN_CHANNEL 0x0100
#define CLOSE_CHANNEL 0x0200
#define CONTROL_CHANNEL 0x0300
#define CHECK_DATA 0x0400
/* layer 2 -> layer 1 */
#define PH_ACTIVATE_REQ 0x0101
#define PH_DEACTIVATE_REQ 0x0201
#define PH_DATA_REQ 0x2001
#define MPH_ACTIVATE_REQ 0x0501
#define MPH_DEACTIVATE_REQ 0x0601
#define MPH_INFORMATION_REQ 0x0701
#define PH_CONTROL_REQ 0x0801
/* layer 1 -> layer 2 */
#define PH_ACTIVATE_IND 0x0102
#define PH_ACTIVATE_CNF 0x4102
#define PH_DEACTIVATE_IND 0x0202
#define PH_DEACTIVATE_CNF 0x4202
#define PH_DATA_IND 0x2002
#define PH_DATA_E_IND 0x3002
#define MPH_ACTIVATE_IND 0x0502
#define MPH_DEACTIVATE_IND 0x0602
#define MPH_INFORMATION_IND 0x0702
#define PH_DATA_CNF 0x6002
#define PH_CONTROL_IND 0x0802
#define PH_CONTROL_CNF 0x4802
/* layer 3 -> layer 2 */
#define DL_ESTABLISH_REQ 0x1004
#define DL_RELEASE_REQ 0x1104
#define DL_DATA_REQ 0x3004
#define DL_UNITDATA_REQ 0x3104
#define DL_INFORMATION_REQ 0x0004
/* layer 2 -> layer 3 */
#define DL_ESTABLISH_IND 0x1008
#define DL_ESTABLISH_CNF 0x5008
#define DL_RELEASE_IND 0x1108
#define DL_RELEASE_CNF 0x5108
#define DL_DATA_IND 0x3008
#define DL_UNITDATA_IND 0x3108
#define DL_INFORMATION_IND 0x0008
/* intern layer 2 management */
#define MDL_ASSIGN_REQ 0x1804
#define MDL_ASSIGN_IND 0x1904
#define MDL_REMOVE_REQ 0x1A04
#define MDL_REMOVE_IND 0x1B04
#define MDL_STATUS_UP_IND 0x1C04
#define MDL_STATUS_DOWN_IND 0x1D04
#define MDL_STATUS_UI_IND 0x1E04
#define MDL_ERROR_IND 0x1F04
#define MDL_ERROR_RSP 0x5F04
/* intern layer 2 */
#define DL_TIMER200_IND 0x7004
#define DL_TIMER203_IND 0x7304
#define DL_INTERN_MSG 0x7804
/* DL_INFORMATION_IND types */
#define DL_INFO_L2_CONNECT 0x0001
#define DL_INFO_L2_REMOVED 0x0002
/* PH_CONTROL types */
/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */
#define DTMF_TONE_VAL 0x2000
#define DTMF_TONE_MASK 0x007F
#define DTMF_TONE_START 0x2100
#define DTMF_TONE_STOP 0x2200
#define DTMF_HFC_COEF 0x4000
#define DSP_CONF_JOIN 0x2403
#define DSP_CONF_SPLIT 0x2404
#define DSP_RECEIVE_OFF 0x2405
#define DSP_RECEIVE_ON 0x2406
#define DSP_ECHO_ON 0x2407
#define DSP_ECHO_OFF 0x2408
#define DSP_MIX_ON 0x2409
#define DSP_MIX_OFF 0x240a
#define DSP_DELAY 0x240b
#define DSP_JITTER 0x240c
#define DSP_TXDATA_ON 0x240d
#define DSP_TXDATA_OFF 0x240e
#define DSP_TX_DEJITTER 0x240f
#define DSP_TX_DEJ_OFF 0x2410
#define DSP_TONE_PATT_ON 0x2411
#define DSP_TONE_PATT_OFF 0x2412
#define DSP_VOL_CHANGE_TX 0x2413
#define DSP_VOL_CHANGE_RX 0x2414
#define DSP_BF_ENABLE_KEY 0x2415
#define DSP_BF_DISABLE 0x2416
#define DSP_BF_ACCEPT 0x2416
#define DSP_BF_REJECT 0x2417
#define DSP_PIPELINE_CFG 0x2418
#define HFC_VOL_CHANGE_TX 0x2601
#define HFC_VOL_CHANGE_RX 0x2602
#define HFC_SPL_LOOP_ON 0x2603
#define HFC_SPL_LOOP_OFF 0x2604
/* for T30 FAX and analog modem */
#define HW_MOD_FRM 0x4000
#define HW_MOD_FRH 0x4001
#define HW_MOD_FTM 0x4002
#define HW_MOD_FTH 0x4003
#define HW_MOD_FTS 0x4004
#define HW_MOD_CONNECT 0x4010
#define HW_MOD_OK 0x4011
#define HW_MOD_NOCARR 0x4012
#define HW_MOD_FCERROR 0x4013
#define HW_MOD_READY 0x4014
#define HW_MOD_LASTDATA 0x4015
/* DSP_TONE_PATT_ON parameter */
#define TONE_OFF 0x0000
#define TONE_GERMAN_DIALTONE 0x0001
#define TONE_GERMAN_OLDDIALTONE 0x0002
#define TONE_GERMAN_MORSEDIALTONE 0x0003
#define TONE_AMERICAN_DIALTONE 0x0004
#define TONE_GERMAN_DIALPBX 0x0005
#define TONE_GERMAN_OLDDIALPBX 0x0006
#define TONE_AMERICAN_DIALPBX 0x0007
#define TONE_GERMAN_RINGING 0x0008
#define TONE_GERMAN_OLDRINGING 0x0009
#define TONE_AMERICAN_RINGPBX 0x000b
#define TONE_GERMAN_RINGPBX 0x000c
#define TONE_GERMAN_OLDRINGPBX 0x000d
#define TONE_AMERICAN_RINGING 0x000e
#define TONE_GERMAN_BUSY 0x000f
#define TONE_GERMAN_OLDBUSY 0x0010
#define TONE_AMERICAN_BUSY 0x0011
#define TONE_GERMAN_HANGUP 0x0012
#define TONE_GERMAN_OLDHANGUP 0x0013
#define TONE_AMERICAN_HANGUP 0x0014
#define TONE_SPECIAL_INFO 0x0015
#define TONE_GERMAN_GASSENBESETZT 0x0016
#define TONE_GERMAN_AUFSCHALTTON 0x0017
/* MPH_INFORMATION_IND */
#define L1_SIGNAL_LOS_OFF 0x0010
#define L1_SIGNAL_LOS_ON 0x0011
#define L1_SIGNAL_AIS_OFF 0x0012
#define L1_SIGNAL_AIS_ON 0x0013
#define L1_SIGNAL_RDI_OFF 0x0014
#define L1_SIGNAL_RDI_ON 0x0015
#define L1_SIGNAL_SLIP_RX 0x0020
#define L1_SIGNAL_SLIP_TX 0x0021
/*
* protocol ids
* D channel 1-31
* B channel 33 - 63
*/
#define ISDN_P_NONE 0
#define ISDN_P_BASE 0
#define ISDN_P_TE_S0 0x01
#define ISDN_P_NT_S0 0x02
#define ISDN_P_TE_E1 0x03
#define ISDN_P_NT_E1 0x04
#define ISDN_P_TE_UP0 0x05
#define ISDN_P_NT_UP0 0x06
#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \
(p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE))
#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \
(p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT))
#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0))
#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1))
#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0))
#define ISDN_P_LAPD_TE 0x10
#define ISDN_P_LAPD_NT 0x11
#define ISDN_P_B_MASK 0x1f
#define ISDN_P_B_START 0x20
#define ISDN_P_B_RAW 0x21
#define ISDN_P_B_HDLC 0x22
#define ISDN_P_B_X75SLP 0x23
#define ISDN_P_B_L2DTMF 0x24
#define ISDN_P_B_L2DSP 0x25
#define ISDN_P_B_L2DSPHDLC 0x26
#define ISDN_P_B_T30_FAX 0x27
#define ISDN_P_B_MODEM_ASYNC 0x28
#define OPTION_L2_PMX 1
#define OPTION_L2_PTP 2
#define OPTION_L2_FIXEDTEI 3
#define OPTION_L2_CLEANUP 4
#define OPTION_L1_HOLD 5
/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
#define MISDN_MAX_IDLEN 20
struct mISDNhead {
unsigned int prim;
unsigned int id;
} __attribute__((packed));
#define MISDN_HEADER_LEN sizeof(struct mISDNhead)
#define MAX_DATA_SIZE 2048
#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN)
#define MAX_DFRAME_LEN 260
#define MISDN_ID_ADDR_MASK 0xFFFF
#define MISDN_ID_TEI_MASK 0xFF00
#define MISDN_ID_SAPI_MASK 0x00FF
#define MISDN_ID_TEI_ANY 0x7F00
#define MISDN_ID_ANY 0xFFFF
#define MISDN_ID_NONE 0xFFFE
#define GROUP_TEI 127
#define TEI_SAPI 63
#define CTRL_SAPI 0
#define MISDN_MAX_CHANNEL 127
#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3)
#define SOL_MISDN 0
struct sockaddr_mISDN {
sa_family_t family;
unsigned char dev;
unsigned char channel;
unsigned char sapi;
unsigned char tei;
};
struct mISDNversion {
unsigned char major;
unsigned char minor;
unsigned short release;
};
struct mISDN_devinfo {
u_int id;
u_int Dprotocols;
u_int Bprotocols;
u_int protocol;
u_char channelmap[MISDN_CHMAP_SIZE];
u_int nrbchan;
char name[MISDN_MAX_IDLEN];
};
struct mISDN_devrename {
u_int id;
char name[MISDN_MAX_IDLEN]; /* new name */
};
/* MPH_INFORMATION_REQ payload */
struct ph_info_ch {
__u32 protocol;
__u64 Flags;
};
struct ph_info_dch {
struct ph_info_ch ch;
__u16 state;
__u16 num_bch;
};
struct ph_info {
struct ph_info_dch dch;
struct ph_info_ch bch[];
};
/* timer device ioctl */
#define IMADDTIMER _IOR('I', 64, int)
#define IMDELTIMER _IOR('I', 65, int)
/* socket ioctls */
#define IMGETVERSION _IOR('I', 66, int)
#define IMGETCOUNT _IOR('I', 67, int)
#define IMGETDEVINFO _IOR('I', 68, int)
#define IMCTRLREQ _IOR('I', 69, int)
#define IMCLEAR_L2 _IOR('I', 70, int)
#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename)
#define IMHOLD_L1 _IOR('I', 72, int)
static inline int
test_channelmap(u_int nr, u_char *map)
{
if (nr <= MISDN_MAX_CHANNEL)
return map[nr >> 3] & (1 << (nr & 7));
else
return 0;
}
static inline void
set_channelmap(u_int nr, u_char *map)
{
map[nr >> 3] |= (1 << (nr & 7));
}
static inline void
clear_channelmap(u_int nr, u_char *map)
{
map[nr >> 3] &= ~(1 << (nr & 7));
}
/* CONTROL_CHANNEL parameters */
#define MISDN_CTRL_GETOP 0x0000
#define MISDN_CTRL_LOOP 0x0001
#define MISDN_CTRL_CONNECT 0x0002
#define MISDN_CTRL_DISCONNECT 0x0004
#define MISDN_CTRL_RX_BUFFER 0x0008
#define MISDN_CTRL_PCMCONNECT 0x0010
#define MISDN_CTRL_PCMDISCONNECT 0x0020
#define MISDN_CTRL_SETPEER 0x0040
#define MISDN_CTRL_UNSETPEER 0x0080
#define MISDN_CTRL_RX_OFF 0x0100
#define MISDN_CTRL_FILL_EMPTY 0x0200
#define MISDN_CTRL_GETPEER 0x0400
#define MISDN_CTRL_L1_TIMER3 0x0800
#define MISDN_CTRL_HW_FEATURES_OP 0x2000
#define MISDN_CTRL_HW_FEATURES 0x2001
#define MISDN_CTRL_HFC_OP 0x4000
#define MISDN_CTRL_HFC_PCM_CONN 0x4001
#define MISDN_CTRL_HFC_PCM_DISC 0x4002
#define MISDN_CTRL_HFC_CONF_JOIN 0x4003
#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004
#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005
#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006
#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007
#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008
#define MISDN_CTRL_HFC_WD_INIT 0x4009
#define MISDN_CTRL_HFC_WD_RESET 0x400A
/* special RX buffer value for MISDN_CTRL_RX_BUFFER request.p1 is the minimum
* buffer size request.p2 the maximum. Using MISDN_CTRL_RX_SIZE_IGNORE will
* not change the value, but still read back the actual stetting.
*/
#define MISDN_CTRL_RX_SIZE_IGNORE -1
/* socket options */
#define MISDN_TIME_STAMP 0x0001
struct mISDN_ctrl_req {
int op;
int channel;
int p1;
int p2;
};
/* muxer options */
#define MISDN_OPT_ALL 1
#define MISDN_OPT_TEIMGR 2
#ifdef __MISDNL1L2__
#include "mlist.h"
#include "skbuff.h"
#include "printk.h"
#include "bitops.h"
//#include "container_of.h"
#include <linux/net.h>
//#include <linux/completion.h>
#define kzalloc(a, b) calloc(1, a)
#define kfree(a) free(a)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define rwlock_t int
#define rwlock_init(l)
#define read_lock_irqsave(l, f)
#define read_unlock_irqrestore(l, f)
#define write_lock_irqsave(l, f)
#define write_unlock_irqrestore(l, f)
#define read_lock(l)
#define read_unlock(l)
#define write_lock(l)
#define write_unlock(l)
#define read_lock_bh(l)
#define read_unlock_bh(l)
#define write_lock_bh(l)
#define write_unlock_bh(l)
#define mutex_init(m)
#define mutex_lock(m)
#define mutex_unlock(m)
#define EXPORT_SYMBOL(a) ;
#define likely(x) (x)
#define unlikely(x) (x)
#define dev_name(dev) ((dev)->dev_name)
#define dev_set_name(dev, fmt, arg...) sprintf((dev)->dev_name, fmt, ## arg)
#define module_put(x)
#define __module_get(x)
#define WARN_ON(what)
#define DEBUG_CORE 0x000000ff
#define DEBUG_CORE_FUNC 0x00000002
#define DEBUG_SOCKET 0x00000004
#define DEBUG_MANAGER 0x00000008
#define DEBUG_SEND_ERR 0x00000010
#define DEBUG_MSG_THREAD 0x00000020
#define DEBUG_QUEUE_FUNC 0x00000040
#define DEBUG_L1 0x0000ff00
#define DEBUG_L1_FSM 0x00000200
#define DEBUG_L2 0x00ff0000
#define DEBUG_L2_FSM 0x00020000
#define DEBUG_L2_CTRL 0x00040000
#define DEBUG_L2_RECV 0x00080000
#define DEBUG_L2_TEI 0x00100000
#define DEBUG_L2_TEIFSM 0x00200000
#define DEBUG_TIMER 0x01000000
#define DEBUG_CLOCK 0x02000000
#define mISDN_HEAD_P(s) ((struct mISDNhead *)&s->cb[0])
#define mISDN_HEAD_PRIM(s) (((struct mISDNhead *)&s->cb[0])->prim)
#define mISDN_HEAD_ID(s) (((struct mISDNhead *)&s->cb[0])->id)
/* socket states */
#define MISDN_OPEN 1
#define MISDN_BOUND 2
#define MISDN_CLOSED 3
struct mISDNchannel;
struct mISDNdevice;
struct mISDNstack;
struct mISDNclock;
struct channel_req {
u_int protocol;
struct sockaddr_mISDN adr;
struct mISDNchannel *ch;
};
typedef int (ctrl_func_t)(struct mISDNchannel *, u_int, void *);
typedef int (send_func_t)(struct mISDNchannel *, struct sk_buff *);
typedef int (create_func_t)(struct channel_req *);
struct Bprotocol {
struct list_head list;
char *name;
u_int Bprotocols;
create_func_t *create;
};
struct mISDNchannel {
struct list_head list;
u_int protocol;
u_int nr;
u_long opt;
u_int addr;
struct mISDNstack *st;
struct mISDNchannel *peer;
send_func_t *send;
send_func_t *recv;
ctrl_func_t *ctrl;
};
struct mISDN_sock_list {
struct m_hlist_head head;
rwlock_t lock;
};
struct mISDN_sock {
struct m_hlist_node list;
int sk_protocol;
int sk_state;
struct sk_buff_head sk_receive_queue;
struct mISDNchannel ch;
u_int cmask;
struct mISDNdevice *dev;
};
struct mISDN_dev_list {
struct m_hlist_head head;
rwlock_t lock;
};
struct mISDNdevice {
struct m_hlist_node list;
struct mISDNchannel D;
u_int id;
u_int Dprotocols;
u_int Bprotocols;
u_int nrbchan;
u_char channelmap[MISDN_CHMAP_SIZE];
struct list_head bchannels;
struct mISDNchannel *teimgr;
char dev_name[64];
};
struct mISDNstack {
u_long status;
struct mISDNdevice *dev;
struct task_struct *thread;
struct completion *notify;
// wait_queue_head_t workq;
struct sk_buff_head msgq;
struct list_head layer2;
struct mISDNchannel *layer1;
struct mISDNchannel own;
int lmutex; /* protect lists */
struct mISDN_sock_list l1sock;
#ifdef MISDN_MSG_STATS
u_int msg_cnt;
u_int sleep_cnt;
u_int stopped_cnt;
#endif
};
typedef int (clockctl_func_t)(void *, int);
struct mISDNclock {
struct list_head list;
char name[64];
int pri;
clockctl_func_t *ctl;
void *priv;
};
/* global alloc/queue functions */
static inline struct sk_buff *
mI_alloc_skb(unsigned int len, gfp_t gfp_mask)
{
struct sk_buff *skb;
skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask);
if (skb)
skb_reserve(skb, MISDN_HEADER_LEN);
return skb;
}
static inline struct sk_buff *
_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask)
{
struct sk_buff *skb = mI_alloc_skb(len, gfp_mask);
struct mISDNhead *hh;
if (!skb)
return NULL;
if (len)
skb_put_data(skb, dp, len);
hh = mISDN_HEAD_P(skb);
hh->prim = prim;
hh->id = id;
return skb;
}
static inline void
_queue_data(struct mISDNchannel *ch, u_int prim,
u_int id, u_int len, void *dp, gfp_t gfp_mask)
{
struct sk_buff *skb;
if (!ch->peer)
return;
skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask);
if (!skb)
return;
if (ch->recv(ch->peer, skb))
dev_kfree_skb(skb);
}
/* global register/unregister functions */
extern int mISDN_register_device(struct mISDNdevice *,
void *parent, const char *name);
extern void mISDN_unregister_device(struct mISDNdevice *);
extern int mISDN_register_Bprotocol(struct Bprotocol *);
extern void mISDN_unregister_Bprotocol(struct Bprotocol *);
extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *,
void *);
extern void mISDN_unregister_clock(struct mISDNclock *);
#if 0
static inline struct mISDNdevice *dev_to_mISDN(struct device *dev)
{
if (dev)
return dev_get_drvdata(dev);
else
return NULL;
}
#endif
extern void set_channel_address(struct mISDNchannel *, u_int, u_int);
#if 0
extern void mISDN_clock_update(struct mISDNclock *, int, ktime_t *);
extern unsigned short mISDN_clock_get(void);
#endif
extern const char *mISDNDevName4ch(struct mISDNchannel *);
#endif /* __MISDNL1L2__ */
#endif /* mISDNIF_H */

198
src/libmisdn/mlist.h Normal file
View File

@ -0,0 +1,198 @@
/* mlist.h
*
* Simple doubly linked list implementation and helper
* function reduce version of linux kernel list.h
*/
#ifndef _MLIST_H
#define _MLIST_H
struct list_head {
struct list_head *next, *prev;
};
struct m_hlist_head {
struct m_hlist_node *first;
};
struct m_hlist_node {
struct m_hlist_node *next, **pprev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
#define INIT_M_HLIST_HEAD(ptr) ((ptr)->first = NULL)
/*
* list_add_head - add a new entry on the top
* @new: new entry to be added
* @head: list head to add it after
*
*/
static inline void list_add_head(struct list_head *new, struct list_head *head)
{
head->next->prev = new;
new->next = head->next;
new->prev = head;
head->next = new;
}
/**
* m_hlist_add_head - add a new entry at the beginning of the m_hlist
* @n: new entry to be added
* @h: m_hlist head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void m_hlist_add_head(struct m_hlist_node *n, struct m_hlist_head *h)
{
struct m_hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
/*
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
head->prev->next = new;
new->next = head;
new->prev = head->prev;
head->prev = new;
}
#define LIST_POISON1 0xdeadbee1
#define LIST_POISON2 0xdeadbee2
/*
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
*/
static inline void list_del(struct list_head *entry)
{
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
entry->next = (void *)LIST_POISON1;
entry->prev = (void *)LIST_POISON2;
}
static inline void m_hlist_del(struct m_hlist_node *n)
{
struct m_hlist_node *next = n->next;
struct m_hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
n->next = (void *)LIST_POISON1;
n->pprev = (void *)LIST_POISON2;
}
/*
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list, const struct list_head *head)
{
return list->next == head;
}
/*
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* m_hlist_empty - Is the specified m_hlist_head structure an empty m_hlist?
* @h: Structure to check.
*/
static inline int m_hlist_empty(const struct m_hlist_head *h)
{
return !h->first;
}
/*
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/*
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)
/*
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
#endif
#define m_hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define m_hlist_entry_safe(ptr, type, member) \
({ typeof(ptr) ____ptr = (ptr); \
____ptr ? m_hlist_entry(____ptr, type, member) : NULL; \
})
/**
* m_hlist_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the m_hlist_node within the struct.
*/
#define m_hlist_for_each_entry(pos, head, member) \
for (pos = m_hlist_entry_safe((head)->first, typeof(*(pos)), member);\
pos; \
pos = m_hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))

39
src/libmisdn/printk.c Normal file
View File

@ -0,0 +1,39 @@
#include <stdio.h>
#include <stdarg.h>
#include "printk.h"
void _printk(const char *file, __attribute__((unused)) const char *function, int line, const char *fmt, ...)
{
int level = LOGL_INFO;
va_list args;
/* remove debug level from format */
if (fmt[0] >= '0' && fmt[0] <= '9') {
switch (fmt[0]) {
case '0':
case '1':
case '2':
case '3':
level = LOGL_ERROR;
break;
case '4':
case '5':
level = LOGL_NOTICE;
break;
case '6':
level = LOGL_INFO;
break;
case '7':
case '8':
case '9':
level = LOGL_DEBUG;
break;
}
fmt++;
}
va_start(args, fmt);
LOGPSRC(DMISDN, level, file, line, fmt, args);
va_end(args);
}

20
src/libmisdn/printk.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef MISDN_PRINTK_H
#define MISDN_PRINTK_H
#include <stdint.h>
#include "../liblogging/logging.h"
#define KERN_EMERG "0" /* system is unusable */
#define KERN_ALERT "1" /* action must be taken immediately */
#define KERN_CRIT "2" /* critical conditions */
#define KERN_ERR "3" /* error conditions */
#define KERN_WARNING "4" /* warning conditions */
#define KERN_NOTICE "5" /* normal but significant condition */
#define KERN_INFO "6" /* informational */
#define KERN_DEBUG "7" /* debug-level messages */
#define printk(fmt, arg...) _printk(__FILE__, __FUNCTION__, __LINE__, fmt, ## arg)
void _printk(const char *file, const char *function, int line, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 4, 5)));
#endif

241
src/libmisdn/skbuff.h Normal file
View File

@ -0,0 +1,241 @@
#ifndef _MISDN_MSKBUFF_H
#define _MISDN_MSKBUFF_H
#include <stdlib.h>
#include <string.h>
#define GFP_ATOMIC 0
#define GFP_KERNEL 1
typedef unsigned int __bitwise gfp_t;
/*
* head data tail end
* | | len | |
* -----> --------> ----->
* headroom tailroom
*/
struct sk_buff {
struct sk_buff *next;
struct sk_buff *prev;
char cb[48];
gfp_t priority;
unsigned int len;
unsigned char *head,
*data,
*tail,
*end;
/* the buffer begins behind sk_buff structure size */
unsigned char buffer[0];
};
#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \
union { \
struct { MEMBERS } ATTRS; \
struct TAG { MEMBERS } ATTRS NAME; \
}
#define struct_group_tagged(TAG, NAME, MEMBERS...) \
__struct_group(TAG, NAME, /* no attrs */, MEMBERS)
struct sk_buff_head {
/* These two members must be first to match sk_buff. */
struct_group_tagged(sk_buff_list, list,
struct sk_buff *next;
struct sk_buff *prev;
);
__u32 qlen;
// spinlock_t lock;
};
static inline struct sk_buff *alloc_skb(unsigned int size,
gfp_t priority)
{
struct sk_buff *skb = calloc(1, sizeof(*skb) + size);
skb->head = skb->buffer;
skb->data = skb->buffer;
skb->tail = skb->buffer;
skb->end = skb->buffer + size;
skb->len = 0;
skb->priority = priority;
return skb;
}
static inline struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int headroom)
{
/* headroom is incremented, but never shrinks! this way copying is easy */
if (skb->data - skb->head > headroom)
headroom = skb->data - skb->head;
size_t osize = skb->end - skb->head;
size_t nsize = skb->end - skb->data + headroom;
struct sk_buff *n = alloc_skb(nsize, skb->priority);
if (!n)
return NULL;
n->data = n->head + headroom;
n->tail = n->data + (skb->tail - skb->data);
n->len = n->tail - n->data;
memcpy(n->end - osize, skb->head, osize);
memcpy(n->cb, skb->cb, sizeof(n->cb));
return n;
}
static inline struct sk_buff *skb_clone(struct sk_buff *skb)
{
return skb_realloc_headroom(skb, skb->data - skb->head);
}
static inline struct sk_buff *skb_copy(struct sk_buff *skb, gfp_t priority)
{
struct sk_buff *n = skb_realloc_headroom(skb, skb->data - skb->head);
if (!n)
return NULL;
n->priority = priority;
return n;
}
static inline void kfree_skb(struct sk_buff *skb)
{
free(skb);
}
static inline void __net_timestamp(struct sk_buff __attribute__((unused)) *skb)
{
// skb->tstamp = ktime_get_real();
}
#define dev_kfree_skb(a) kfree_skb(a)
static inline void skb_reserve(struct sk_buff *skb, int len)
{
skb->data += len;
skb->tail += len;
}
static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)
{
return skb->tail;
}
static inline void *skb_put(struct sk_buff *skb, unsigned int len)
{
void *tmp = skb_tail_pointer(skb);
skb->tail += len;
skb->len += len;
if (skb->tail > skb->end)
abort();
return tmp;
}
static inline void *skb_put_data(struct sk_buff *skb, const void *data,
unsigned int len)
{
void *tmp = skb_put(skb, len);
memcpy(tmp, data, len);
return tmp;
}
static inline void skb_trim(struct sk_buff *skb, unsigned int len)
{
skb->tail = skb->data + len;
skb->len = len;
}
static inline void *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data -= len;
skb->len += len;
return skb->data;
}
static inline void *skb_pull(struct sk_buff *skb, unsigned int len)
{
void *tmp = skb->data;
if (skb->len < len)
abort();
skb->len -= len;
skb->data += len;
return tmp;
}
static inline void skb_queue_head_init(struct sk_buff_head *list)
{
list->prev = list->next = (struct sk_buff *)list;
list->qlen = 0;
}
static inline void skb_queue_tail(struct sk_buff_head *list,
struct sk_buff *newsk)
{
newsk->prev = list->prev;
newsk->next = (struct sk_buff *)list;
list->prev->next = newsk;
list->prev = newsk;
list->qlen++;
}
static inline void skb_queue_head(struct sk_buff_head *list,
struct sk_buff *newsk)
{
newsk->prev = (struct sk_buff *)list;
newsk->next = list->next;
list->next->prev = newsk;
list->next = newsk;
list->qlen++;
}
static inline struct sk_buff *skb_dequeue(struct sk_buff_head *list)
{
struct sk_buff *skb;
if (list->next == (struct sk_buff *)list)
return NULL;
skb = list->next;
list->next = list->next->next;
list->next->prev = (struct sk_buff *)list;
list->qlen--;
skb->prev = skb->next = NULL;
return skb;
}
static inline void skb_queue_purge(struct sk_buff_head *list)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(list)) != NULL)
kfree_skb(skb);
}
static inline int skb_queue_empty(const struct sk_buff_head *list)
{
return list->next == (const struct sk_buff *) list;
}
static inline __u32 skb_queue_len(const struct sk_buff_head *list)
{
return list->qlen;
}
static inline int skb_tailroom(const struct sk_buff *skb)
{
return skb->end - skb->tail;
}
#endif

646
src/libmisdn/socket.c Normal file
View File

@ -0,0 +1,646 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "mISDNif.h"
#include "core.h"
#include <stdio.h>
#include <stdarg.h>
#include <sys/socket.h>
#include "socket.h"
static u_int *debug;
static struct mISDN_sock_list data_sockets = {
.lock = 0 //__RW_LOCK_UNLOCKED(data_sockets.lock)
};
static struct mISDN_sock_list base_sockets = {
.lock = 0 //__RW_LOCK_UNLOCKED(base_sockets.lock)
};
#define L2_HEADER_LEN 4
static inline struct sk_buff *
_l2_alloc_skb(unsigned int len, gfp_t gfp_mask)
{
struct sk_buff *skb;
skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask);
if (likely(skb))
skb_reserve(skb, L2_HEADER_LEN);
return skb;
}
static void
mISDN_sock_link(struct mISDN_sock_list *l, struct mISDN_sock *msk)
{
write_lock_bh(&l->lock);
m_hlist_add_head(&msk->list, &l->head);
write_unlock_bh(&l->lock);
}
static void mISDN_sock_unlink(struct mISDN_sock_list __attribute__((unused)) *l, struct mISDN_sock *msk)
{
write_lock_bh(&l->lock);
m_hlist_del(&msk->list);
write_unlock_bh(&l->lock);
}
static int
mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
{
struct mISDN_sock *msk;
msk = container_of(ch, struct mISDN_sock, ch);
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb);
if (msk->sk_state == MISDN_CLOSED)
return -EUNATCH;
__net_timestamp(skb);
skb_queue_tail(&msk->sk_receive_queue, skb);
return 0;
}
static int
mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
{
struct mISDN_sock *msk;
msk = container_of(ch, struct mISDN_sock, ch);
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg);
switch (cmd) {
case CLOSE_CHANNEL:
msk->sk_state = MISDN_CLOSED;
break;
}
return 0;
}
int mISDN_data_recvmsg(void *inst, unsigned char *data, size_t len, int flags, struct sockaddr_mISDN *maddr)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
struct sk_buff *skb;
size_t copied;
if (flags & (MSG_OOB))
return -EOPNOTSUPP;
if (msk->sk_state == MISDN_CLOSED)
return 0;
skb = skb_dequeue(&msk->sk_receive_queue);
if (!skb)
return -EAGAIN;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
__func__, (int)len, flags, msk->ch.nr,
msk->sk_protocol);
if (maddr) {
maddr->family = AF_ISDN;
maddr->dev = msk->dev->id;
if ((msk->sk_protocol == ISDN_P_LAPD_TE) ||
(msk->sk_protocol == ISDN_P_LAPD_NT)) {
maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff;
maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
} else {
maddr->channel = msk->ch.nr;
maddr->sapi = msk->ch.addr & 0xFF;
maddr->tei = (msk->ch.addr >> 8) & 0xFF;
}
}
copied = skb->len + MISDN_HEADER_LEN;
if (len < copied) {
if (flags & MSG_PEEK)
/*refcount_dec(&skb->users)*/;
else
skb_queue_head(&msk->sk_receive_queue, skb);
return -ENOSPC;
}
memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
MISDN_HEADER_LEN);
memcpy(data, skb->data, skb->len);
dev_kfree_skb(skb);
return copied;
}
int mISDN_data_sendmsg(void *inst, unsigned char *data, size_t len, int __attribute__((unused)) flags, struct sockaddr_mISDN *maddr)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
struct sk_buff *skb;
int err = -ENOMEM;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: len %d ch %d proto %x\n",
__func__, (int)len, msk->ch.nr,
msk->sk_protocol);
if (len < MISDN_HEADER_LEN)
return -EINVAL;
if (msk->sk_state != MISDN_BOUND)
return -EBADFD;
skb = _l2_alloc_skb(len, GFP_KERNEL);
if (!skb)
goto done;
memcpy(skb_put(skb, len), data, len);
memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
skb_pull(skb, MISDN_HEADER_LEN);
if (maddr) {
/* if we have a address, we use it */
mISDN_HEAD_ID(skb) = maddr->channel;
} else { /* use default for L2 messages */
if ((msk->sk_protocol == ISDN_P_LAPD_TE) ||
(msk->sk_protocol == ISDN_P_LAPD_NT))
mISDN_HEAD_ID(skb) = msk->ch.nr;
}
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: ID:%x\n",
__func__, mISDN_HEAD_ID(skb));
err = -ENODEV;
if (!msk->ch.peer)
goto done;
err = msk->ch.recv(msk->ch.peer, skb);
if (err)
goto done;
else {
skb = NULL;
err = len;
}
done:
kfree_skb(skb);
return err;
}
int mISDN_data_release(void *inst)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
if (!msk)
return 0;
switch (msk->sk_protocol) {
case ISDN_P_TE_S0:
case ISDN_P_NT_S0:
case ISDN_P_TE_E1:
case ISDN_P_NT_E1:
if (msk->sk_state == MISDN_BOUND)
delete_channel(&msk->ch);
else
mISDN_sock_unlink(&data_sockets, msk);
break;
case ISDN_P_LAPD_TE:
case ISDN_P_LAPD_NT:
case ISDN_P_B_RAW:
case ISDN_P_B_HDLC:
case ISDN_P_B_X75SLP:
case ISDN_P_B_L2DTMF:
case ISDN_P_B_L2DSP:
case ISDN_P_B_L2DSPHDLC:
delete_channel(&msk->ch);
mISDN_sock_unlink(&data_sockets, msk);
break;
}
skb_queue_purge(&msk->sk_receive_queue);
free(msk);
return 0;
}
static int
data_sock_ioctl_bound(struct mISDN_sock *msk, unsigned int cmd, void *p)
{
struct mISDN_ctrl_req cq;
int err = -EINVAL, val[2];
struct mISDNchannel *bchan, *next;
if (!msk->dev) {
err = -ENODEV;
goto done;
}
switch (cmd) {
case IMCTRLREQ:
if (memcpy(&cq, p, sizeof(cq))) {
err = -EFAULT;
break;
}
if ((msk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
list_for_each_entry_safe(bchan, next,
&msk->dev->bchannels, list) {
if ((int)bchan->nr == cq.channel) {
err = bchan->ctrl(bchan,
CONTROL_CHANNEL, &cq);
break;
}
}
} else
err = msk->dev->D.ctrl(&msk->dev->D,
CONTROL_CHANNEL, &cq);
if (err)
break;
if (memcpy(p, &cq, sizeof(cq)))
err = -EFAULT;
break;
case IMCLEAR_L2:
if (msk->sk_protocol != ISDN_P_LAPD_NT) {
err = -EINVAL;
break;
}
val[0] = cmd;
val[1] = *((int *)p);
err = msk->dev->teimgr->ctrl(msk->dev->teimgr,
CONTROL_CHANNEL, val);
break;
case IMHOLD_L1:
if (msk->sk_protocol != ISDN_P_LAPD_NT
&& msk->sk_protocol != ISDN_P_LAPD_TE) {
err = -EINVAL;
break;
}
val[0] = cmd;
val[1] = *((int *)p);
err = msk->dev->teimgr->ctrl(msk->dev->teimgr,
CONTROL_CHANNEL, val);
break;
default:
err = -EINVAL;
break;
}
done:
return err;
}
int mISDN_data_ioctl(void *inst, unsigned int cmd, void *arg)
{
int err = 0, id;
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
struct mISDNdevice *dev;
struct mISDNversion ver;
switch (cmd) {
case IMGETVERSION:
ver.major = MISDN_MAJOR_VERSION;
ver.minor = MISDN_MINOR_VERSION;
ver.release = MISDN_RELEASE;
memcpy((void *)arg, &ver, sizeof(ver));
break;
case IMGETCOUNT:
id = get_mdevice_count();
*((int *)arg) = id;
break;
case IMGETDEVINFO:
id = *((int *)arg);
dev = get_mdevice(id);
if (dev) {
struct mISDN_devinfo di;
memset(&di, 0, sizeof(di));
di.id = dev->id;
di.Dprotocols = dev->Dprotocols;
di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
di.protocol = dev->D.protocol;
memcpy(di.channelmap, dev->channelmap,
sizeof(di.channelmap));
di.nrbchan = dev->nrbchan;
strncpy(di.name, dev_name(dev), sizeof(di.name));
di.name[sizeof(di.name) - 1] = '\0';
memcpy((void *)arg, &di, sizeof(di));
} else
err = -ENODEV;
break;
default:
if (msk->sk_state == MISDN_BOUND)
err = data_sock_ioctl_bound(msk, cmd, arg);
else
err = -ENOTCONN;
}
return err;
}
#if 0
int mISDN_data_setopt(void *inst, int level, int optname,
sockptr_t optval, unsigned int len)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
int err = 0, opt = 0;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s(%p, %d, %x, optval, %d)\n", __func__, sock,
level, optname, len);
switch (optname) {
case MISDN_TIME_STAMP:
if (copy_from_sockptr(&opt, optval, sizeof(int))) {
err = -EFAULT;
break;
}
if (opt)
msk->cmask |= MISDN_TIME_STAMP;
else
msk->cmask &= ~MISDN_TIME_STAMP;
break;
default:
err = -ENOPROTOOPT;
break;
}
return err;
}
int mISDN_data_getopt(void *inst, int level, int optname,
char *optval, int *optlen)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
int len, opt;
len = *optlen;
if (len != sizeof(char))
return -EINVAL;
switch (optname) {
case MISDN_TIME_STAMP:
if (msk->cmask & MISDN_TIME_STAMP)
opt = 1;
else
opt = 0;
*((int *)optval) = opt;
return -EFAULT;
break;
default:
return -ENOPROTOOPT;
}
return 0;
}
#endif
int mISDN_data_bind(void *inst, struct sockaddr *addr, int addr_len)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
struct mISDN_sock *cmsk;
int err = 0;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
if (addr_len != sizeof(struct sockaddr_mISDN))
return -EINVAL;
if (!maddr || maddr->family != AF_ISDN)
return -EINVAL;
if (msk->dev) {
err = -EALREADY;
goto done;
}
msk->dev = get_mdevice(maddr->dev);
if (!msk->dev) {
err = -ENODEV;
goto done;
}
if (msk->sk_protocol < ISDN_P_B_START) {
read_lock_bh(&data_sockets.lock);
m_hlist_for_each_entry(cmsk, &data_sockets.head, list) {
if (msk == cmsk)
continue;
if (cmsk->dev != msk->dev)
continue;
if (cmsk->sk_protocol >= ISDN_P_B_START)
continue;
if (IS_ISDN_P_TE(cmsk->sk_protocol)
== IS_ISDN_P_TE(msk->sk_protocol))
continue;
read_unlock_bh(&data_sockets.lock);
err = -EBUSY;
goto done;
}
read_unlock_bh(&data_sockets.lock);
}
msk->ch.send = mISDN_send;
msk->ch.ctrl = mISDN_ctrl;
switch (msk->sk_protocol) {
case ISDN_P_TE_S0:
case ISDN_P_NT_S0:
case ISDN_P_TE_E1:
case ISDN_P_NT_E1:
mISDN_sock_unlink(&data_sockets, msk);
err = connect_layer1(msk->dev, &msk->ch,
msk->sk_protocol, maddr);
if (err)
mISDN_sock_link(&data_sockets, msk);
break;
case ISDN_P_LAPD_TE:
case ISDN_P_LAPD_NT:
err = create_l2entity(msk->dev, &msk->ch,
msk->sk_protocol, maddr);
break;
case ISDN_P_B_RAW:
case ISDN_P_B_HDLC:
case ISDN_P_B_X75SLP:
case ISDN_P_B_L2DTMF:
case ISDN_P_B_L2DSP:
case ISDN_P_B_L2DSPHDLC:
err = connect_Bstack(msk->dev, &msk->ch,
msk->sk_protocol, maddr);
break;
default:
err = -EPROTONOSUPPORT;
}
if (err)
goto done;
msk->sk_state = MISDN_BOUND;
msk->ch.protocol = msk->sk_protocol;
done:
return err;
}
int mISDN_data_getname(void *inst, struct sockaddr *addr, int __attribute__((unused)) peer)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
if (!msk->dev)
return -EBADFD;
maddr->family = AF_ISDN;
maddr->dev = msk->dev->id;
maddr->channel = msk->ch.nr;
maddr->sapi = msk->ch.addr & 0xff;
maddr->tei = (msk->ch.addr >> 8) & 0xff;
return sizeof(*maddr);
}
int mISDN_data_create(void **inst, int protocol)
{
struct mISDN_sock *msk;
msk = calloc(1, sizeof(*msk));
if (!msk)
return -ENOMEM;
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
skb_queue_head_init(&msk->sk_receive_queue);
msk->sk_protocol = protocol;
msk->sk_state = MISDN_OPEN;
mISDN_sock_link(&data_sockets, msk);
*inst = msk;
return 0;
}
int mISDN_base_release(void *inst)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
if (!msk)
return 0;
mISDN_sock_unlink(&base_sockets, msk);
free(msk);
return 0;
}
int mISDN_base_ioctl(void *inst __attribute__((unused)), unsigned int cmd, void *arg)
{
int err = 0, id;
struct mISDNdevice *dev;
struct mISDNversion ver;
switch (cmd) {
case IMGETVERSION:
ver.major = MISDN_MAJOR_VERSION;
ver.minor = MISDN_MINOR_VERSION;
ver.release = MISDN_RELEASE;
memcpy((void *)arg, &ver, sizeof(ver));
break;
case IMGETCOUNT:
id = get_mdevice_count();
*((int *)arg) = id;
break;
case IMGETDEVINFO:
id = *((int *)arg);
dev = get_mdevice(id);
if (dev) {
struct mISDN_devinfo di;
memset(&di, 0, sizeof(di));
di.id = dev->id;
di.Dprotocols = dev->Dprotocols;
di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
di.protocol = dev->D.protocol;
memcpy(di.channelmap, dev->channelmap,
sizeof(di.channelmap));
di.nrbchan = dev->nrbchan;
strncpy(di.name, dev_name(dev), sizeof(di.name));
di.name[sizeof(di.name) - 1] = '\0';
memcpy((void *)arg, &di, sizeof(di));
} else
err = -ENODEV;
break;
case IMSETDEVNAME:
{
struct mISDN_devrename dn;
memcpy(&dn, (void *)arg, sizeof(dn));
dn.name[sizeof(dn.name) - 1] = '\0';
dev = get_mdevice(dn.id);
if (dev)
dev_set_name(dev, "%s", dn.name);
else
err = -ENODEV;
err = -EINVAL;
}
break;
default:
err = -EINVAL;
}
return err;
}
int mISDN_base_bind(void *inst, struct sockaddr *addr, int addr_len)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
int err = 0;
if (addr_len < (int)sizeof(struct sockaddr_mISDN))
return -EINVAL;
if (!maddr || maddr->family != AF_ISDN)
return -EINVAL;
if (msk->dev) {
err = -EALREADY;
goto done;
}
msk->dev = get_mdevice(maddr->dev);
if (!msk->dev) {
err = -ENODEV;
goto done;
}
msk->sk_state = MISDN_BOUND;
done:
return err;
}
int mISDN_base_create(void **inst, int protocol)
{
struct mISDN_sock *msk;
msk = calloc(1, sizeof(*msk));
if (!msk)
return -ENOMEM;
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
skb_queue_head_init(&msk->sk_receive_queue);
msk->sk_protocol = protocol;
msk->sk_state = MISDN_OPEN;
mISDN_sock_link(&base_sockets, msk);
*inst = msk;
return 0;
}
int misdn_sock_init(u_int *deb)
{
debug = deb;
return 0;
}
void misdn_sock_cleanup(void)
{
}

14
src/libmisdn/socket.h Normal file
View File

@ -0,0 +1,14 @@
int mISDN_base_create(void **inst, int protocol);
int mISDN_base_bind(void *inst, struct sockaddr *addr, int addr_len);
int mISDN_base_ioctl(void *inst, unsigned int cmd, void *arg);
int mISDN_base_release(void *inst);
int mISDN_data_create(void **inst, int protocol);
int mISDN_data_getname(void *inst, struct sockaddr *addr, int peer);
int mISDN_data_bind(void *inst, struct sockaddr *addr, int addr_len);
int mISDN_data_ioctl(void *inst, unsigned int cmd, void *arg);
int mISDN_data_release(void *inst);
int mISDN_data_sendmsg(void *inst, unsigned char *data, size_t len, int flags, struct sockaddr_mISDN *maddr);
int mISDN_data_recvmsg(void *inst, unsigned char *data, size_t len, int flags, struct sockaddr_mISDN *maddr);

662
src/libmisdn/stack.c Normal file
View File

@ -0,0 +1,662 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "mISDNif.h"
#include "core.h"
#include <stdio.h>
#include <stdarg.h>
static u_int *debug;
static inline void
_queue_message(struct mISDNstack *st, struct sk_buff *skb)
{
struct mISDNhead *hh = mISDN_HEAD_P(skb);
if (*debug & DEBUG_QUEUE_FUNC)
printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
__func__, hh->prim, hh->id, skb);
skb_queue_tail(&st->msgq, skb);
if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) {
test_and_set_bit(mISDN_STACK_WORK, &st->status);
// wake_up_interruptible(&st->workq);
}
}
static int
mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
{
_queue_message(ch->st, skb);
return 0;
}
static struct mISDNchannel *
get_channel4id(struct mISDNstack *st, u_int id)
{
struct mISDNchannel *ch;
mutex_lock(&st->lmutex);
list_for_each_entry(ch, &st->layer2, list) {
if (id == ch->nr)
goto unlock;
}
ch = NULL;
unlock:
mutex_unlock(&st->lmutex);
return ch;
}
static void
send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb)
{
struct mISDN_sock *msk;
struct sk_buff *cskb = NULL;
read_lock(&sl->lock);
m_hlist_for_each_entry(msk, &sl->head, list) {
if (msk->sk_state != MISDN_BOUND)
continue;
if (!cskb)
cskb = skb_copy(skb, GFP_ATOMIC);
if (!cskb) {
printk(KERN_WARNING "%s no skb\n", __func__);
break;
}
skb_queue_tail(&msk->sk_receive_queue, cskb);
cskb = NULL;
}
read_unlock(&sl->lock);
dev_kfree_skb(cskb);
}
static void
send_layer2(struct mISDNstack *st, struct sk_buff *skb)
{
struct sk_buff *cskb;
struct mISDNhead *hh = mISDN_HEAD_P(skb);
struct mISDNchannel *ch;
int ret;
if (!st)
return;
mutex_lock(&st->lmutex);
if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */
list_for_each_entry(ch, &st->layer2, list) {
if (list_is_last(&ch->list, &st->layer2)) {
cskb = skb;
skb = NULL;
} else {
cskb = skb_copy(skb, GFP_KERNEL);
}
if (cskb) {
ret = ch->send(ch, cskb);
if (ret) {
if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s ch%d prim(%x) addr(%x)"
" err %d\n",
__func__, ch->nr,
hh->prim, ch->addr, ret);
dev_kfree_skb(cskb);
}
} else {
printk(KERN_WARNING "%s ch%d addr %x no mem\n",
__func__, ch->nr, ch->addr);
goto out;
}
}
} else {
list_for_each_entry(ch, &st->layer2, list) {
if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) {
ret = ch->send(ch, skb);
if (!ret)
skb = NULL;
goto out;
}
}
ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb);
if (!ret)
skb = NULL;
else if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s mgr prim(%x) err %d\n",
__func__, hh->prim, ret);
}
out:
mutex_unlock(&st->lmutex);
dev_kfree_skb(skb);
}
static inline int
send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
{
struct mISDNhead *hh = mISDN_HEAD_P(skb);
struct mISDNchannel *ch;
int lm;
lm = hh->prim & MISDN_LAYERMASK;
if (*debug & DEBUG_QUEUE_FUNC)
printk(KERN_DEBUG "%s prim(%x) id(%x) lm(%x) %p\n",
__func__, hh->prim, hh->id, lm, skb);
if (lm == 0x1) {
if (!m_hlist_empty(&st->l1sock.head)) {
__net_timestamp(skb);
send_socklist(&st->l1sock, skb);
}
return st->layer1->send(st->layer1, skb);
} else if (lm == 0x2) {
if (!m_hlist_empty(&st->l1sock.head))
send_socklist(&st->l1sock, skb);
send_layer2(st, skb);
return 0;
} else if (lm == 0x4) {
ch = get_channel4id(st, hh->id);
if (ch)
return ch->send(ch, skb);
else
printk(KERN_WARNING
"%s: dev(%s) prim(%x) id(%x) no channel\n",
__func__, dev_name(st->dev), hh->prim,
hh->id);
} else if (lm == 0x8) {
WARN_ON(lm == 0x8);
ch = get_channel4id(st, hh->id);
if (ch)
return ch->send(ch, skb);
else
printk(KERN_WARNING
"%s: dev(%s) prim(%x) id(%x) no channel\n",
__func__, dev_name(st->dev), hh->prim,
hh->id);
} else {
/* broadcast not handled yet */
printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
__func__, dev_name(st->dev), hh->prim);
}
return -ESRCH;
}
static void
do_clear_stack(struct mISDNstack __attribute__((unused)) *st)
{
}
int
work_stack(struct mISDNdevice *dev)
{
struct mISDNstack *st = dev->D.st;
int work = 0;
#if 0
#ifdef MISDN_MSG_STATS
u64 utime, stime;
#endif
#endif
int err = 0;
#if 0
sigfillset(&current->blocked);
if (*debug & DEBUG_MSG_THREAD)
printk(KERN_DEBUG "mISDNStackd %s started\n",
dev_name(st->dev));
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
for (;;) {
#endif
struct sk_buff *skb;
if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) {
test_and_clear_bit(mISDN_STACK_WORK, &st->status);
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
} else
test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
while (test_bit(mISDN_STACK_WORK, &st->status)) {
skb = skb_dequeue(&st->msgq);
if (!skb) {
test_and_clear_bit(mISDN_STACK_WORK,
&st->status);
/* test if a race happens */
skb = skb_dequeue(&st->msgq);
if (!skb)
continue;
test_and_set_bit(mISDN_STACK_WORK,
&st->status);
}
work = 1;
#ifdef MISDN_MSG_STATS
st->msg_cnt++;
#endif
err = send_msg_to_layer(st, skb);
if (unlikely(err)) {
if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s: %s prim(%x) id(%x) "
"send call(%d)\n",
__func__, dev_name(st->dev),
mISDN_HEAD_PRIM(skb),
mISDN_HEAD_ID(skb), err);
dev_kfree_skb(skb);
continue;
}
if (unlikely(test_bit(mISDN_STACK_STOPPED,
&st->status))) {
test_and_clear_bit(mISDN_STACK_WORK,
&st->status);
test_and_clear_bit(mISDN_STACK_RUNNING,
&st->status);
break;
}
}
if (test_bit(mISDN_STACK_CLEARING, &st->status)) {
test_and_set_bit(mISDN_STACK_STOPPED, &st->status);
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
do_clear_stack(st);
test_and_clear_bit(mISDN_STACK_CLEARING, &st->status);
test_and_set_bit(mISDN_STACK_RESTART, &st->status);
}
if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) {
test_and_clear_bit(mISDN_STACK_STOPPED, &st->status);
test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
if (!skb_queue_empty(&st->msgq))
test_and_set_bit(mISDN_STACK_WORK,
&st->status);
}
#if 0
if (test_bit(mISDN_STACK_ABORT, &st->status))
break;
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
#endif
#ifdef MISDN_MSG_STATS
st->sleep_cnt++;
#endif
#if 0
test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
wait_event_interruptible(st->workq, (st->status &
mISDN_STACK_ACTION_MASK));
if (*debug & DEBUG_MSG_THREAD)
printk(KERN_DEBUG "%s: %s wake status %08lx\n",
__func__, dev_name(st->dev), st->status);
test_and_set_bit(mISDN_STACK_ACTIVE, &st->status);
test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status);
if (test_bit(mISDN_STACK_STOPPED, &st->status)) {
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
#ifdef MISDN_MSG_STATS
st->stopped_cnt++;
#endif
}
}
#ifdef MISDN_MSG_STATS
printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d "
"msg %d sleep %d stopped\n",
dev_name(st->dev), st->msg_cnt, st->sleep_cnt,
st->stopped_cnt);
task_cputime(st->thread, &utime, &stime);
printk(KERN_DEBUG
"mISDNStackd daemon for %s utime(%llu) stime(%llu)\n",
dev_name(st->dev), utime, stime);
printk(KERN_DEBUG
"mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
dev_name(st->dev), st->thread->nvcsw, st->thread->nivcsw);
printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
dev_name(st->dev));
#endif
test_and_set_bit(mISDN_STACK_KILLED, &st->status);
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
test_and_clear_bit(mISDN_STACK_ABORT, &st->status);
skb_queue_purge(&st->msgq);
st->thread = NULL;
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
#endif
return work;
}
static int
l1_receive(struct mISDNchannel *ch, struct sk_buff *skb)
{
if (!ch->st)
return -ENODEV;
__net_timestamp(skb);
_queue_message(ch->st, skb);
return 0;
}
void
set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei)
{
ch->addr = sapi | (tei << 8);
}
void
__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
{
list_add_tail(&ch->list, &st->layer2);
}
void
add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
{
mutex_lock(&st->lmutex);
__add_layer2(ch, st);
mutex_unlock(&st->lmutex);
}
static int
st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
{
if (!ch->st || !ch->st->layer1)
return -EINVAL;
return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg);
}
int
create_stack(struct mISDNdevice *dev)
{
struct mISDNstack *newst;
int err;
// DECLARE_COMPLETION_ONSTACK(done);
newst = kzalloc(sizeof(struct mISDNstack), GFP_KERNEL);
if (!newst) {
printk(KERN_ERR "kmalloc mISDN_stack failed\n");
return -ENOMEM;
}
newst->dev = dev;
INIT_LIST_HEAD(&newst->layer2);
INIT_M_HLIST_HEAD(&newst->l1sock.head);
rwlock_init(&newst->l1sock.lock);
// init_waitqueue_head(&newst->workq);
skb_queue_head_init(&newst->msgq);
mutex_init(&newst->lmutex);
dev->D.st = newst;
err = create_teimanager(dev);
if (err) {
printk(KERN_ERR "kmalloc teimanager failed\n");
kfree(newst);
return err;
}
dev->teimgr->peer = &newst->own;
dev->teimgr->recv = mISDN_queue_message;
dev->teimgr->st = newst;
newst->layer1 = &dev->D;
dev->D.recv = l1_receive;
dev->D.peer = &newst->own;
newst->own.st = newst;
newst->own.ctrl = st_own_ctrl;
newst->own.send = mISDN_queue_message;
newst->own.recv = mISDN_queue_message;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: st(%s)\n", __func__,
dev_name(newst->dev));
#if 0
newst->notify = &done;
newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
dev_name(newst->dev));
if (IS_ERR(newst->thread)) {
err = PTR_ERR(newst->thread);
printk(KERN_ERR
"mISDN:cannot create kernel thread for %s (%d)\n",
dev_name(newst->dev), err);
delete_teimanager(dev->teimgr);
kfree(newst);
} else
wait_for_completion(&done);
#endif
return err;
}
int
connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch);
struct channel_req rq;
int err;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(dev), protocol, adr->dev,
adr->channel, adr->sapi, adr->tei);
switch (protocol) {
case ISDN_P_NT_S0:
case ISDN_P_NT_E1:
case ISDN_P_TE_S0:
case ISDN_P_TE_E1:
ch->recv = mISDN_queue_message;
ch->peer = &dev->D.st->own;
ch->st = dev->D.st;
rq.protocol = protocol;
rq.adr.channel = adr->channel;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret %d (dev %d)\n", __func__, err,
dev->id);
if (err)
return err;
write_lock_bh(&dev->D.st->l1sock.lock);
m_hlist_add_head(&msk->list, &dev->D.st->l1sock.head);
write_unlock_bh(&dev->D.st->l1sock.lock);
break;
default:
return -ENOPROTOOPT;
}
return 0;
}
int
connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct channel_req rq, rq2;
int pmask, err;
struct Bprotocol *bp;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(dev), protocol,
adr->dev, adr->channel, adr->sapi,
adr->tei);
ch->st = dev->D.st;
pmask = 1 << (protocol & ISDN_P_B_MASK);
if (pmask & dev->Bprotocols) {
rq.protocol = protocol;
rq.adr = *adr;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
if (err)
return err;
ch->recv = rq.ch->send;
ch->peer = rq.ch;
rq.ch->recv = ch->send;
rq.ch->peer = ch;
rq.ch->st = dev->D.st;
} else {
bp = get_Bprotocol4mask(pmask);
if (!bp)
return -ENOPROTOOPT;
rq2.protocol = protocol;
rq2.adr = *adr;
rq2.ch = ch;
err = bp->create(&rq2);
if (err)
return err;
ch->recv = rq2.ch->send;
ch->peer = rq2.ch;
rq2.ch->st = dev->D.st;
rq.protocol = rq2.protocol;
rq.adr = *adr;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
if (err) {
rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL);
return err;
}
rq2.ch->recv = rq.ch->send;
rq2.ch->peer = rq.ch;
rq.ch->recv = rq2.ch->send;
rq.ch->peer = rq2.ch;
rq.ch->st = dev->D.st;
}
ch->protocol = protocol;
ch->nr = rq.ch->nr;
return 0;
}
int
create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct channel_req rq;
int err;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(dev), protocol,
adr->dev, adr->channel, adr->sapi,
adr->tei);
rq.protocol = ISDN_P_TE_S0;
if (dev->Dprotocols & (1 << ISDN_P_TE_E1))
rq.protocol = ISDN_P_TE_E1;
switch (protocol) {
case ISDN_P_LAPD_NT:
rq.protocol = ISDN_P_NT_S0;
if (dev->Dprotocols & (1 << ISDN_P_NT_E1))
rq.protocol = ISDN_P_NT_E1;
/* FALLTHRU */
case ISDN_P_LAPD_TE:
ch->recv = mISDN_queue_message;
ch->peer = &dev->D.st->own;
ch->st = dev->D.st;
rq.adr.channel = 0;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
if (err)
break;
rq.protocol = protocol;
rq.adr = *adr;
rq.ch = ch;
err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err);
if (!err) {
if ((protocol == ISDN_P_LAPD_NT) && !rq.ch)
break;
add_layer2(rq.ch, dev->D.st);
rq.ch->recv = mISDN_queue_message;
rq.ch->peer = &dev->D.st->own;
rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */
}
break;
default:
err = -EPROTONOSUPPORT;
}
return err;
}
void
delete_channel(struct mISDNchannel *ch)
{
struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch);
struct mISDNchannel *pch;
if (!ch->st) {
printk(KERN_WARNING "%s: no stack\n", __func__);
return;
}
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__,
dev_name(ch->st->dev), ch->protocol);
if (ch->protocol >= ISDN_P_B_START) {
if (ch->peer) {
ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL);
ch->peer = NULL;
}
return;
}
switch (ch->protocol) {
case ISDN_P_NT_S0:
case ISDN_P_TE_S0:
case ISDN_P_NT_E1:
case ISDN_P_TE_E1:
write_lock_bh(&ch->st->l1sock.lock);
m_hlist_del(&msk->list);
write_unlock_bh(&ch->st->l1sock.lock);
ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL);
break;
case ISDN_P_LAPD_TE:
pch = get_channel4id(ch->st, ch->nr);
if (pch) {
mutex_lock(&ch->st->lmutex);
list_del(&pch->list);
mutex_unlock(&ch->st->lmutex);
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
pch = ch->st->dev->teimgr;
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
} else
printk(KERN_WARNING "%s: no l2 channel\n",
__func__);
break;
case ISDN_P_LAPD_NT:
pch = ch->st->dev->teimgr;
if (pch) {
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
} else
printk(KERN_WARNING "%s: no l2 channel\n",
__func__);
break;
default:
break;
}
return;
}
void
delete_stack(struct mISDNdevice *dev)
{
struct mISDNstack *st = dev->D.st;
// DECLARE_COMPLETION_ONSTACK(done);
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: st(%s)\n", __func__,
dev_name(st->dev));
if (dev->teimgr)
delete_teimanager(dev->teimgr);
if (st->thread) {
if (st->notify) {
printk(KERN_WARNING "%s: notifier in use\n",
__func__);
// complete(st->notify);
}
// st->notify = &done;
test_and_set_bit(mISDN_STACK_ABORT, &st->status);
test_and_set_bit(mISDN_STACK_WAKEUP, &st->status);
// wake_up_interruptible(&st->workq);
// wait_for_completion(&done);
}
if (!list_empty(&st->layer2))
printk(KERN_WARNING "%s: layer2 list not empty\n",
__func__);
if (!m_hlist_empty(&st->l1sock.head))
printk(KERN_WARNING "%s: layer1 list not empty\n",
__func__);
kfree(st);
}
void
mISDN_initstack(u_int *dp)
{
debug = dp;
}

1412
src/libmisdn/tei.c Normal file

File diff suppressed because it is too large Load Diff

19
src/libmisdn/timer.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _MISDN_TIMER_H
#define _MISDN_TIMER_H
#include <osmocom/core/timer.h>
#if 0
#define timer_list timer
#define timer_setup(ti, fu, flags) timer_init(ti, fu, NULL)
#define add_timer osmo_timer_schedule
#define del_timer osmo_timer_del
#define timer_pending osmo_timer_pending
#endif
#define from_timer(var, callback_timer, timer_fieldname) \
container_of(callback_timer, typeof(*var), timer_fieldname)
#endif

View File

@ -0,0 +1,23 @@
AM_CPPFLAGS = -Wall -Wextra -Wno-unused-parameter -Wno-array-bounds -Wno-sign-compare -g $(all_includes) -I$(srcdir)/include
noinst_LIBRARIES = libmisdnuser.a
ASN1_SRC = asn1/asn1.c asn1/asn1_enc.c asn1/asn1_generic.c
SUPPSERV_SRC = suppserv/aoc.c suppserv/basic_service.c suppserv/component.c \
suppserv/diversion.c suppserv/address.c suppserv/ccbs.c \
suppserv/fac.c suppserv/ect.c suppserv/3pty.c
SUPPSERV_INC = include/ccbs.h include/diversion.h include/ect.h include/asn1.h \
include/3pty.h
MISC_SRC = misc/debug.c misc/fsm.c misc/mbuffer.c misc/mtimer.c
LAYER3_SRC = layer3/dss1net.c layer3/dss1user.c layer3/layer3.c layer3/mlayer3.c layer3/q931.c
INCLUDE_SRC = include/debug.h include/dss1.h include/fsm.h include/helper.h include/layer3.h \
include/mlist.h include/mtimer.h $(SUPPSERV_INC)
libmisdnuser_a_SOURCES = $(MISC_SRC) $(LAYER3_SRC) $(INCLUDE_SRC) $(ASN1_SRC) $(SUPPSERV_SRC)

View File

@ -0,0 +1,57 @@
/*
* Basic ASN1 parser functions
*
* Copyright 2009,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 "asn1.h"
int ParseTag(u_char * p, u_char * end, int *tag)
{
if (p >= end) {
eprint("ParseTag underflow %p/%p\n", p, end);
return -1;
}
*tag = *p;
return 1;
}
int ParseLen(u_char * p, u_char * end, int *len)
{
int l, i;
if (p >= end) {
eprint("ParseLen underflow %p/%p\n", p, end);
return -1;
}
if (*p == 0x80) { // indefinite
*len = -1;
return 1;
}
if (!(*p & 0x80)) { // one byte
*len = *p;
return 1;
}
*len = 0;
l = *p & ~0x80;
p++;
if (p + l >= end) {
eprint("ParseLen underflow %p/%p\n", p, end);
return -1;
}
for (i = 0; i < l; i++) {
*len = (*len << 8) + *p;
p++;
}
return l + 1;
}

View File

@ -0,0 +1,173 @@
/*
* Encode basic ASN1 elements
*
* Copyright 2009,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 "asn1.h"
#include <string.h>
int encodeLen_Long_u8(__u8 * dest, __u8 length)
{
dest[0] = 0x80 + ASN1_NUM_OCTETS_LONG_LENGTH_u8 - 1;
dest[1] = length;
return ASN1_NUM_OCTETS_LONG_LENGTH_u8;
} /* end encodeLen_Long_u8() */
int encodeLen_Long_u16(__u8 * dest, __u16 length)
{
dest[0] = 0x80 + ASN1_NUM_OCTETS_LONG_LENGTH_u16 - 1;
dest[1] = (length >> 8) & 0xFF;
dest[2] = length & 0xFF;
return ASN1_NUM_OCTETS_LONG_LENGTH_u16;
} /* end encodeLen_Long_u16() */
int encodeNull(__u8 * dest, __u8 tagType)
{
dest[0] = tagType;
dest[1] = 0; /* length */
return 2;
}
int encodeBoolean(__u8 * dest, __u8 tagType, __u32 i)
{
dest[0] = tagType;
dest[1] = 1; /* length */
dest[2] = i ? 1 : 0; /* value */
return 3;
}
int encodeInt(__u8 * dest, __u8 tagType, __s32 i)
{
unsigned count;
__u32 test_mask;
__u32 value;
__u8 *p;
dest[0] = tagType;
/* Find most significant octet of 32 bit integer that carries meaning. */
test_mask = 0xFF800000;
value = (__u32) i;
for (count = 4; --count;) {
if ((value & test_mask) != test_mask && (value & test_mask) != 0) {
/*
* The first 9 bits of a multiple octet integer is not
* all ones or zeroes.
*/
break;
}
test_mask >>= 8;
} /* end for */
/* length */
dest[1] = count + 1;
/* Store value */
p = &dest[2];
do {
value = (__u32) i;
value >>= (8 * count);
*p++ = value & 0xFF;
} while (count--);
return p - dest;
}
int encodeEnum(__u8 * dest, __u8 tagType, __s32 i)
{
return encodeInt(dest, tagType, i);
}
/*
* Use to encode the following string types:
* ASN1_TAG_OCTET_STRING
* ASN1_TAG_NUMERIC_STRING
* ASN1_TAG_PRINTABLE_STRING
* ASN1_TAG_IA5_STRING
*
* Note The string length MUST be less than 128 characters.
*/
static int encodeString(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len)
{
__u8 *p;
int i;
dest[0] = tagType;
/* Store value */
p = &dest[2];
for (i = 0; i < len; i++)
*p++ = *str++;
/* length */
dest[1] = p - &dest[2];
return p - dest;
}
int encodeOctetString(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len)
{
return encodeString(dest, tagType, str, len);
} /* end encodeOctetString() */
int encodeNumericString(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len)
{
return encodeString(dest, tagType, str, len);
} /* end encodeNumericString() */
int encodePrintableString(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len)
{
return encodeString(dest, tagType, str, len);
} /* end encodePrintableString() */
int encodeIA5String(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len)
{
return encodeString(dest, tagType, str, len);
} /* end encodeIA5String() */
int encodeOid(__u8 * dest, __u8 tagType, const struct asn1Oid *oid)
{
unsigned numValues;
unsigned count;
__u32 value;
__u8 *p;
dest[0] = tagType;
/* For all OID subidentifer values */
p = &dest[2];
for (numValues = 0; numValues < oid->numValues; ++numValues) {
/*
* Count the number of 7 bit chunks that are needed
* to encode the integer.
*/
value = oid->value[numValues] >> 7;
for (count = 0; value; ++count) {
/* There are bits still set */
value >>= 7;
} /* end for */
/* Store OID subidentifier value */
do {
value = oid->value[numValues];
value >>= (7 * count);
*p++ = (value & 0x7F) | (count ? 0x80 : 0);
} while (count--);
} /* end for */
/* length */
dest[1] = p - &dest[2];
return p - dest;
} /* end encodeOid() */

View File

@ -0,0 +1,234 @@
/*
* ASN1 parser for standard elements
*
* Copyright 2009,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 "asn1.h"
// ======================================================================
// general ASN.1
int ParseBoolean(struct asn1_parm *pc, u_char * p, u_char * end, int *i)
{
INIT;
*i = *p ? 1 : 0;
dprint(DBGM_ASN1_DEC, " DEBUG> BOOL = %d %#x\n", *i, *i);
return end - beg;
}
int ParseNull(struct asn1_parm *pc, u_char * p, u_char * end, int dummy)
{
INIT;
return p - beg;
}
static int ParseUInt(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *i)
{
INIT;
*i = 0;
while (len--) {
*i = (*i << 8) | *p;
p++;
}
return p - beg;
} /* end ParseInt() */
static int ParseSInt(struct asn1_parm *pc, u_char * p, u_char * end, signed int *i)
{
INIT;
/* Read value as signed */
if (*p & 0x80) {
/* The value is negative */
*i = -1;
} else {
*i = 0;
}
while (len--) {
*i = (*i << 8) | *p;
p++;
}
return p - beg;
} /* end ParseInt() */
int ParseUnsignedInteger(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *i)
{
int length;
length = ParseUInt(pc, p, end, i);
dprint(DBGM_ASN1_DEC, " DEBUG> INT = %d %#x\n", *i, *i);
return length;
}
int ParseSignedInteger(struct asn1_parm *pc, u_char * p, u_char * end, signed int *i)
{
int length;
length = ParseSInt(pc, p, end, i);
dprint(DBGM_ASN1_DEC, " DEBUG> INT = %d %#x\n", *i, *i);
return length;
}
int ParseEnum(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *i)
{
int length;
length = ParseUInt(pc, p, end, i);
dprint(DBGM_ASN1_DEC, " DEBUG> ENUM = %d %#x\n", *i, *i);
return length;
}
int ParseIA5String(struct asn1_parm *pc, u_char * p, u_char * end, struct asn1ParseString *str)
{
char *buf;
int numChars;
INIT;
if (len < 0) {
/* We do not handle indefinite length strings */
return -1;
}
if (str->maxSize < len + 1) {
numChars = str->maxSize - 1;
} else {
numChars = len;
}
len -= numChars;
str->length = numChars;
buf = str->buf;
while (numChars--)
*buf++ = *p++;
*buf = 0;
dprint(DBGM_ASN1_DEC, " DEBUG> IA5 = %s\n", str->buf);
if (0 < len) {
wprint("Discard %d IA5 max %zd\n", len, str->maxSize);
/* Discard the remainder of the string. We have no room left. */
p += len;
}
return p - beg;
}
int ParseNumericString(struct asn1_parm *pc, u_char * p, u_char * end, struct asn1ParseString *str)
{
char *buf;
int numChars;
INIT;
if (len < 0) {
/* We do not handle indefinite length strings */
return -1;
}
if (str->maxSize < len + 1) {
numChars = str->maxSize - 1;
} else {
numChars = len;
}
len -= numChars;
str->length = numChars;
buf = str->buf;
while (numChars--)
*buf++ = *p++;
*buf = 0;
dprint(DBGM_ASN1_DEC, " DEBUG> NumStr = %s\n", str->buf);
if (0 < len) {
wprint("Discard %d NumStr max %zd\n", len, str->maxSize);
/* Discard the remainder of the string. We have no room left. */
p += len;
}
return p - beg;
}
int ParseOctetString(struct asn1_parm *pc, u_char * p, u_char * end, struct asn1ParseString *str)
{
char *buf;
INIT;
if (len < 0) {
/* We do not handle indefinite length strings */
return -1;
}
if (str->maxSize < len + 1) {
/*
* The octet string will not fit in the available buffer
* and truncating it is not a good idea in all cases.
*/
eprint("OctetString does not fit %d max %zd\n", len, str->maxSize);
return -1;
}
str->length = len;
buf = str->buf;
while (len--)
*buf++ = *p++;
*buf = 0;
if (DBGM_ASN1_DEC & mI_debug_mask) {
char *tmp = malloc((3 * len) + 1);
if (tmp) {
mi_shexprint(tmp, p, len);
dprint(DBGM_ASN1_DEC, " DEBUG> Octets = %s\n", tmp);
free(tmp);
} else
eprint("MALLOC for %d bytes failed\n", (3 * len) + 1);
}
return p - beg;
}
int ParseOid(struct asn1_parm *pc, u_char * p, u_char * end, struct asn1Oid *oid)
{
int numValues;
int value;
INIT;
numValues = 0;
while (len) {
value = 0;
for (;;) {
--len;
value = (value << 7) | (*p & 0x7F);
if (!(*p++ & 0x80)) {
/* Last octet in the OID subidentifier value */
if (numValues < sizeof(oid->value) / sizeof(oid->value[0])) {
oid->value[numValues] = value;
} else {
dprint(DBGM_ASN1_WARN, "Too many OID subidentifier %d\n", value);
}
++numValues;
break;
}
if (!len) {
oid->numValues = 0;
wprint("Last OID subidentifier value (%d) not terminated!\n", value);
return -1;
}
} /* end for */
} /* end while */
if (numValues <= sizeof(oid->value) / sizeof(oid->value[0])) {
oid->numValues = numValues;
for (numValues = 0; numValues < oid->numValues; numValues++)
dprint(DBGM_ASN1_DEC, "OID->value[%d] = %d\n", numValues, oid->value[numValues]);
return p - beg;
} else {
/* Need to increase the size of the OID subidentifier list. */
wprint("Too many OID values (%d)!\n", numValues);
return -1;
}
}

View File

@ -0,0 +1,26 @@
/*
*
* Three Party (3PTY) Supplementary Services ETS 300 188-2
*
* 3PTY Facility ie encode/decode header
*/
#ifndef __ASN1_3PTY_H
#define __ASN1_3PTY_H
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------- */
int encodeFacBegin3PTY(__u8 * Dest, const struct asn1_parm *pc, const void *unused);
int encodeFacEnd3PTY(__u8 * Dest, const struct asn1_parm *pc, const void *unused);
/* ------------------------------------------------------------------- */
#ifdef __cplusplus
}
#endif
#endif /* __ASN1_3PTY_H */
/* ------------------------------------------------------------------- *//* end asn1_3pty.h */

View File

@ -0,0 +1,290 @@
/*
*
* Abstract Syntax Notation.1 (ASN.1) ITU-T X.208
*
* Copyright 2009,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.
*
*/
#ifndef __ASN1_H__
#define __ASN1_H__
#include <mISDN/suppserv.h>
#include <asm/types.h>
#include <sys/types.h>
#include "debug.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------- */
int ParseTag(u_char * p, u_char * end, int *tag);
int ParseLen(u_char * p, u_char * end, int *len);
#define ASN1_TAG_BOOLEAN 1
#define ASN1_TAG_INTEGER 2
#define ASN1_TAG_BIT_STRING 3
#define ASN1_TAG_OCTET_STRING 4
#define ASN1_TAG_NULL 5
#define ASN1_TAG_OBJECT_IDENTIFIER 6
#define ASN1_TAG_ENUM 10
#define ASN1_TAG_SEQUENCE (ASN1_TAG_CONSTRUCTED | 16)
#define ASN1_TAG_SET (ASN1_TAG_CONSTRUCTED | 17)
#define ASN1_TAG_NUMERIC_STRING 18
#define ASN1_TAG_PRINTABLE_STRING 19
#define ASN1_TAG_IA5_STRING 22
#define ASN1_TAG_UTC_TIME 23
#define ASN1_TAG_CONSTRUCTED 0x20
#define ASN1_TAG_TYPE_MASK 0xC0
#define ASN1_TAG_UNIVERSAL 0x00
#define ASN1_TAG_APPLICATION_WIDE 0x40
#define ASN1_TAG_CONTEXT_SPECIFIC 0x80
#define ASN1_TAG_PRIVATE 0xC0
#define ASN1_TAG_EXPLICIT 0x100
#define ASN1_TAG_OPT 0x200
#define ASN1_NOT_TAGGED 0x400
#define CallASN1(ret, p, end, todo) \
do { \
ret = todo; \
if (ret < 0) { \
eprint("CallASN1 internal error\n"); \
return -1; \
} \
p += ret; \
} while (0)
/* INIT must be placed after the last variable declared */
#define INIT \
int tag, len; \
int ret; \
u_char *beg __attribute__((unused)); \
\
beg = p; \
CallASN1(ret, p, end, ParseTag(p, end, &tag)); \
CallASN1(ret, p, end, ParseLen(p, end, &len)); \
if (len >= 0) { \
if (p + len > end) { \
eprint("ASN1 parser underflow %p/%p in %s\n", p + len, end, __func__); \
return -1; \
} \
dprint(DBGM_ASN1_DEC, "Start p=%p end=%p-->%p len=%d\n", p, end, p + len, len); \
end = p + len; \
}
#define XSEQUENCE_1(todo, act_tag, the_tag, arg1) \
do { \
if (p < end) { \
if (((the_tag) & ~ASN1_TAG_OPT) == ASN1_NOT_TAGGED) { \
if (((act_tag) == ASN1_NOT_TAGGED) || ((u_char) (act_tag) == *p)) { \
CallASN1(ret, p, end, todo(pc, p, end, arg1)); \
} else { \
if (!((the_tag) & ASN1_TAG_OPT)) { \
eprint("ASN1 XSEQUENCE_1 internal error 1\n"); \
return -1; \
} \
} \
} else if ((the_tag) & ASN1_TAG_EXPLICIT) { \
/* EXPLICIT tags are always constructed */ \
if ((u_char) (((the_tag) & 0xff) | (((act_tag) & ASN1_TAG_TYPE_MASK) | ASN1_TAG_CONSTRUCTED)) == *p) { \
int xtag, xlen; \
CallASN1(ret, p, end, ParseTag(p, end, &xtag)); \
CallASN1(ret, p, end, ParseLen(p, end, &xlen)); \
CallASN1(ret, p, end, todo(pc, p, end, arg1)); \
} else { \
if (!((the_tag) & ASN1_TAG_OPT)) { \
eprint("ASN1 XSEQUENCE_1 internal error 2\n"); \
return -1; \
} \
} \
} else { /* IMPLICIT */ \
if ((u_char) (((the_tag) & 0xff) | ((act_tag) & (ASN1_TAG_TYPE_MASK | ASN1_TAG_CONSTRUCTED))) == *p) { \
CallASN1(ret, p, end, todo(pc, p, end, arg1)); \
} else { \
if (!((the_tag) & ASN1_TAG_OPT)) { \
eprint("ASN1 XSEQUENCE_1 internal error 3\n"); \
return -1; \
} \
} \
} \
} else { \
if (!((the_tag) & ASN1_TAG_OPT)) { \
eprint("ASN1 XSEQUENCE_1 internal error 4\n"); \
return -1; \
} \
} \
} while (0)
#define XSEQUENCE_OPT_1(todo, act_tag, the_tag, arg1) \
XSEQUENCE_1(todo, act_tag, (the_tag | ASN1_TAG_OPT), arg1)
#define XSEQUENCE(todo, act_tag, the_tag) XSEQUENCE_1(todo, act_tag, the_tag, -1)
#define XSEQUENCE_OPT(todo, act_tag, the_tag) XSEQUENCE_OPT_1(todo, act_tag, the_tag, -1)
#define XCHOICE_1(todo, act_tag, the_tag, arg1) \
do { \
if ((act_tag) == ASN1_NOT_TAGGED) { \
return todo(pc, beg, end, arg1); \
} else if ((the_tag) == ASN1_NOT_TAGGED) { \
if ((act_tag) == tag) { \
return todo(pc, beg, end, arg1); \
} \
} else if ((act_tag) & ASN1_TAG_EXPLICIT) { \
/* EXPLICIT tags are always constructed */ \
if (((the_tag) | (((act_tag) & ASN1_TAG_TYPE_MASK) | ASN1_TAG_CONSTRUCTED)) == tag) { \
return todo(pc, p, end, arg1); \
} \
} else { \
if (((the_tag) | ((act_tag) & (ASN1_TAG_TYPE_MASK | ASN1_TAG_CONSTRUCTED))) == tag) { \
return todo(pc, beg, end, arg1); \
} \
} \
} while (0)
#define XCHOICE(todo, act_tag, the_tag) XCHOICE_1(todo, act_tag, the_tag, -1)
#define XCHOICE_DEFAULT \
do { \
eprint("ASN1 XCHOICE_DEFAULT internal error in %s\n", __func__); \
return -1; \
} while (0)
#define CHECK_P \
do { \
if (p >= end) { \
eprint("ASN1 parser underflow %p/%p in %s\n", p, end, __func__); \
return -1; \
} \
} while (0)
const struct asn1OidConvert *FindOidByOidValue(int length, const __u16 oidValues[]);
const struct asn1OidConvert *FindOidByEnum(__u16 value);
__u16 ConvertOidToEnum(const struct asn1Oid *oid, __u16 errorValue);
int ConvertEnumToOid(struct asn1Oid *oid, __u16 enumValue);
#define IsEnumOid(enumValue) \
((FAC_OID_BASE(1) <= (enumValue) \
&& (enumValue) < FAC_OID_BASE(FacOIDBase_Last)) ? 1 : 0)
/*
** ASN.1 Encoding
*/
/* Facility-Information-Element-Components prototypes */
enum asn1ComponentTag {
asn1ComponentTag_Invoke = ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED | 1,
asn1ComponentTag_Result = ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED | 2,
asn1ComponentTag_Error = ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED | 3,
asn1ComponentTag_Reject = ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED | 4,
};
__u8 *encodeComponent_Head(__u8 * p, enum asn1ComponentTag componentTag);
__u8 *encodeComponent_Head_Long_u8(__u8 * p, enum asn1ComponentTag componentTag);
int encodeComponent_Length(__u8 * msg, __u8 * end);
int encodeComponent_Length_Long_u8(__u8 * msg, __u8 * p);
__u8 *encodeComponentInvoke_Head(__u8 * Dest, int InvokeID, enum Operation OperationValue);
__u8 *encodeComponentInvoke_Head_Long_u8(__u8 * Dest, int InvokeID, enum Operation OperationValue);
int encodeOperationValue(__u8 * dest, int operationValue);
int encodeErrorValue(__u8 * dest, int errorValue);
/* Primitive ASN.1 prototypes */
#define ASN1_NUM_OCTETS_LONG_LENGTH_u8 2
#define ASN1_NUM_OCTETS_LONG_LENGTH_u16 3
int encodeLen_Long_u8(__u8 * dest, __u8 length);
int encodeLen_Long_u16(__u8 * dest, __u16 length);
int encodeNull(__u8 * dest, __u8 tagType);
int encodeBoolean(__u8 * dest, __u8 tagType, __u32 i);
int encodeInt(__u8 * dest, __u8 tagType, __s32 i);
int encodeEnum(__u8 * dest, __u8 tagType, __s32 i);
int encodeOctetString(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len);
int encodeNumericString(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len);
int encodePrintableString(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len);
int encodeIA5String(__u8 * dest, __u8 tagType, const __s8 * str, __u8 len);
int encodeOid(__u8 * dest, __u8 tagType, const struct asn1Oid *oid);
/* Addressing-Data-Elements prototypes */
int encodePartyNumber_Full(__u8 * Dest, const struct FacPartyNumber *PartyNumber);
int encodePartySubaddress_Full(__u8 * Dest, const struct FacPartySubaddress *PartySubaddress);
int encodeAddress_Full(__u8 * Dest, const struct FacAddress *Address);
int encodePresentedNumberUnscreened_Full(__u8 * Dest, const struct FacPresentedNumberUnscreened *Presented);
int encodePresentedAddressScreened_Full(__u8 * Dest, const struct FacPresentedAddressScreened *Presented);
/*
** ASN.1 Parsing
*/
/* Facility-Information-Element-Components prototypes */
int ParseComponent(struct asn1_parm *parm, u_char * p, u_char * end);
/* Primitive ASN.1 prototypes */
int ParseBoolean(struct asn1_parm *pc, u_char * p, u_char * end, int *i);
int ParseNull(struct asn1_parm *pc, u_char * p, u_char * end, int dummy);
int ParseUnsignedInteger(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *i);
int ParseSignedInteger(struct asn1_parm *pc, u_char * p, u_char * end, signed int *i);
int ParseEnum(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *i);
struct asn1ParseString {
char *buf; /* Where to put the parsed string characters */
size_t maxSize; /* sizeof string buffer (Including an ASCIIz terminator) */
size_t length; /* length of string put into the string buffer (Without the terminator) */
};
int ParseIA5String(struct asn1_parm *pc, u_char * p, u_char * end, struct asn1ParseString *str);
int ParseNumericString(struct asn1_parm *pc, u_char * p, u_char * end, struct asn1ParseString *str);
int ParseOctetString(struct asn1_parm *pc, u_char * p, u_char * end, struct asn1ParseString *str);
int ParseOid(struct asn1_parm *pc, u_char * p, u_char * end, struct asn1Oid *oid);
/* Addressing-Data-Elements prototypes */
int ParsePartyNumber_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartyNumber *PartyNumber);
int ParsePartySubaddress_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartySubaddress *PartySubaddress);
int ParseAddress_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAddress *Address);
int ParsePresentedNumberUnscreened_Full(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacPresentedNumberUnscreened *Presented);
int ParsePresentedAddressScreened_Full(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacPresentedAddressScreened *Presented);
/* Advice Of Charge (AOC) prototypes */
int ParseAOCECurrency(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur);
int ParseAOCDChargingUnit(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu);
int ParseAOCDCurrency(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur);
int ParseAOCDCurrencyInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur);
int ParseAOCDChargingUnitInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu);
int ParseRecordedCurrency(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur);
int ParseRecordedUnitsList(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *recordedUnits);
int ParseTypeOfChargingInfo(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *typeOfChargingInfo);
int ParseRecordedUnits(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *recordedUnits);
int ParseAOCDBillingId(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *billingId);
int ParseAOCECurrencyInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur);
int ParseAOCEChargingUnit(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu);
int ParseAOCEChargingUnitInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu);
int ParseAOCEBillingId(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *billingId);
int ParseCurrency(struct asn1_parm *pc, u_char * p, u_char * end, char *currency);
int ParseAmount(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur);
int ParseCurrencyAmount(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *currencyAmount);
int ParseMultiplier(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *multiplier);
int ParseTypeOfUnit(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *typeOfUnit);
int ParseNumberOfUnits(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *numberOfUnits);
int ParseChargingAssociation(struct asn1_parm *pc, u_char * p, u_char * end, struct ChargingAssociation *chargeAssoc);
int ParseChargeIdentifier(struct asn1_parm *pc, u_char * p, u_char * end, int *chargeIdentifier);
/* ------------------------------------------------------------------- */
#ifdef __cplusplus
}
#endif
#endif /* __ASN1_H__ */
/* ------------------------------------------------------------------- *//* end asn1.h */

View File

@ -0,0 +1,94 @@
/*
* $Id$
*
* CCBS Supplementary Services ETS 300 359-1
* CCNR Supplementary Services ETS 301 065-1
*
* Generic functional protocol for the support of supplementary services ETS 300 196-1
*
* CCBS/CCNR Facility ie encode/decode header
*/
#ifndef __ASN1_CCBS_H
#define __ASN1_CCBS_H
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------- */
int encodeFacStatusRequest(__u8 * Dest, const struct asn1_parm *pc, const struct FacStatusRequest *StatusRequest);
int ParseStatusRequest(struct asn1_parm *pc, u_char * p, u_char * end, struct FacStatusRequest *StatusRequest);
int ParseStatusRequest_RES(struct asn1_parm *pc, u_char * p, u_char * end, struct FacStatusRequest_RES *StatusRequest);
int encodeFacCallInfoRetain(__u8 * Dest, const struct asn1_parm *pc, const struct FacCallInfoRetain *CallInfoRetain);
int ParseCallInfoRetain(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCallInfoRetain *CallInfoRetain);
int encodeFacEraseCallLinkageID(__u8 * Dest, const struct asn1_parm *pc, const struct FacEraseCallLinkageID *EraseCallLinkageID);
int ParseEraseCallLinkageID(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacEraseCallLinkageID *EraseCallLinkageID);
int encodeFacCCBSDeactivate(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSDeactivate *CCBSDeactivate);
int ParseCCBSDeactivate(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSDeactivate *CCBSDeactivate);
int encodeFacCCBSErase(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSErase *CCBSErase);
int ParseCCBSErase(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSErase *CCBSErase);
int encodeFacCCBSRemoteUserFree(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSRemoteUserFree *CCBSRemoteUserFree);
int ParseCCBSRemoteUserFree(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacCCBSRemoteUserFree *CCBSRemoteUserFree);
int encodeFacCCBSCall(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSCall *CCBSCall);
int ParseCCBSCall(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSCall *CCBSCall);
int encodeFacCCBSBFree(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSBFree *CCBSBFree);
int ParseCCBSBFree(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSBFree *CCBSBFree);
int encodeFacCCBSStopAlerting(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSStopAlerting *CCBSStopAlerting);
int ParseCCBSStopAlerting(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSStopAlerting *CCBSStopAlerting);
int encodeFacCCBSStatusRequest(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSStatusRequest *CCBSStatusRequest);
int ParseCCBSStatusRequest(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacCCBSStatusRequest *CCBSStatusRequest);
int ParseCCBSStatusRequest_RES(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacCCBSStatusRequest_RES *CCBSStatusRequest);
int encodeFacCCBSRequest(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSRequest *CCBSRequest);
int encodeFacCCNRRequest(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSRequest *CCNRRequest);
int ParseCCBSRequest(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSRequest *CCBSRequest);
int ParseCCNRRequest(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSRequest *CCNRRequest);
int ParseCCBSRequest_RES(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSRequest_RES *CCBSRequest);
int ParseCCNRRequest_RES(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBSRequest_RES *CCNRRequest);
int encodeFacCCBSInterrogate(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSInterrogate *CCBSInterrogate);
int encodeFacCCNRInterrogate(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBSInterrogate *CCNRInterrogate);
int ParseCCBSInterrogate(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacCCBSInterrogate *CCBSInterrogate);
int ParseCCNRInterrogate(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacCCBSInterrogate *CCNRInterrogate);
int ParseCCBSInterrogate_RES(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacCCBSInterrogate_RES *CCBSInterrogate);
int ParseCCNRInterrogate_RES(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacCCBSInterrogate_RES *CCNRInterrogate);
int encodeFacCCBS_T_Call(__u8 * Dest, const struct asn1_parm *pc, const void *val);
int encodeFacCCBS_T_Suspend(__u8 * Dest, const struct asn1_parm *pc, const void *val);
int encodeFacCCBS_T_Resume(__u8 * Dest, const struct asn1_parm *pc, const void *val);
int encodeFacCCBS_T_RemoteUserFree(__u8 * Dest, const struct asn1_parm *pc, const void *val);
int encodeFacCCBS_T_Available(__u8 * Dest, const struct asn1_parm *pc, const void *val);
int encodeFacCCBS_T_Request(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBS_T_Request *CCBS_T_Request);
int encodeFacCCNR_T_Request(__u8 * Dest, const struct asn1_parm *pc, const struct FacCCBS_T_Request *CCNR_T_Request);
int ParseCCBS_T_Request(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBS_T_Request *CCBS_T_Request);
int ParseCCNR_T_Request(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBS_T_Request *CCNR_T_Request);
int ParseCCBS_T_Request_RES(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBS_T_Request_RES *CCBS_T_Request);
int ParseCCNR_T_Request_RES(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCCBS_T_Request_RES *CCNR_T_Request);
/* ------------------------------------------------------------------- */
#ifdef __cplusplus
}
#endif
#endif /* __ASN1_CCBS_H */
/* ------------------------------------------------------------------- *//* end asn1_ccbs.h */

View File

@ -0,0 +1,78 @@
/* debug.h
*
* Basic Layer3 functions
*
* Author Karsten Keil <kkeil@linux-pingi.de>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
* Copyright 2009, 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.
*
*/
#ifndef ISDN_DEBUG_H
#define ISDN_DEBUG_H
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <mISDN/mISDNcompat.h>
#define L3_DEB_WARN 0x00000001
#define L3_DEB_PROTERR 0x00000002
#define L3_DEB_STATE 0x00000004
#define L3_DEB_PROC 0x00000008
#define L3_DEB_CHECK 0x00000010
#define DBGM_L3 0x00000040
#define DBGM_L3DATA 0x00000080
#define DBGM_L3BUFFER 0x00000100
#define DBGM_L2 0x00000200
#define DBGM_L2_STATE 0x00000400
#define DBGM_ASN1_WARN 0x00010000
#define DBGM_ASN1_ENC 0x00020000
#define DBGM_ASN1_DEC 0x00040000
#define DBGM_ASN1_DATA 0x00080000
#define DBGM_ALL 0xffffffff
extern unsigned int mI_debug_mask;
#define mi_thread_create(a, b, c, d) _mi_thread_create(a, b, c, d, __FILE__, __PRETTY_FUNCTION__, __LINE__, #c)
extern int _mi_thread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine) (void *),
void *arg, const char *file, const char *caller, int line, const char *start_fn);
#define eprint(fmt, ...) mi_printf(__FILE__, __LINE__, __PRETTY_FUNCTION__, 1, fmt, ##__VA_ARGS__)
#define wprint(fmt, ...) mi_printf(__FILE__, __LINE__, __PRETTY_FUNCTION__, 2, fmt, ##__VA_ARGS__)
#define iprint(fmt, ...) mi_printf(__FILE__, __LINE__, __PRETTY_FUNCTION__, 3, fmt, ##__VA_ARGS__)
#define dprint(m, fmt, ...) do { if (m & mI_debug_mask) mi_printf(__FILE__, __LINE__, __PRETTY_FUNCTION__, 4, fmt, ##__VA_ARGS__);} while(0)
extern int mi_printf(const char *file, int line, const char *func, int lev, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
#define dhexprint(m, h, d, l) do { if (m & mI_debug_mask) mi_dhexprint(__FILE__, __LINE__, __PRETTY_FUNCTION__, h, d, l);} while(0)
extern void mi_dhexprint(const char *file, int line, const char *func, const char *head, unsigned char *buf, int len);
extern void mi_shexprint(char *dest, unsigned char *buf, int len);
#ifdef MEMLEAK_DEBUG
extern void *__mi_alloc(size_t size, const char *file, int lineno, const char *func);
extern void *__mi_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func);
extern void __mi_free(void *ptr, const char *file, int lineno, const char *func);
extern void __mi_reuse(void *ptr, const char *file, int lineno, const char *func);
#undef malloc
#undef calloc
#undef free
#define malloc(s) __mi_alloc(s, __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define calloc(n, s) __mi_calloc(n, s, __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define free(p) __mi_free(p, __FILE__, __LINE__, __PRETTY_FUNCTION__)
#endif
#endif

View File

@ -0,0 +1,91 @@
/*
* $Id$
*
* Diversion Supplementary Services ETS 300 207-1 Table 3
*
* Diversion Facility ie encode/decode header
*
* Copyright 2009,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.
*
*/
#ifndef __ASN1_DIVERSION_H__
#define __ASN1_DIVERSION_H__
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------- */
int encodeFacActivationDiversion(__u8 * Dest, const struct asn1_parm *pc, const struct FacActivationDiversion *ActivationDiversion);
int ParseActivationDiversion(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacActivationDiversion *ActivationDiversion);
int encodeFacDeactivationDiversion(__u8 * Dest, const struct asn1_parm *pc, const struct FacDeactivationDiversion *DeactivationDiversion);
int ParseDeactivationDiversion(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacDeactivationDiversion *DeactivationDiversion);
int encodeFacActivationStatusNotificationDiv(__u8 * Dest, const struct asn1_parm *pc,
const struct FacActivationStatusNotificationDiv
*ActivationStatusNotificationDiv);
int ParseActivationStatusNotificationDiv(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacActivationStatusNotificationDiv *ActivationStatusNotificationDiv);
int encodeFacDeactivationStatusNotificationDiv(__u8 * Dest, const struct asn1_parm *pc,
const struct FacDeactivationStatusNotificationDiv
*DeactivationStatusNotificationDiv);
int ParseDeactivationStatusNotificationDiv(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacDeactivationStatusNotificationDiv
*DeactivationStatusNotificationDiv);
int encodeFacInterrogationDiversion(__u8 * Dest, const struct asn1_parm *pc, const struct FacInterrogationDiversion *InterrogationDiversion);
int ParseInterrogationDiversion(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacInterrogationDiversion *InterrogationDiversion);
#define ParseInterrogationDiversion_RES ParseIntResultList
int ParseIntResultList(struct asn1_parm *pc, u_char * p, u_char * end, struct FacForwardingList *IntResultList);
int encodeFacDiversionInformation(__u8 * Dest, const struct asn1_parm *pc, const struct FacDiversionInformation *DiversionInformation);
int ParseDiversionInformation(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacDiversionInformation *DiversionInformation);
int encodeFacCallDeflection(__u8 * Dest, const struct asn1_parm *pc, const struct FacCallDeflection *CallDeflection);
int ParseCallDeflection(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCallDeflection *CallDeflection);
int encodeFacCallRerouteing(__u8 * Dest, const struct asn1_parm *pc, const struct FacCallRerouteing *CallRerouteing);
int ParseCallRerouteing(struct asn1_parm *pc, u_char * p, u_char * end, struct FacCallRerouteing *CallRerouteing);
int encodeFacInterrogateServedUserNumbers(__u8 * Dest, const struct asn1_parm *pc,
const struct FacServedUserNumberList *InterrogateServedUserNumbers);
#define ParseInterrogateServedUserNumbers_RES ParseServedUserNumberList
int ParseServedUserNumberList(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacServedUserNumberList *ServedUserNumberList);
int encodeFacDivertingLegInformation1(__u8 * Dest, const struct asn1_parm *pc, const struct FacDivertingLegInformation1 *DivertingLegInformation1);
int ParseDivertingLegInformation1(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacDivertingLegInformation1 *DivertingLegInformation1);
int encodeFacDivertingLegInformation2(__u8 * Dest, const struct asn1_parm *pc, const struct FacDivertingLegInformation2 *DivertingLegInformation2);
int ParseDivertingLegInformation2(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacDivertingLegInformation2 *DivertingLegInformation2);
int encodeFacDivertingLegInformation3(__u8 * Dest, const struct asn1_parm *pc, const struct FacDivertingLegInformation3 *DivertingLegInformation3);
int ParseDivertingLegInformation3(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacDivertingLegInformation3 *DivertingLegInformation3);
/* ------------------------------------------------------------------- */
#ifdef __cplusplus
}
#endif
#endif /* __ASN1_DIVERSION_H__ */
/* ------------------------------------------------------------------- *//* end asn1_diversion.h */

View File

@ -0,0 +1,74 @@
/* dss1.h
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
#ifndef DSS1_H
#define DSS1_H
/*
* Timer values
*/
#define T302 15000
#define T303 4000
#define T304 30000
#define T305 30000
#define T308 4000
/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */
/* This makes some tests easier and quicker */
/* default value is 90 seconds */
#define T309 90000
/* T310 can be between 30-120 Seconds. We use 120 Seconds so the user can hear
the inband messages */
#define T310 120000
#define T312 6000
#define T313 4000
#define T318 4000
#define T319 4000
#define N303 1
#define T_CTRL 180000
#define THOLD 4000
#define TRETRIEVE 4000
/* private TIMER events */
#define CC_T301 0x030101
#define CC_T302 0x030201
#define CC_T303 0x030301
#define CC_T304 0x030401
#define CC_T305 0x030501
#define CC_T308_1 0x030801
#define CC_T308_2 0x030802
#define CC_T309 0x030901
#define CC_T310 0x031001
#define CC_T312 0x031201
#define CC_T313 0x031301
#define CC_T318 0x031801
#define CC_T319 0x031901
#define CC_TCTRL 0x031f01
#define CC_THOLD 0x03a001
#define CC_TRETRIEVE 0x03a101
#define AUX_IDLE 0
#define AUX_HOLD_REQ 1
#define AUX_CALL_HELD 2
#define AUX_RETRIEVE_REQ 3
#define AUX_HOLD_IND 4
#define AUX_RETRIEVE_IND 5
#define VALID_HOLD_STATES_PTMP (SBIT(3) | SBIT(4) | SBIT(10))
#define VALID_HOLD_STATES_PTP (SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10))
#endif

View File

@ -0,0 +1,47 @@
/*
* $Id$
*
* Explicit Call Transfer (ECT) Supplementary Services ETS 300 369-1
*
* ECT Facility ie encode/decode header
*/
#ifndef __ASN1_ECT_H
#define __ASN1_ECT_H
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------------------------------------------------------- */
int encodeFacEctExecute(__u8 * Dest, const struct asn1_parm *pc, const void *val);
int encodeFacExplicitEctExecute(__u8 * Dest, const struct asn1_parm *pc, const struct FacExplicitEctExecute *ExplicitEctExecute);
int ParseExplicitEctExecute(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacExplicitEctExecute *ExplicitEctExecute);
int encodeFacRequestSubaddress(__u8 * Dest, const struct asn1_parm *pc, const void *val);
int encodeFacSubaddressTransfer(__u8 * Dest, const struct asn1_parm *pc, const struct FacSubaddressTransfer *SubaddressTransfer);
int ParseSubaddressTransfer(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacSubaddressTransfer *SubaddressTransfer);
int encodeFacEctLinkIdRequest(__u8 * Dest, const struct asn1_parm *pc, const struct FacEctLinkIdRequest_RES *EctLinkIdRequest);
int ParseEctLinkIdRequest_RES(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacEctLinkIdRequest_RES *EctLinkIdRequest);
int encodeFacEctInform(__u8 * Dest, const struct asn1_parm *pc, const struct FacEctInform *EctInform);
int ParseEctInform(struct asn1_parm *pc, u_char * p, u_char * end, struct FacEctInform *EctInform);
int encodeFacEctLoopTest(__u8 * Dest, const struct asn1_parm *pc, const struct FacEctLoopTest *EctLoopTest);
int ParseEctLoopTest(struct asn1_parm *pc, u_char * p, u_char * end, struct FacEctLoopTest *EctLoopTest);
int ParseEctLoopTest_RES(struct asn1_parm *pc, u_char * p, u_char * end, struct FacEctLoopTest_RES *EctLoopTest);
/* ------------------------------------------------------------------- */
#ifdef __cplusplus
}
#endif
#endif /* __ASN1_ECT_H */
/* ------------------------------------------------------------------- *//* end asn1_ect.h */

View File

@ -0,0 +1,63 @@
/* $Id: fsm.h,v 2.0 2007/06/29 14:35:31 kkeil Exp $
*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
/* Statemachine */
#include "mtimer.h"
struct FsmInst;
typedef void (* FSMFNPTR)(struct FsmInst *, int, void *);
struct Fsm {
FSMFNPTR *jumpmatrix;
int state_count, event_count;
const char **strEvent, **strState;
};
struct FsmInst {
struct Fsm *fsm;
struct timer_base *tb;
int state;
int debug;
void *userdata;
int userint;
void (*printdebug)(struct FsmInst *, const char *, ...);
};
struct FsmNode {
int state, event;
void (*routine) (struct FsmInst *, int, void *);
};
struct FsmTimer {
struct FsmInst *fi;
struct mtimer tl;
int event;
void *arg;
};
extern void FsmNew(struct Fsm *, struct FsmNode *, int);
extern void FsmFree(struct Fsm *);
extern int FsmEvent(struct FsmInst *, int , void *);
extern void FsmChangeState(struct FsmInst *, int);
extern void FsmInitTimer(struct FsmInst *, struct FsmTimer *);
extern int FsmAddTimer(struct FsmTimer *, int, int, void *, int);
extern void FsmRestartTimer(struct FsmTimer *, int, int, void *, int);
extern void FsmDelTimer(struct FsmTimer *, int);

View File

@ -0,0 +1,76 @@
/* helper.h
*
* some helper functions
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
#ifndef _HELPER_H
#define _HELPER_H
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/* Simple replacement for the NON-ATOMIC routines which asm/bitops.h
was providing. */
static inline int test_bit(int bit, unsigned long *word)
{
return !!((*word) & (1<<bit));
}
static inline int test_and_clear_bit(int bit, unsigned long *word)
{
int ret = !!((*word) & (1<<bit));
*word &= ~(1<<bit);
return ret;
}
static inline int test_and_set_bit(int bit, unsigned long *word)
{
int ret = !!((*word) & (1<<bit));
*word |= 1<<bit;
return ret;
}
static inline void clear_bit(int bit, unsigned long *word)
{
*word &= ~(1<<bit);
}
static inline void set_bit(int bit, unsigned long *word)
{
*word |= 1<<bit;
}
#endif

View File

@ -0,0 +1,178 @@
/* layer3.h
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
#ifndef LAYER3_H
#define LAYER3_H
#include <mISDN/mbuffer.h>
#include "helper.h"
#include "mlist.h"
#include "mtimer.h"
#include "fsm.h"
#include "debug.h"
typedef struct _l3_process l3_process_t;
struct _layer3;
struct l3protocol {
const char *name;
unsigned int protocol;
void (*init)(struct _layer3 *);
};
struct L3Timer {
struct mtimer tl;
struct _layer3 *l3;
unsigned int pid;
unsigned int nr;
};
struct l2l3if;
struct _l3_process {
struct list_head list;
struct _layer3 *L3;
struct l2l3if *l2if;
l3_process_t *master;
struct list_head child;
unsigned long flags;
int pid;
int selpid;
int state;
struct L3Timer timer1;
struct L3Timer timer2;
int n303;
struct l3_msg *t303msg;
unsigned char cid[4];
int cause;
int rm_cause;
int aux_state;
};
#define FLG_L3P_TIMER312 1
#define FLG_L3P_TIMER303_1 2
#define FLG_L3P_TIMER308_1 3
#define FLG_L3P_TIMER309 4
#define FLG_L3P_GOTRELCOMP 5
/*
* maximum orginated callrefs
* have to be a multiple of 8
*/
#define MAX_PID_COUNT 128
struct l2l3if {
struct list_head list;
struct _layer3 *l3;
struct sockaddr_mISDN l2addr;
struct FsmInst l3m;
struct FsmTimer l3m_timer;
struct mqueue squeue;
};
typedef struct _layer3 {
struct mlayer3 ml3;
pthread_t worker;
int l2sock;
void *l2inst;
int maxfd;
struct l2l3if l2master;
struct timer_base tbase;
int next_cr;
struct list_head plist;
l3_process_t global;
l3_process_t dummy;
int (*p_mgr)(l3_process_t *, u_int, struct l3_msg *);
int (*from_l2)(struct _layer3 *, struct mbuffer *);
int (*to_l3)(struct _layer3 *, struct l3_msg *);
int debug;
struct mqueue app_queue;
struct mqueue mgr_queue;
pthread_mutex_t run;
} layer3_t;
struct stateentry {
unsigned int state;
unsigned int primitive;
void (*rout) (l3_process_t *, unsigned int, struct l3_msg *);
};
extern l3_process_t *get_l3process4pid(layer3_t *, unsigned int);
extern l3_process_t *get_l3process4cref(layer3_t *, unsigned int);
extern l3_process_t *create_new_process(layer3_t *, unsigned int, unsigned int, l3_process_t *);
extern void release_l3_process(struct _l3_process *);
extern void SendMsg(struct _l3_process *, struct l3_msg *, int);
extern void mISDN_l3up(l3_process_t *, unsigned int, struct l3_msg *);
extern void mIl3_debug(layer3_t *, const char *, ...);
extern void mIpc_debug(u_int, struct _l3_process *, const char *, ...);
static inline void
newl3state(l3_process_t *pc, int state)
{
mIpc_debug(L3_DEB_STATE, pc, "state %d --> %d", pc->state, state);
pc->state = state;
}
static inline struct l3_msg *
MsgStart(l3_process_t *pc, unsigned char mt)
{
struct l3_msg *l3m;
struct mbuffer *mb;
l3m = alloc_l3_msg();
if (!l3m)
return NULL;
mb = container_of(l3m, struct mbuffer, l3);
l3m->type = mt;
mb->l3h.type = mt;
l3m->pid = pc->pid;
return(l3m);
}
/* L3 timer functions */
extern void StopAllL3Timer(struct _l3_process *);
extern void L3AddTimer(struct L3Timer *, int, unsigned int);
extern void L3DelTimer(struct L3Timer *);
extern void l3_manager(struct l2l3if *, unsigned int);
extern int assembleQ931(struct _l3_process *, struct l3_msg *);
#define SBIT(state) (1 << state)
#define ALL_STATES 0xffffffff
/*
* internal used property flags
* 0...15 reserved for set properties
*/
#define FLG_USER 16
#define FLG_NETWORK 17
#define FLG_BASICRATE 18
#define FLG_L2BLOCK 19
#define FLG_RUN_WAIT 30
#define FLG_ABORT 31
extern void init_l3(layer3_t *);
extern void release_l3(layer3_t *);
extern int l3_start(struct _layer3 *);
extern void l3_stop(struct _layer3 *);
extern int layer3_work(struct _layer3 *l3, double *misdn_timeout);
extern void mISDNl3New(void);
extern void mISDNl3Free(void);
#endif

View File

@ -0,0 +1,40 @@
/*af_isdn.h
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2010 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*
* simple implementation to allow different values for
* AF_ISDN/PF_ISDN
*
* Important notes:
*
* This file is C code, not only declarations, so a program should only
* include it in one code file, best in the file containing main() as well.
* Programs using libmisdn do not need that file, since libmisdn already have
* it.
*
*/
int __af_isdn = MISDN_AF_ISDN;
int set_af_isdn(int af)
{
if (af < 0)
return -1;
if (af >= AF_MAX)
return -1;
__af_isdn = af;
return 0;
}

View File

@ -0,0 +1,78 @@
/* $Id: l3dss1.h,v 2.0 2007/06/29 14:08:15 kkeil Exp $
*
* DSS1 (Euro) D-channel protocol defines
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
#ifndef _L3DSS1_H
#define _L3DSS1_H
#define T301 180000
#define T302 15000
#define T303 4000
#define T304 30000
#define T305 30000
#define T308 4000
/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */
/* This makes some tests easier and quicker */
#define T309 40000
#define T310 30000
#define T312 6000
#define T313 4000
#define T318 4000
#define T319 4000
#define N303 1
#define T_CTRL 180000
/* private TIMER events */
#define CC_TIMER 0x000001
#define CC_T301 0x030101
#define CC_T302 0x030201
#define CC_T303 0x030301
#define CC_T304 0x030401
#define CC_T305 0x030501
#define CC_T308 0x030801
#define CC_T309 0x030901
#define CC_T310 0x031001
#define CC_T312 0x031201
#define CC_T313 0x031301
#define CC_T318 0x031801
#define CC_T319 0x031901
#define CC_TCTRL 0x031f01
#define HOLDAUX_IDLE 0
#define HOLDAUX_HOLD_REQ 1
#define HOLDAUX_HOLD 2
#define HOLDAUX_RETR_REQ 3
#define HOLDAUX_HOLD_IND 4
#define HOLDAUX_RETR_IND 5
/* l3dss1 specific data in l3 process */
typedef struct
{ unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */
ulong ll_id; /* remebered ll id */
u_char remote_operation; /* handled remote operation, 0 = not active */
int proc; /* rememered procedure */
ulong remote_result; /* result of remote operation for statcallb */
char uus1_data[35]; /* data send during alerting or disconnect */
} dss1_proc_priv;
/* l3dss1 specific data in protocol stack */
typedef struct
{ unsigned char last_invoke_id; /* last used value for invoking */
unsigned char invoke_used[32]; /* 256 bits for 256 values */
} dss1_stk_priv;
#endif

View File

@ -0,0 +1,5 @@
// to be compatible...
#define MISDN_AF_ISDN 34

View File

@ -0,0 +1,421 @@
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
#ifndef mISDNIF_H
#define mISDNIF_H
#include <stdarg.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <mISDN/mISDNcompat.h>
#define MISDN_GIT_RELEASE 0x4000
/*
* ABI Version 32 bit
*
* <8 bit> Major version
* - changed if any interface become backwards incompatible
*
* <8 bit> Minor version
* - changed if any interface is extended but backwards compatible
*
* <14 bit> Release number
* - should be incremented on every checkin
*
* <bit 15> set in kernel version IOCTL is used to mark it as git.misdn.eu version
*
*/
#define MISDN_MAJOR_VERSION 1
#define MISDN_MINOR_VERSION 1
#define MISDN_RELEASE 31
/* primitives for information exchange
* generell format
* <16 bit 0 >
* <8 bit command>
* BIT 8 = 1 LAYER private
* BIT 7 = 1 answer
* BIT 6 = 1 DATA
* <8 bit target layer mask>
*
* Layer = 00 is reserved for general commands
Layer = 01 L2 -> HW
Layer = 02 HW -> L2
Layer = 04 L3 -> L2
Layer = 08 L2 -> L3
* Layer = FF is reserved for broadcast commands
*/
#define MISDN_CMDMASK 0xff00
#define MISDN_LAYERMASK 0x00ff
/* generell commands */
#define OPEN_CHANNEL 0x0100
#define CLOSE_CHANNEL 0x0200
#define CONTROL_CHANNEL 0x0300
#define CHECK_DATA 0x0400
/* layer 2 -> layer 1 */
#define PH_ACTIVATE_REQ 0x0101
#define PH_DEACTIVATE_REQ 0x0201
#define PH_DATA_REQ 0x2001
#define MPH_ACTIVATE_REQ 0x0501
#define MPH_DEACTIVATE_REQ 0x0601
#define MPH_INFORMATION_REQ 0x0701
#define PH_CONTROL_REQ 0x0801
/* layer 1 -> layer 2 */
#define PH_ACTIVATE_IND 0x0102
#define PH_ACTIVATE_CNF 0x4102
#define PH_DEACTIVATE_IND 0x0202
#define PH_DEACTIVATE_CNF 0x4202
#define PH_DATA_IND 0x2002
#define PH_DATA_E_IND 0x3002
#define MPH_ACTIVATE_IND 0x0502
#define MPH_DEACTIVATE_IND 0x0602
#define MPH_INFORMATION_IND 0x0702
#define PH_DATA_CNF 0x6002
#define PH_CONTROL_IND 0x0802
#define PH_CONTROL_CNF 0x4802
/* layer 3 -> layer 2 */
#define DL_ESTABLISH_REQ 0x1004
#define DL_RELEASE_REQ 0x1104
#define DL_DATA_REQ 0x3004
#define DL_UNITDATA_REQ 0x3104
#define DL_INFORMATION_REQ 0x0004
/* layer 2 -> layer 3 */
#define DL_ESTABLISH_IND 0x1008
#define DL_ESTABLISH_CNF 0x5008
#define DL_RELEASE_IND 0x1108
#define DL_RELEASE_CNF 0x5108
#define DL_DATA_IND 0x3008
#define DL_UNITDATA_IND 0x3108
#define DL_INFORMATION_IND 0x0008
/* intern layer 2 management */
#define MDL_ASSIGN_REQ 0x1804
#define MDL_ASSIGN_IND 0x1904
#define MDL_REMOVE_REQ 0x1A04
#define MDL_REMOVE_IND 0x1B04
#define MDL_STATUS_UP_IND 0x1C04
#define MDL_STATUS_DOWN_IND 0x1D04
#define MDL_STATUS_UI_IND 0x1E04
#define MDL_ERROR_IND 0x1F04
#define MDL_ERROR_RSP 0x5F04
/* intern layer 2 */
#define DL_TIMER200_IND 0x7004
#define DL_TIMER203_IND 0x7304
#define DL_INTERN_MSG 0x7804
/* DL_INFORMATION_IND types */
#define DL_INFO_L2_CONNECT 0x0001
#define DL_INFO_L2_REMOVED 0x0002
/* PH_CONTROL types */
/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */
#define DTMF_TONE_VAL 0x2000
#define DTMF_TONE_MASK 0x007F
#define DTMF_TONE_START 0x2100
#define DTMF_TONE_STOP 0x2200
#define DTMF_HFC_COEF 0x4000
#define DSP_CONF_JOIN 0x2403
#define DSP_CONF_SPLIT 0x2404
#define DSP_RECEIVE_OFF 0x2405
#define DSP_RECEIVE_ON 0x2406
#define DSP_ECHO_ON 0x2407
#define DSP_ECHO_OFF 0x2408
#define DSP_MIX_ON 0x2409
#define DSP_MIX_OFF 0x240a
#define DSP_DELAY 0x240b
#define DSP_JITTER 0x240c
#define DSP_TXDATA_ON 0x240d
#define DSP_TXDATA_OFF 0x240e
#define DSP_TX_DEJITTER 0x240f
#define DSP_TX_DEJ_OFF 0x2410
#define DSP_TONE_PATT_ON 0x2411
#define DSP_TONE_PATT_OFF 0x2412
#define DSP_VOL_CHANGE_TX 0x2413
#define DSP_VOL_CHANGE_RX 0x2414
#define DSP_BF_ENABLE_KEY 0x2415
#define DSP_BF_DISABLE 0x2416
#define DSP_BF_ACCEPT 0x2416
#define DSP_BF_REJECT 0x2417
#define DSP_PIPELINE_CFG 0x2418
#define HFC_VOL_CHANGE_TX 0x2601
#define HFC_VOL_CHANGE_RX 0x2602
#define HFC_SPL_LOOP_ON 0x2603
#define HFC_SPL_LOOP_OFF 0x2604
/* for T30 FAX and analog modem */
#define HW_MOD_FRM 0x4000
#define HW_MOD_FRH 0x4001
#define HW_MOD_FTM 0x4002
#define HW_MOD_FTH 0x4003
#define HW_MOD_FTS 0x4004
#define HW_MOD_CONNECT 0x4010
#define HW_MOD_OK 0x4011
#define HW_MOD_NOCARR 0x4012
#define HW_MOD_FCERROR 0x4013
#define HW_MOD_READY 0x4014
#define HW_MOD_LASTDATA 0x4015
/* DSP_TONE_PATT_ON parameter */
#define TONE_OFF 0x0000
#define TONE_GERMAN_DIALTONE 0x0001
#define TONE_GERMAN_OLDDIALTONE 0x0002
#define TONE_GERMAN_MORSEDIALTONE 0x0003
#define TONE_AMERICAN_DIALTONE 0x0004
#define TONE_GERMAN_DIALPBX 0x0005
#define TONE_GERMAN_OLDDIALPBX 0x0006
#define TONE_AMERICAN_DIALPBX 0x0007
#define TONE_GERMAN_RINGING 0x0008
#define TONE_GERMAN_OLDRINGING 0x0009
#define TONE_AMERICAN_RINGPBX 0x000b
#define TONE_GERMAN_RINGPBX 0x000c
#define TONE_GERMAN_OLDRINGPBX 0x000d
#define TONE_AMERICAN_RINGING 0x000e
#define TONE_GERMAN_BUSY 0x000f
#define TONE_GERMAN_OLDBUSY 0x0010
#define TONE_AMERICAN_BUSY 0x0011
#define TONE_GERMAN_HANGUP 0x0012
#define TONE_GERMAN_OLDHANGUP 0x0013
#define TONE_AMERICAN_HANGUP 0x0014
#define TONE_SPECIAL_INFO 0x0015
#define TONE_GERMAN_GASSENBESETZT 0x0016
#define TONE_GERMAN_AUFSCHALTTON 0x0017
/* MPH_INFORMATION_IND */
#define L1_SIGNAL_LOS_OFF 0x0010
#define L1_SIGNAL_LOS_ON 0x0011
#define L1_SIGNAL_AIS_OFF 0x0012
#define L1_SIGNAL_AIS_ON 0x0013
#define L1_SIGNAL_RDI_OFF 0x0014
#define L1_SIGNAL_RDI_ON 0x0015
#define L1_SIGNAL_SLIP_RX 0x0020
#define L1_SIGNAL_SLIP_TX 0x0021
/*
* protocol ids
* D channel 1-31
* B channel 33 - 63
*/
#define ISDN_P_NONE 0
#define ISDN_P_BASE 0
#define ISDN_P_TE_S0 0x01
#define ISDN_P_NT_S0 0x02
#define ISDN_P_TE_E1 0x03
#define ISDN_P_NT_E1 0x04
#define ISDN_P_TE_UP0 0x05
#define ISDN_P_NT_UP0 0x06
#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \
(p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE))
#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \
(p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT))
#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0))
#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1))
#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0))
#define ISDN_P_LAPD_TE 0x10
#define ISDN_P_LAPD_NT 0x11
#define ISDN_P_B_MASK 0x1f
#define ISDN_P_B_START 0x20
#define ISDN_P_B_RAW 0x21
#define ISDN_P_B_HDLC 0x22
#define ISDN_P_B_X75SLP 0x23
#define ISDN_P_B_L2DTMF 0x24
#define ISDN_P_B_L2DSP 0x25
#define ISDN_P_B_L2DSPHDLC 0x26
#define ISDN_P_B_T30_FAX 0x27
#define ISDN_P_B_MODEM_ASYNC 0x28
#define OPTION_L2_PMX 1
#define OPTION_L2_PTP 2
#define OPTION_L2_FIXEDTEI 3
#define OPTION_L2_CLEANUP 4
#define OPTION_L1_HOLD 5
/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
#define MISDN_MAX_IDLEN 20
struct mISDNhead {
unsigned int prim;
unsigned int id;
} __attribute__((packed));
#define MISDN_HEADER_LEN sizeof(struct mISDNhead)
#define MAX_DATA_SIZE 2048
#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN)
#define MAX_DFRAME_LEN 260
#define MISDN_ID_ADDR_MASK 0xFFFF
#define MISDN_ID_TEI_MASK 0xFF00
#define MISDN_ID_SAPI_MASK 0x00FF
#define MISDN_ID_TEI_ANY 0x7F00
#define MISDN_ID_ANY 0xFFFF
#define MISDN_ID_NONE 0xFFFE
#define GROUP_TEI 127
#define TEI_SAPI 63
#define CTRL_SAPI 0
#define MISDN_MAX_CHANNEL 127
#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3)
#define SOL_MISDN 0
struct sockaddr_mISDN {
sa_family_t family;
unsigned char dev;
unsigned char channel;
unsigned char sapi;
unsigned char tei;
};
struct mISDNversion {
unsigned char major;
unsigned char minor;
unsigned short release;
};
#define MAX_DEVICE_ID 63
struct mISDN_devinfo {
u_int id;
u_int Dprotocols;
u_int Bprotocols;
u_int protocol;
u_char channelmap[MISDN_CHMAP_SIZE];
u_int nrbchan;
char name[MISDN_MAX_IDLEN];
};
struct mISDN_devrename {
u_int id;
char name[MISDN_MAX_IDLEN]; /* new name */
};
/* MPH_INFORMATION_REQ payload */
struct ph_info_ch {
__u32 protocol;
__u64 Flags;
};
struct ph_info_dch {
struct ph_info_ch ch;
__u16 state;
__u16 num_bch;
};
struct ph_info {
struct ph_info_dch dch;
struct ph_info_ch bch[];
};
/* timer device ioctl */
#define IMADDTIMER _IOR('I', 64, int)
#define IMDELTIMER _IOR('I', 65, int)
/* socket ioctls */
#define IMGETVERSION _IOR('I', 66, int)
#define IMGETCOUNT _IOR('I', 67, int)
#define IMGETDEVINFO _IOR('I', 68, int)
#define IMCTRLREQ _IOR('I', 69, int)
#define IMCLEAR_L2 _IOR('I', 70, int)
#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename)
#define IMHOLD_L1 _IOR('I', 72, int)
static inline int
test_channelmap(u_int nr, u_char *map)
{
if (nr <= MISDN_MAX_CHANNEL)
return map[nr >> 3] & (1 << (nr & 7));
else
return 0;
}
static inline void
set_channelmap(u_int nr, u_char *map)
{
map[nr >> 3] |= (1 << (nr & 7));
}
static inline void
clear_channelmap(u_int nr, u_char *map)
{
map[nr >> 3] &= ~(1 << (nr & 7));
}
/* CONTROL_CHANNEL parameters */
#define MISDN_CTRL_GETOP 0x0000
#define MISDN_CTRL_LOOP 0x0001
#define MISDN_CTRL_CONNECT 0x0002
#define MISDN_CTRL_DISCONNECT 0x0004
#define MISDN_CTRL_RX_BUFFER 0x0008
#define MISDN_CTRL_PCMCONNECT 0x0010
#define MISDN_CTRL_PCMDISCONNECT 0x0020
#define MISDN_CTRL_SETPEER 0x0040
#define MISDN_CTRL_UNSETPEER 0x0080
#define MISDN_CTRL_RX_OFF 0x0100
#define MISDN_CTRL_FILL_EMPTY 0x0200
#define MISDN_CTRL_GETPEER 0x0400
#define MISDN_CTRL_L1_TIMER3 0x0800
#define MISDN_CTRL_HW_FEATURES_OP 0x2000
#define MISDN_CTRL_HW_FEATURES 0x2001
#define MISDN_CTRL_HFC_OP 0x4000
#define MISDN_CTRL_HFC_PCM_CONN 0x4001
#define MISDN_CTRL_HFC_PCM_DISC 0x4002
#define MISDN_CTRL_HFC_CONF_JOIN 0x4003
#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004
#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005
#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006
#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007
#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008
#define MISDN_CTRL_HFC_WD_INIT 0x4009
#define MISDN_CTRL_HFC_WD_RESET 0x400A
/* special RX buffer value for MISDN_CTRL_RX_BUFFER request.p1 is the minimum
* buffer size request.p2 the maximum. Using MISDN_CTRL_RX_SIZE_IGNORE will
* not change the value, but still read back the actual stetting.
*/
#define MISDN_CTRL_RX_SIZE_IGNORE -1
/* socket options */
#define MISDN_TIME_STAMP 0x0001
struct mISDN_ctrl_req {
int op;
int channel;
int p1;
int p2;
int unused; /* temporary support for older git.misdn.eu drivers */
};
/* muxer options */
#define MISDN_OPT_ALL 1
#define MISDN_OPT_TEIMGR 2
#endif /* mISDNIF_H */

View File

@ -0,0 +1,259 @@
/* mbuffer.h
*
* Author Karsten Keil <kkeil@linux-pingi.de>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
* Copyright 2011 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.
*
*/
#ifndef _MBUFFER_H
#define _MBUFFER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/socket.h>
#include <pthread.h>
#include <semaphore.h>
#include <mISDN/mISDNif.h>
#include <mISDN/mlayer3.h>
#ifndef eprint
#define eprint(fmt, ...) mi_printf(__FILE__, __LINE__, __PRETTY_FUNCTION__, 1, fmt, ##__VA_ARGS__)
extern int mi_printf(const char *file, int line, const char *func, int lev, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
#endif
struct mqueue {
struct mbuffer *prev;
struct mbuffer *next;
pthread_mutex_t lock;
int len;
};
#ifdef MEMLEAK_DEBUG
struct lhead {
struct lhead *prev;
struct lhead *next;
};
#endif
struct mbuffer {
struct mbuffer *prev;
struct mbuffer *next;
struct mqueue *list;
int refcnt;
struct mISDNhead *h;
struct sockaddr_mISDN addr;
unsigned char *head;
unsigned char *data;
unsigned char *tail;
unsigned char *end;
unsigned char *chead;
unsigned char *ctail;
unsigned char *cend;
int len;
struct l3_head l3h;
struct l3_msg l3;
#ifdef MEMLEAK_DEBUG
struct lhead Alist;
char d_fn[80];
int d_ln;
#endif
} __attribute__((__may_alias__));
#define MBUFFER_DATA_SIZE 280
/*
* init mbuffer caching
* @parameter count of cached mbuffers
*/
extern void init_mbuffer(int);
/*
* free the cache
*/
extern void cleanup_mbuffer(void);
#ifdef MEMLEAK_DEBUG
/*
* alloc a new mbuffer
*/
extern struct mbuffer *__alloc_mbuffer(const char *file, int lineno, const char *func);
/*
* free the message
*/
extern void __free_mbuffer(struct mbuffer *, const char *file, int lineno, const char *func);
#define alloc_mbuffer() __alloc_mbuffer(__FILE__, __LINE__, __PRETTY_FUNCTION__)
#define free_mbuffer(p) __free_mbuffer(p, __FILE__, __LINE__, __PRETTY_FUNCTION__)
#else
/*
* alloc a new mbuffer
*/
extern struct mbuffer *alloc_mbuffer(void);
/*
* free the message
*/
extern void free_mbuffer(struct mbuffer *);
#endif
static inline void
mqueue_init(struct mqueue *q)
{
pthread_mutex_init(&q->lock, NULL);
q->len = 0;
q->prev = (struct mbuffer *)q;
q->next = (struct mbuffer *)q;
}
static inline int mqueue_len(struct mqueue *q)
{
return(q->len);
}
static inline void mqueue_head(struct mqueue *q, struct mbuffer *newm)
{
pthread_mutex_lock(&q->lock);
newm->list = q;
q->len++;
q->next->prev = newm;
newm->next = q->next;
newm->prev = (struct mbuffer *)q;
q->next = newm;
pthread_mutex_unlock(&q->lock);
}
static inline void mqueue_tail(struct mqueue *q, struct mbuffer *newm)
{
pthread_mutex_lock(&q->lock);
newm->list = q;
q->len++;
q->prev->next = newm;
newm->next = (struct mbuffer *)q;;
newm->prev = q->prev;
q->prev = newm;
pthread_mutex_unlock(&q->lock);
}
static inline struct mbuffer *mdequeue(struct mqueue *q)
{
struct mbuffer *next, *prev, *result;
pthread_mutex_lock(&q->lock);
prev = (struct mbuffer *)q;
next = prev->next;
result = NULL;
if (next != prev) {
result = next;
next = next->next;
q->len--;
next->prev = prev;
prev->next = next;
result->list = NULL;
}
pthread_mutex_unlock(&q->lock);
return result;
}
static __inline__ void mqueue_purge(struct mqueue *q)
{
struct mbuffer *mb;
while ((mb = mdequeue(q))!=NULL)
free_mbuffer(mb);
}
static __inline__ unsigned char *msg_put(struct mbuffer *msg, unsigned int len)
{
unsigned char *tmp = msg->tail;
msg->tail += len;
msg->len += len;
if (msg->tail > msg->end) {
eprint("msg_over_panic msg(%p) data(%p) head(%p)\n",
msg, msg->data, msg->head);
return NULL;
}
return tmp;
}
static __inline__ unsigned char *msg_push(struct mbuffer *msg, unsigned int len)
{
msg->data -= len;
msg->len += len;
if(msg->data < msg->head)
{
eprint("msg_under_panic msg(%p) data(%p) head(%p)\n",
msg, msg->data, msg->head);
return NULL;
}
return msg->data;
}
static __inline__ unsigned char *__msg_pull(struct mbuffer *msg, unsigned int len)
{
unsigned char *tmp = msg->data;
msg->len -= len;
msg->data += len;
return tmp;
}
static __inline__ unsigned char * msg_pull(struct mbuffer *msg, unsigned int len)
{
if (len > (unsigned int)msg->len)
return NULL;
return (unsigned char *)__msg_pull(msg,len);
}
static __inline__ int msg_headroom(struct mbuffer *msg)
{
return msg->data - msg->head;
}
static __inline__ int msg_tailroom(struct mbuffer *msg)
{
return msg->end-msg->tail;
}
static __inline__ void msg_reserve(struct mbuffer *msg, unsigned int len)
{
msg->data += len;
msg->tail += len;
}
static __inline__ void __msg_trim(struct mbuffer *msg, unsigned int len)
{
msg->len = len;
msg->tail = msg->data + len;
}
static __inline__ void msg_trim(struct mbuffer *msg, unsigned int len)
{
if ((unsigned int)msg->len > len) {
__msg_trim(msg, len);
}
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,260 @@
/* mlayer3.h
*
* Author Karsten Keil <kkeil@linux-pingi.de>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
* Copyright 2011 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.
*
*/
#ifndef _MLAYER3_H
#define _MLAYER3_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#include <pthread.h>
#include <mISDN/mISDNcompat.h>
struct l3_head {
unsigned char type;
unsigned char crlen;
unsigned short cr;
};
struct m_extie {
unsigned char ie;
unsigned char codeset;
unsigned char len;
unsigned char *val;
};
struct l3_msg {
unsigned int type;
unsigned int pid;
unsigned char *bearer_capability; /* ie 0x04 pos 0 */
unsigned char *cause; /* ie 0x08 pos 1 */
unsigned char *call_id; /* ie 0x10 pos 2 */
unsigned char *call_state; /* ie 0x14 pos 3 */
unsigned char *channel_id; /* ie 0x18 pos 4 */
unsigned char *facility; /* ie 0x1c pos 5 */
unsigned char *progress; /* ie 0x1e pos 6 */
unsigned char *net_fac; /* ie 0x20 pos 7 */
unsigned char *notify; /* ie 0x27 pos 8 */
unsigned char *display; /* ie 0x28 pos 9 */
unsigned char *date; /* ie 0x29 pos 10 */
unsigned char *keypad; /* ie 0x2c pos 11 */
unsigned char *signal; /* ie 0x34 pos 12 */
unsigned char *info_rate; /* ie 0x40 pos 13 */
unsigned char *end2end_transit; /* ie 0x42 pos 14 */
unsigned char *transit_delay_sel; /* ie 0x43 pos 15 */
unsigned char *pktl_bin_para; /* ie 0x44 pos 16 */
unsigned char *pktl_window; /* ie 0x45 pos 17 */
unsigned char *pkt_size; /* ie 0x46 pos 18 */
unsigned char *closed_userg; /* ie 0x47 pos 19 */
unsigned char *reverse_charge; /* ie 0x4a pos 20 */
unsigned char *connected_nr; /* ie 0x4c pos 21 */
unsigned char *connected_sub; /* ie 0x4d pos 22 */
unsigned char *calling_nr; /* ie 0x6c pos 23 */
unsigned char *calling_sub; /* ie 0x6d pos 24 */
unsigned char *called_nr; /* ie 0x70 pos 25 */
unsigned char *called_sub; /* ie 0x71 pos 26 */
unsigned char *redirecting_nr; /* ie 0x74 pos 27 */
unsigned char *redirection_nr; /* ie 0x76 pos 28 */
unsigned char *transit_net_sel; /* ie 0x78 pos 29 */
unsigned char *restart_ind; /* ie 0x79 pos 30 */
unsigned char *llc; /* ie 0x7c pos 31 */
unsigned char *hlc; /* ie 0x7d pos 32 */
unsigned char *useruser; /* ie 0x7e pos 33 */
unsigned char comprehension_req;
unsigned char more_data;
unsigned char sending_complete;
unsigned char congestion_level;
struct m_extie extra[8];
};
struct mlayer3;
/*
* callback function to send and receive messages from and to layer3
* @parameter1 struct mlayer3 - identfy the layer3
* @parameter2 message type MT_ constants (Q931 and some private)
* @parameter3 PID (process identification) value to identify the target process
* @parameter4 optional layer3 message, if here are no special IE to deliver, use NULL
*/
typedef int (mlayer3_cb_t)(struct mlayer3 *, unsigned int, unsigned int, struct l3_msg *);
/* debug helper */
#define MISDN_LIBDEBUG_ERROR 1
#define MISDN_LIBDEBUG_WARN 2
#define MISDN_LIBDEBUG_INFO 3
#define MISDN_LIBDEBUG_DEBUG 4
typedef int (*mi_thread_create_t)(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine) (void *),
void *arg, const char *file, const char *caller, int line, const char *start_fn);
typedef int (*mi_debug_t)(const char *file, int line, const char *func, int level, const char *fmt, va_list va);
typedef void * (*mi_malloc_t)(size_t size, const char *file, int line, const char *func);
typedef void * (*mi_calloc_t)(size_t nmemb, size_t size, const char *file, int line, const char *func);
typedef void (*mi_free_t)(void *ptr, const char *file, int line, const char *func);
struct mi_ext_fn_s {
mi_thread_create_t thread_create;
mi_debug_t prt_debug;
mi_malloc_t malloc;
mi_calloc_t calloc;
mi_free_t reuse;
mi_free_t free;
};
extern struct mi_ext_fn_s *mi_extern_func;
/*
* To avoid to include always all headers needed for mISDNif.h we redefine MISDN_CHMAP_SIZE here
* Please make sure to keep it in sync with mISDNif.h (but changes are very unlikely)
*/
#ifndef MISDN_CHMAP_SIZE
#define MISDN_MAX_CHANNEL 127
#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3)
#endif
struct mISDN_devinfo;
struct mlayer3 {
unsigned int device;
unsigned int nr_bchannel;
unsigned long options;
mlayer3_cb_t *to_layer3;
mlayer3_cb_t *from_layer3;
void *priv; /* free use for applications */
struct mISDN_devinfo *devinfo;
};
/*
* Layer3 protocols
*/
#define L3_PROTOCOL_DSS1_USER 0x101
#define L3_PROTOCOL_DSS1_NET 0x102
/*
* Layer3 property Flags
*
* 16...31 reserved for internal use
*
*/
#define MISDN_FLG_PTP 1
#define MISDN_FLG_NET_HOLD 2
#define MISDN_FLG_L2_HOLD 3
#define MISDN_FLG_L2_CLEAN 4
#define MISDN_FLG_L1_HOLD 5
/*
* Layer3 <----> Application additional message types
* Basic messages are coded like Q931 MT_ from q931.h
*/
/* Application <---> L3 */
#define MT_ASSIGN 0x11000
/* L3 ---> Application */
#define MT_FREE 0x11001
#define MT_L2ESTABLISH 0x12000
#define MT_L2RELEASE 0x12001
#define MT_L2IDLE 0x12002
#define MT_ERROR 0x18000
#define MT_TIMEOUT 0x18001
/*
* process IDs
*
*
*/
#define MISDN_PID_DUMMY 0x81000000
#define MISDN_PID_GLOBAL 0x82000000
#define MISDN_PID_NONE 0xFFFFFFFF
#define MISDN_PID_MASTER 0xFF000000
#define MISDN_PID_CRTYPE_MASK 0xFF000000
#define MISDN_PID_CID_MASK 0x00FF0000
#define MISDN_PID_CR_MASK 0xFF00FFFF
#define MISDN_PID_CRVAL_MASK 0x0000FFFF
#define MISDN_PID_CR_FLAG 0x00008000
#define MISDN_CES_MASTER 0x0000FF00
#define MISDN_LIB_VERSION 2
#define MISDN_LIB_RELEASE 5
#define MISDN_LIB_INTERFACE ((MISDN_LIB_VERSION << 16) | MISDN_LIB_RELEASE)
/*
* init layer3 statemachines and caches
* must be called before first open
* @parameter count of cached mbuffers
* @parameter optional block of external functions for debug
* @parameter use linke user space mISDN library instead of kernel socket
* @return: interface version
*/
extern unsigned int init_layer3(int, struct mi_ext_fn_s *, int user_space_misdn);
/*
* cleanup layer3 statemachines and chaches
* must be called after all layer3 are closed
*/
extern void cleanup_layer3(void);
/*
* 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
*/
extern struct mlayer3 *open_layer3(unsigned int, unsigned int, unsigned int, mlayer3_cb_t *, void *);
/*
* close a layer3 stack
* parameter1 - stack struct
*/
extern void close_layer3(struct mlayer3 *);
/*
* run stack for one loop
* parameter1 - stack struct
*/
extern int work_layer3(struct mlayer3 *, double *);
extern unsigned int request_new_pid(struct mlayer3 *);
extern int mISDN_get_pcm_slots(struct mlayer3 *, int, int *, int *);
extern int mISDN_set_pcm_slots(struct mlayer3 *, int, int, int);
extern int add_layer3_ie(struct l3_msg *, unsigned char, int, unsigned char *);
extern void l3_msg_increment_refcnt(struct l3_msg *);
#ifdef MEMLEAK_DEBUG
extern struct l3_msg *__alloc_l3_msg(const char *file, int lineno, const char *func);
extern void __free_l3_msg(struct l3_msg *, const char *file, int lineno, const char *func);
#define alloc_l3_msg() __alloc_l3_msg(__FILE__, __LINE__, __PRETTY_FUNCTION__)
#define free_l3_msg(p) __free_l3_msg(p, __FILE__, __LINE__, __PRETTY_FUNCTION__)
#else
extern struct l3_msg *alloc_l3_msg(void);
extern void free_l3_msg(struct l3_msg *);
#endif
extern void mISDN_set_debug_level(unsigned int);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,363 @@
/* q931.h
*
* Basic Q931 defines
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
#ifndef _Q931_H
#define _Q931_H
#include <time.h>
/*
* Q931 protocol discriminator
*/
#define Q931_PD 0x08
/*
* Q931 Message-Types
*/
#define MT_ALERTING 0x01
#define MT_CALL_PROCEEDING 0x02
#define MT_CONNECT 0x07
#define MT_CONNECT_ACKNOWLEDGE 0x0f
#define MT_PROGRESS 0x03
#define MT_SETUP 0x05
#define MT_SETUP_ACKNOWLEDGE 0x0d
#define MT_RESUME 0x26
#define MT_RESUME_ACKNOWLEDGE 0x2e
#define MT_RESUME_REJECT 0x22
#define MT_SUSPEND 0x25
#define MT_SUSPEND_ACKNOWLEDGE 0x2d
#define MT_SUSPEND_REJECT 0x21
#define MT_USER_INFORMATION 0x20
#define MT_DISCONNECT 0x45
#define MT_RELEASE 0x4d
#define MT_RELEASE_COMPLETE 0x5a
#define MT_RESTART 0x46
#define MT_RESTART_ACKNOWLEDGE 0x4e
#define MT_SEGMENT 0x60
#define MT_CONGESTION_CONTROL 0x79
#define MT_INFORMATION 0x7b
#define MT_FACILITY 0x62
#define MT_NOTIFY 0x6e
#define MT_STATUS 0x7d
#define MT_STATUS_ENQUIRY 0x75
#define MT_HOLD 0x24
#define MT_HOLD_ACKNOWLEDGE 0x28
#define MT_HOLD_REJECT 0x30
#define MT_RETRIEVE 0x31
#define MT_RETRIEVE_ACKNOWLEDGE 0x33
#define MT_RETRIEVE_REJECT 0x37
#define MT_REGISTER 0x64
/*
* Info Elements
*/
// not implemented
// #define IE_SEGMENT 0x00
#define IE_BEARER 0x04
#define IE_CAUSE 0x08
#define IE_CALL_ID 0x10
#define IE_CALL_STATE 0x14
#define IE_CHANNEL_ID 0x18
#define IE_FACILITY 0x1c
#define IE_PROGRESS 0x1e
#define IE_NET_FAC 0x20
#define IE_NOTIFY 0x27
#define IE_DISPLAY 0x28
#define IE_DATE 0x29
#define IE_KEYPAD 0x2c
#define IE_SIGNAL 0x34
#define IE_INFORATE 0x40
#define IE_E2E_TDELAY 0x42
#define IE_TDELAY_SEL 0x43
#define IE_PACK_BINPARA 0x44
#define IE_PACK_WINSIZE 0x45
#define IE_PACK_SIZE 0x46
#define IE_CUG 0x47
#define IE_REV_CHARGE 0x4a
#define IE_CONNECT_PN 0x4c
#define IE_CONNECT_SUB 0x4d
#define IE_CALLING_PN 0x6c
#define IE_CALLING_SUB 0x6d
#define IE_CALLED_PN 0x70
#define IE_CALLED_SUB 0x71
#define IE_REDIRECTING_NR 0x74
#define IE_REDIRECTION_NR 0x76
#define IE_TRANS_SEL 0x78
#define IE_RESTART_IND 0x79
#define IE_LLC 0x7c
#define IE_HLC 0x7d
#define IE_USER_USER 0x7e
#define IE_COUNT 34
// not implemented
#define IE_ESCAPE 0x7f
// one octet IE
#define IE_SHIFT 0x90
#define IE_MORE_DATA 0xa0
#define IE_COMPLETE 0xa1
#define IE_CONGESTION 0xb0
// not allowed for ETSI
#define IE_REPEAT 0xd0
/*
* weight for IE in check lists
*/
#define IE_MANDATORY 0x0100
/* mandatory not in every case */
#define IE_MANDATORY_1 0x0200
/*
* Cause location
*/
#define CAUSE_LOC_USER 0
#define CAUSE_LOC_PRVN_LOCUSER 1
#define CAUSE_LOC_PUBN_LOCUSER 2
#define CAUSE_LOC_TRANSIT 3
#define CAUSE_LOC_PUBN_RMTUSER 4
#define CAUSE_LOC_PRVN_RMTUSER 5
/*
* Cause values
*/
#define CAUSE_UNASSIGNED_NUMBER 1
#define CAUSE_NO_TRANSIT_ROUTE 2
#define CAUSE_NO_ROUTE 3
#define CAUSE_CHANNEL_UNACCEPT 6
#define CAUSE_NORMAL_CLEARING 16
#define CAUSE_USER_BUSY 17
#define CAUSE_NOUSER_RESPONDING 18
#define CAUSE_ALERTED_NO_ANSWER 19
#define CAUSE_CALL_REJECTED 21
#define CAUSE_NONSELECTED_USER 26
#define CAUSE_DEST_OUT_OF_ORDER 27
#define CAUSE_INVALID_NUMBER 28
#define CAUSE_FACILITY_REJECTED 29
#define CAUSE_STATUS_RESPONSE 30
#define CAUSE_NORMALUNSPECIFIED 31
#define CAUSE_NO_CHANNEL 34
#define CAUSE_NET_OUT_OF_ORDER 28
#define CAUSE_TEMPORARY_FAILURE 41
#define CAUSE_SEQ_CONGESTION 42
#define CAUSE_REQUESTED_CHANNEL 44
#define CAUSE_RESOURCES_UNAVAIL 47
#define CAUSE_FACILITY_NOTSUBSCRIBED 50
#define CAUSE_FACILITY_NOTIMPLEMENTED 69
#define CAUSE_INVALID_CALLREF 81
#define CAUSE_INCOMPATIBLE_DEST 88
#define CAUSE_MANDATORY_IE_MISS 96
#define CAUSE_MT_NOTIMPLEMENTED 97
#define CAUSE_NOTCOMPAT_STATE_OR_MT_NOTIMPLEMENTED 98
#define CAUSE_IE_NOTIMPLEMENTED 99
#define CAUSE_INVALID_CONTENTS 100
#define CAUSE_NOTCOMPAT_STATE 101
#define CAUSE_TIMER_EXPIRED 102
#define CAUSE_PROTOCOL_ERROR 111
#define NO_CAUSE 254
/*
* Restart indication class values
*/
#define RESTART_CLASS_CHANNEL 0
#define RESTART_CLASS_SINGLE 6
#define RESTART_CLASS_ALL 7
/*
* Parser error codes
*/
#define Q931_ERROR_LEN 0x010000
#define Q931_ERROR_OVERFLOW 0x020000
#define Q931_ERROR_CREF 0x040000
#define Q931_ERROR_FATAL 0x0F0000
#define Q931_ERROR_IELEN 0x100000
#define Q931_ERROR_UNKNOWN 0x200000
#define Q931_ERROR_COMPREH 0x400000
#define Q931_ERROR_IESEQ 0x800000 /* do not care in our implementation */
/* Bearer capability */
#define Q931_CAP_SPEECH 0x00
#define Q931_CAP_UNRES_DIGITAL 0x08
#define Q931_CAP_RES_DIGITAL 0x09
#define Q931_CAP_3KHZ_AUDIO 0x10
#define Q931_CAP_7KHZ_AUDIO 0x11
#define Q931_CAP_VIDEO 0x18
/* Bearer L1 user info */
#define Q931_L1INFO_V110 0x01
#define Q931_L1INFO_ULAW 0x02
#define Q931_L1INFO_ALAW 0x03
#define Q931_L1INFO_G721 0x04
#define Q931_L1INFO_G722_5 0x05
#define Q931_L1INFO_G7XX_VIDEO 0x06
#define Q931_L1INFO_NONE_CCITT 0x07
#define Q931_L1INFO_V120 0x08
#define Q931_L1INFO_X31 0x09
struct misdn_channel_info {
unsigned char nr; /* channel number/slot or special */
unsigned char flags; /* exclusiv, not Basic, ANY, NONE */
unsigned char type; /* B-channel, D-channel, H0, H11, H12 */
unsigned char ctrl; /* Allocated, updated, needsend, sent */
} __attribute__((packed));
/*
* special channel number defines
*/
#define MI_CHAN_ANY 0xff
#define MI_CHAN_NONE 0xfe
#define MI_CHAN_DCHANNEL 0xfd
#define MI_CHAN_FLG_NONE 0x01
#define MI_CHAN_FLG_ANY 0x02
#define MI_CHAN_FLG_DCHANNEL 0x04
#define MI_CHAN_FLG_EXCLUSIVE 0x08
#define MI_CHAN_FLG_OTHER_IF 0x20
#define MI_CHAN_TYP_NONE 0
#define MI_CHAN_TYP_B 1
#define MI_CHAN_TYP_D 2
#define MI_CHAN_TYP_H0 3
#define MI_CHAN_TYP_H11 4
#define MI_CHAN_TYP_H12 5
#define MI_CHAN_CTRL_UPDATED 0x01
#define MI_CHAN_CTRL_NEEDSEND 0x02
#define MI_CHAN_CTRL_SENT 0x04
#define MI_CHAN_CTRL_ALLOCATED 0x10
#define MI_CHAN_CTRL_DOWN 0x20
/* progress info */
struct misdn_progress_info {
unsigned char loc; /* location, octet 3 */
unsigned char desc; /* description, octet 3 */
unsigned char resv; /* reserved */
unsigned char ctrl; /* ctrl info flags */
} __attribute__((packed));
/*
* Q931 location
*/
#define Q931_LOC_USER 0
#define Q931_LOC_PRVN_LOCUSER 1
#define Q931_LOC_PUBN_LOCUSER 2
#define Q931_LOC_PUBN_RMTUSER 4
#define Q931_LOC_PRVN_RMTUSER 5
#define Q931_LOC_INTERNATIONAL 7
/*
* Progress values
*/
#define PROGRESS_NOT_E2E_ISDN 1
#define PROGRESS_DEST_NOT_ISDN 2
#define PROGRESS_ORIG_NOT_ISDN 3
#define PROGRESS_RET_TO_ISDN 4
#define PROGRESS_INBAND 8
/* Progress control flags */
#define MI_PROG_CTRL_UPDATED 0x01
#define MI_PROG_CTRL_NEEDSEND 0x02
#define MI_PROG_CTRL_SENT 0x04
/* Reason for diversion */
#define MI_DIV_REASON_UNKNOWN 0x00
#define MI_DIV_REASON_CFB 0x01
#define MI_DIV_REASON_CFNR 0x02
#define MI_DIV_REASON_CD 0x0a
#define MI_DIV_REASON_CFU 0x0f
/* Number qualifiers */
#define Q931_NTYPE_UNKNOWN 0
#define Q931_NTYPE_INTERNATIONAL 1
#define Q931_NTYPE_NATIONAL 2
#define Q931_NTYPE_NETWORKSPECIFIC 3
#define Q931_NTYPE_SUBSCRIBER 4
#define Q931_NTYPE_ABBREVIATED 6
#define Q931_NTYPE_RESERVED 7
#define Q931_NPLAN_UNKNOWN 0x0
#define Q931_NPLAN_ISDN 0x1
#define Q931_NPLAN_DATA 0x3
#define Q931_NPLAN_TELEX 0x4
#define Q931_NPLAN_NATIONAL 0x8
#define Q931_NPLAN_PRIVATE 0x9
#define Q931_NPLAN_RESERVED 0xf
#define Q931_NPRESENTATION_ALLOWED 0
#define Q931_NPRESENTATION_RESTRICTED 1
#define Q931_NPRESENTATION_NOTAVAILABLE 2
#define Q931_NPRESENTATION_RESERVED 3
#define Q931_NSCREEN_USER_NOT 0
#define Q931_NSCREEN_USER_PASSED 1
#define Q931_NSCREEN_USER_FAILED 2
#define Q931_NSCREEN_NETWORK 3
/* Common IE encode helpers */
struct l3_msg;
struct asn1_parm;
extern int l3_ie2pos(unsigned char);
extern int mi_encode_bearer(struct l3_msg *, unsigned int, unsigned int, unsigned int, unsigned int);
extern int mi_encode_hlc(struct l3_msg *, int, int);
extern int mi_encode_channel_id(struct l3_msg *, struct misdn_channel_info *);
extern int mi_encode_calling_nr(struct l3_msg *, char *, int, unsigned int, unsigned int, unsigned int);
extern int mi_encode_connected_nr(struct l3_msg *, char *, int, unsigned int, unsigned int, unsigned int);
extern int mi_encode_called_nr(struct l3_msg *, char *, unsigned int, unsigned int);
extern int mi_encode_redirecting_nr(struct l3_msg *, char *, int, unsigned int, unsigned int, int);
extern int mi_encode_redirection_nr(struct l3_msg *, char *, int, unsigned int, unsigned int);
extern int mi_encode_useruser(struct l3_msg *, int, int, char *);
extern int mi_encode_cause(struct l3_msg *l, int cause, int, int, unsigned char *);
extern int mi_encode_progress(struct l3_msg *, struct misdn_progress_info *);
extern int mi_encode_date(struct l3_msg *, struct tm *);
extern int mi_encode_restart_ind(struct l3_msg *, unsigned char);
extern int mi_encode_facility(struct l3_msg *, struct asn1_parm *);
extern int mi_encode_notification_ind(struct l3_msg *, int);
/* Common IE decode helpers */
struct mbuffer;
extern int parseQ931(struct mbuffer *);
extern int l3_ie2pos(u_char);
extern unsigned char l3_pos2ie(int);
extern int mi_decode_progress(struct l3_msg *, struct misdn_progress_info *);
extern int mi_decode_bearer_capability(struct l3_msg *, int *, int *, int *, int *, int *,
int *, int *, int *, int *, int *, int *, int *, int *);
extern int mi_decode_hlc(struct l3_msg *l3m, int *, int *);
extern int mi_decode_cause(struct l3_msg *, int *, int *, int *, int *, int *, unsigned char *);
extern int mi_decode_channel_id(struct l3_msg *, struct misdn_channel_info *);
extern int mi_decode_calling_nr(struct l3_msg *, int *, int *, int *, int *, char *);
extern int mi_decode_connected_nr(struct l3_msg *, int *, int *, int *, int *, char *);
extern int mi_decode_called_nr(struct l3_msg *, int *, int *, char *);
extern int mi_decode_redirecting_nr(struct l3_msg *, int *, int *, int *, int *, int *, char *);
extern int mi_decode_redirection_nr(struct l3_msg *, int *, int *, int *, char *);
extern int mi_decode_display(struct l3_msg *, char *, int);
extern int mi_decode_useruser(struct l3_msg *, int *, int *, char *, int);
extern int mi_decode_date(struct l3_msg *, struct tm *);
extern int mi_decode_restart_ind(struct l3_msg *, unsigned char *);
extern int mi_decode_facility(struct l3_msg *, struct asn1_parm *);
extern int mi_decode_notification_ind(struct l3_msg *, int*);
/* some print helpers */
extern const char *mi_bearer2str(int);
extern const char *mi_msg_type2str(unsigned int);
extern const char *_mi_msg_type2str(unsigned int);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,119 @@
/* mlist.h
*
* Simple doubly linked list implementation and helper
* function reduce version of linux kernel list.h
*/
#ifndef _MLIST_H
#define _MLIST_H
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* list_add_head - add a new entry on the top
* @new: new entry to be added
* @head: list head to add it after
*
*/
static inline void list_add_head(struct list_head *new, struct list_head *head)
{
head->next->prev = new;
new->next = head->next;
new->prev = head;
head->next = new;
}
/*
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
head->prev->next = new;
new->next = head;
new->prev = head->prev;
head->prev = new;
}
#define LIST_POISON1 0xdeadbee1
#define LIST_POISON2 0xdeadbee2
/*
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
*/
static inline void list_del(struct list_head *entry)
{
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
entry->next = (void *)LIST_POISON1;
entry->prev = (void *)LIST_POISON2;
}
/*
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/*
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/*
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)
/*
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
#endif

View File

@ -0,0 +1,46 @@
/* mtimer.h
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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.
*
*/
#ifndef MTIMER_H
#define MTIMER_H
#include "mlist.h"
typedef void (mtimer_func_t)(void *);
struct timer_base {
struct list_head pending_timer;
int tdev;
};
struct mtimer {
struct list_head list;
struct timer_base *tb;
int id;
double expires;
int timeout;
void *data;
mtimer_func_t *function;
};
extern int init_timer(struct mtimer *, struct timer_base *, void *, mtimer_func_t *);
extern int add_timer(struct mtimer *, int);
extern int del_timer(struct mtimer *);
extern int timer_pending(struct mtimer *);
extern int expire_timer(struct timer_base *, int, double *);
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

386
src/libmisdnuser/layer3/mlayer3.c Executable file
View File

@ -0,0 +1,386 @@
/* 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 = NULL;
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 create 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, double *misdn_timeout)
{
struct _layer3 *l3;
l3 = container_of(ml3, struct _layer3, ml3);
return layer3_work(l3, misdn_timeout);
}
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
/* debug.c
*
* mISDN lib debug functions
*
* Author Karsten Keil <kkeil@linux-pingi.de>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
* Copyright 2009, 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 <stdarg.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "debug.h"
#include <mISDN/mlayer3.h>
unsigned int mI_debug_mask;
#ifdef MEMLEAK_DEBUG
#undef malloc
#undef calloc
#undef free
void *
__mi_alloc(size_t size, const char *file, int lineno, const char *func)
{
void *p;
if (mi_extern_func && mi_extern_func->malloc)
p = mi_extern_func->malloc(size, file, lineno, func);
else
p = malloc(size);
return p;
}
void *
__mi_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
{
void *p;
if (mi_extern_func && mi_extern_func->calloc)
p = mi_extern_func->calloc(nmemb, size, file, lineno, func);
else
p = calloc(nmemb, size);
return p;
}
void
__mi_free(void *ptr, const char *file, int lineno, const char *func)
{
if (mi_extern_func && mi_extern_func->free)
mi_extern_func->free(ptr, file, lineno, func);
else
free(ptr);
}
void __mi_reuse(void *ptr, const char *file, int lineno, const char *func)
{
if (mi_extern_func && mi_extern_func->reuse)
mi_extern_func->reuse(ptr, file, lineno, func);
}
#endif
void
mISDN_set_debug_level(unsigned int mask)
{
mI_debug_mask = mask;
}
int
mi_printf(const char *file, int line, const char *func, int lev, const char *fmt, ...)
{
int ret = 0;
va_list args;
va_start(args, fmt);
if (mi_extern_func && mi_extern_func->prt_debug) {
ret = mi_extern_func->prt_debug(file, line, func, lev, fmt, args);
} else {
FILE *f = stderr;
if (lev == MISDN_LIBDEBUG_DEBUG || lev == MISDN_LIBDEBUG_INFO)
f = stdout;
ret = vfprintf(f, fmt, args);
fflush(f);
}
va_end(args);
return ret;
}
int
_mi_thread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine) (void *),
void *arg, const char *file, const char *caller, int line, const char *start_fn)
{
int ret;
if (mi_extern_func && mi_extern_func->thread_create)
ret = mi_extern_func->thread_create(thread, attr, start_routine, arg, file, caller, line, start_fn);
else
ret = pthread_create(thread, attr, start_routine, arg);
return ret;
}
void
mi_dhexprint(const char *file, int line, const char *func, const char *head, unsigned char *buf, int len)
{
int i;
char *p,*obuf;
obuf = malloc(100);
if (!obuf)
return;
p = obuf;
*p = 0;
for (i = 1; i <= len; i++) {
p += sprintf(p, "%02x ", *buf);
buf++;
if (!(i % 32)) {
p--;
*p = 0;
mi_printf(file, line, func, MISDN_LIBDEBUG_DEBUG, "%s %s\n", head, obuf);
p = obuf;
*p = 0;
}
}
if (*obuf) {
p--;
*p = 0;
mi_printf(file, line, func, MISDN_LIBDEBUG_DEBUG, "%s %s\n", head, obuf);
}
free(obuf);
}
void
mi_shexprint(char *dest, unsigned char *buf, int len)
{
char *p = dest;
while (len) {
p += sprintf(p, "%02x ", *buf);
buf++;
len--;
}
p--;
*p = 0;
}

159
src/libmisdnuser/misc/fsm.c Normal file
View File

@ -0,0 +1,159 @@
/* $Id: fsm.c,v 2.0 2004/06/29 14:35:31 kkeil Exp $
*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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 <stdlib.h>
#include <string.h>
#include "fsm.h"
#include "debug.h"
#define FSM_TIMER_DEBUG 0
void
FsmNew(struct Fsm *fsm,
struct FsmNode *fnlist, int fncount)
{
int i;
fsm->jumpmatrix = (FSMFNPTR *)
malloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count);
if (!fsm->jumpmatrix)
return;
memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count);
for (i = 0; i < fncount; i++)
if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) {
eprint("FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n",
i,(long)fnlist[i].state,(long)fsm->state_count,
(long)fnlist[i].event,(long)fsm->event_count);
} else
fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
}
void
FsmFree(struct Fsm *fsm)
{
free(fsm->jumpmatrix);
}
int
FsmEvent(struct FsmInst *fi, int event, void *arg)
{
FSMFNPTR r;
if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) {
eprint("FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
(long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count);
return(1);
}
r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
if (r) {
if (fi->debug)
fi->printdebug(fi, "State %s Event %s",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
r(fi, event, arg);
return (0);
} else {
if (fi->debug)
fi->printdebug(fi, "State %s Event %s no action",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
return (!0);
}
}
void
FsmChangeState(struct FsmInst *fi, int newstate)
{
if (fi->debug)
fi->printdebug(fi, "ChangeState %s -> %s",
fi->fsm->strState[fi->state], fi->fsm->strState[newstate]);
fi->state = newstate;
}
static void
FsmExpireTimer(void *arg)
{
struct FsmTimer *ft = arg;
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
#endif
FsmEvent(ft->fi, ft->event, ft->arg);
}
void
FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
{
ft->fi = fi;
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft);
#endif
init_timer(&ft->tl, ft->fi->tb, ft, FsmExpireTimer);
}
void
FsmDelTimer(struct FsmTimer *ft, int where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
#endif
del_timer(&ft->tl);
}
int
FsmAddTimer(struct FsmTimer *ft,
int millisec, int event, void *arg, int where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d",
(long) ft, millisec, where);
#endif
if (timer_pending(&ft->tl)) {
eprint("FsmAddTimer: timer already active!\n");
ft->fi->printdebug(ft->fi, "FsmAddTimer already active!");
return -1;
}
ft->event = event;
ft->arg = arg;
add_timer(&ft->tl, millisec);
return 0;
}
void
FsmRestartTimer(struct FsmTimer *ft,
int millisec, int event, void *arg, int where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d",
(long) ft, millisec, where);
#endif
if (timer_pending(&ft->tl))
del_timer(&ft->tl);
ft->event = event;
ft->arg = arg;
add_timer(&ft->tl, millisec);
}

View File

@ -0,0 +1,457 @@
/* mbuffer.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 <mISDN/mbuffer.h>
#include "helper.h"
#include "debug.h"
static struct mqueue free_queue_l2, free_queue_l3;
static int Max_Cache;
#define MIN_CACHE 4
#define MB_TYP_L2 2
#define MB_TYP_L3 3
#ifdef MEMLEAK_DEBUG
#include <mISDN/q931.h>
static struct Aqueue {
struct lhead Alist;
pthread_mutex_t lock;
int len;
} AllocQueue;
static inline void
Aqueue_init(struct Aqueue *q)
{
pthread_mutex_init(&q->lock, NULL);
q->len = 0;
q->Alist.prev = &q->Alist;
q->Alist.next = &q->Alist;
}
static inline int Amqueue_len(struct Aqueue *q)
{
return q->len ;
}
static inline void __list_add(struct lhead *new, struct lhead *prev, struct lhead *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void __list_del(struct lhead * prev, struct lhead * next)
{
next->prev = prev;
prev->next = next;
}
static inline void Aqueue_head(struct Aqueue *q, struct mbuffer *newm)
{
pthread_mutex_lock(&q->lock);
q->len++;
__list_add(&newm->Alist, &q->Alist, q->Alist.next);
pthread_mutex_unlock(&q->lock);
}
static inline void Aqueue_tail(struct Aqueue *q, struct mbuffer *newm)
{
pthread_mutex_lock(&q->lock);
q->len++;
__list_add(&newm->Alist, q->Alist.prev, &q->Alist);
pthread_mutex_unlock(&q->lock);
}
static inline struct mbuffer *Adequeue(struct Aqueue *q)
{
struct mbuffer *m;
struct lhead *le;
pthread_mutex_lock(&q->lock);
if (q->len) {
le = q->Alist.next;
__list_del(le->prev, le->next);
q->len--;
m = container_of(le, struct mbuffer, Alist);
} else
m = NULL;
pthread_mutex_unlock(&q->lock);
return m;
}
static void Aqueue_remove(struct Aqueue *q, struct mbuffer *mb)
{
pthread_mutex_lock(&q->lock);
__list_del(mb->Alist.prev, mb->Alist.next);
q->len--;
pthread_mutex_unlock(&q->lock);
}
#endif
void
init_mbuffer(int max_cache)
{
mqueue_init(&free_queue_l2);
mqueue_init(&free_queue_l3);
#ifdef MEMLEAK_DEBUG
Aqueue_init(&AllocQueue);
#endif
if (max_cache < MIN_CACHE)
max_cache = MIN_CACHE;
Max_Cache = max_cache;
}
static void
__mqueue_purge(struct mqueue *q) {
struct mbuffer *mb;
while ((mb = mdequeue(q))!=NULL) {
if (mb->head)
free(mb->head);
if (mb->chead)
free(mb->chead);
free(mb);
}
}
#ifdef MEMLEAK_DEBUG
static struct mbuffer *
_new_mbuffer(int typ, const char *file, int lineno, const char *func)
{
struct mbuffer *m;
m = __mi_calloc(1, sizeof(struct mbuffer), file, lineno, func);
if (!m)
goto err;
switch(typ) {
case MB_TYP_L3:
m->chead = malloc(MBUFFER_DATA_SIZE);
if (!m->chead) {
free(m);
goto err;
}
m->cend = m->chead + MBUFFER_DATA_SIZE;
m->ctail = m->chead;
case MB_TYP_L2:
m->head = malloc(MBUFFER_DATA_SIZE);
if (!m->head) {
if (m->chead)
free(m->chead);
free(m);
goto err;
}
m->end = m->head + MBUFFER_DATA_SIZE;
m->data = m->tail = m->head;
m->h = (struct mISDNhead *) m->head;
break;
}
return(m);
err:
eprint("%s: no mem for mbuffer\n", __FUNCTION__);
return(NULL);
}
static struct mbuffer *
_alloc_mbuffer(int typ, const char *file, int lineno, const char *func)
{
struct mbuffer *m;
switch(typ) {
case MB_TYP_L3:
m = mdequeue(&free_queue_l3);
break;
case MB_TYP_L2:
m = mdequeue(&free_queue_l2);
break;
default:
eprint("%s: wrong type %d\n", __func__, typ);
return NULL;
}
if (!m)
m = _new_mbuffer(typ, file, lineno, func);
else
__mi_reuse(m, file, lineno, func);
strncpy(m->d_fn, file, 79);
m->d_ln = lineno;
Aqueue_tail(&AllocQueue, m);
return m;
}
struct mbuffer *
__alloc_mbuffer(const char *file, int lineno, const char *func)
{
return _alloc_mbuffer(MB_TYP_L2, file, lineno, func);
}
void
__free_mbuffer(struct mbuffer *m, const char *file, int lineno, const char *func) {
if (!m)
return;
if (m->refcnt) {
m->refcnt--;
return;
}
Aqueue_remove(&AllocQueue, m);
strncpy(m->d_fn, file, 79);
m->d_ln = -lineno;
if (m->list) {
if (m->list == &free_queue_l3)
dprint(DBGM_L3BUFFER, "%s l3 buffer %p already freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0));
else if (m->list == &free_queue_l2)
dprint(DBGM_L3BUFFER, "%s l2 buffer %p already freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0));
else
dprint(DBGM_L3BUFFER, "%s buffer %p still in list %p : %lx\n", __FUNCTION__, m, m->list, (unsigned long)__builtin_return_address(0));
return;
} else
dprint(DBGM_L3BUFFER, "%s buffer %p freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0));
if (m->chead) {
if (free_queue_l3.len > Max_Cache) {
free(m->chead);
free(m->head);
__mi_free(m, file, lineno, func);
} else {
memset(&m->l3, 0, sizeof(m->l3));
memset(&m->l3h, 0, sizeof(m->l3h));
m->data = m->tail = m->head;
m->len = 0;
m->ctail = m->chead;
__mi_reuse(m, file, lineno, "IN_FREE_QUEUE");
mqueue_head(&free_queue_l3, m);
}
} else {
if (free_queue_l2.len > Max_Cache) {
free(m->head);
__mi_free(m, file, lineno, func);
} else {
memset(&m->l3, 0, sizeof(m->l3));
memset(&m->l3h, 0, sizeof(m->l3h));
m->data = m->tail = m->head;
m->len = 0;
__mi_reuse(m, file, lineno, "IN_FREE_QUEUE");
mqueue_head(&free_queue_l2, m);
}
}
}
struct l3_msg *
__alloc_l3_msg(const char *file, int lineno, const char *func)
{
struct mbuffer *m;
m = _alloc_mbuffer(MB_TYP_L3, file, lineno, func);
if (m)
return &m->l3;
else
return NULL;
}
void
__free_l3_msg(struct l3_msg *l3m, const char *file, int lineno, const char *func)
{
struct mbuffer *m;
if (!l3m)
return;
m = container_of(l3m, struct mbuffer, l3);
__free_mbuffer(m, file, lineno, func);
}
void
cleanup_mbuffer(void)
{
struct mbuffer *m;
int ql;
__mqueue_purge(&free_queue_l2);
__mqueue_purge(&free_queue_l3);
ql = Amqueue_len(&AllocQueue);
iprint("AllocQueue has %d lost mbuffer\n", ql);
if (ql) {
m = Adequeue(&AllocQueue);
while (m) {
wprint("Lost mbuffer allocated %s:%d typ=%s len=%d\n",
m->d_fn, m->d_ln, m->chead ? "L3" : "L2", m->len);
wprint(" H: prim=%s id=%d L3: prim=%s pid=%d\n",
_mi_msg_type2str(m->h->prim), m->h->id, _mi_msg_type2str(m->l3.type), m->l3.pid);
free_mbuffer(m);
m = Adequeue(&AllocQueue);
}
}
}
#else
static struct mbuffer *
_new_mbuffer(int typ)
{
struct mbuffer *m;
m = calloc(1, sizeof(struct mbuffer));
if (!m)
goto err;
switch(typ) {
case MB_TYP_L3:
m->chead = malloc(MBUFFER_DATA_SIZE);
if (!m->chead) {
free(m);
goto err;
}
m->cend = m->chead + MBUFFER_DATA_SIZE;
m->ctail = m->chead;
/* FALLTHRU */
case MB_TYP_L2:
m->head = malloc(MBUFFER_DATA_SIZE);
if (!m->head) {
if (m->chead)
free(m->chead);
free(m);
goto err;
}
m->end = m->head + MBUFFER_DATA_SIZE;
m->data = m->tail = m->head;
m->h = (struct mISDNhead *) m->head;
break;
}
return(m);
err:
eprint("%s: no mem for mbuffer\n", __FUNCTION__);
return(NULL);
}
static struct mbuffer *
_alloc_mbuffer(int typ)
{
struct mbuffer *m;
switch(typ) {
case MB_TYP_L3:
m = mdequeue(&free_queue_l3);
break;
case MB_TYP_L2:
m = mdequeue(&free_queue_l2);
break;
default:
eprint("%s: wrong type %d\n", __func__, typ);
return NULL;
}
if (!m)
m = _new_mbuffer(typ);
return m;
}
struct mbuffer *
alloc_mbuffer(void)
{
return _alloc_mbuffer(MB_TYP_L2);
}
void
free_mbuffer(struct mbuffer *m) {
if (!m)
return;
if (m->refcnt) {
m->refcnt--;
return;
}
if (m->list) {
if (m->list == &free_queue_l3)
dprint(DBGM_L3BUFFER, "%s l3 buffer %p already freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0));
else if (m->list == &free_queue_l2)
dprint(DBGM_L3BUFFER, "%s l2 buffer %p already freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0));
else
dprint(DBGM_L3BUFFER, "%s buffer %p still in list %p : %lx\n", __FUNCTION__, m, m->list, (unsigned long)__builtin_return_address(0));
return;
} else
dprint(DBGM_L3BUFFER, "%s buffer %p freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0));
if (m->chead) {
if (free_queue_l3.len > Max_Cache) {
free(m->chead);
free(m->head);
free(m);
} else {
memset(&m->l3, 0, sizeof(m->l3));
memset(&m->l3h, 0, sizeof(m->l3h));
m->data = m->tail = m->head;
m->len = 0;
m->ctail = m->chead;
mqueue_head(&free_queue_l3, m);
}
} else {
if (free_queue_l2.len > Max_Cache) {
free(m->head);
free(m);
} else {
memset(&m->l3, 0, sizeof(m->l3));
memset(&m->l3h, 0, sizeof(m->l3h));
m->data = m->tail = m->head;
m->len = 0;
mqueue_head(&free_queue_l2, m);
}
}
}
struct l3_msg *
alloc_l3_msg(void)
{
struct mbuffer *m;
m = _alloc_mbuffer(MB_TYP_L3);
if (m)
return &m->l3;
else
return NULL;
}
void
free_l3_msg(struct l3_msg *l3m)
{
struct mbuffer *m;
if (!l3m)
return;
m = container_of(l3m, struct mbuffer, l3);
free_mbuffer(m);
}
void
cleanup_mbuffer(void)
{
__mqueue_purge(&free_queue_l2);
__mqueue_purge(&free_queue_l3);
}
#endif
void
l3_msg_increment_refcnt(struct l3_msg *l3m)
{
struct mbuffer *m;
m = container_of(l3m, struct mbuffer, l3);
m->refcnt++;
}

View File

@ -0,0 +1,189 @@
/* mtimer.c
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
*
* 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 <sys/ioctl.h>
#include "mtimer.h"
#include "layer3.h"
#include "helper.h"
//#define DEBUG_TIMERS
#ifdef DEBUG_TIMERS
void debug_list(struct timer_base *tb)
{
struct mtimer *mt;
printf("list all timers of tb=%p:\n", tb);
list_for_each_entry(mt, &tb->pending_timer, list) {
printf(" - timer %p\n", mt);
}
}
#endif
int
init_timer(struct mtimer *mt, struct timer_base *tb, void *data, mtimer_func_t *f)
{
#ifdef DEBUG_TIMERS
printf("init timer %p\n", mt);
#endif
mt->tb = tb;
mt->id = 0;
mt->data = data;
mt->function = f;
return 0;
}
static int nextid = 1;
int
add_timer(struct mtimer *mt, int tout)
{
int ret, para;
struct mtimer *check;
/* hack to be sure that timer will fire even when no or negative time was given */
if (tout < 1)
tout = 1;
#ifdef DEBUG_TIMERS
printf("add timer %p (timeout=%d ms)\n", mt, tout);
debug_list(mt->tb);
#endif
mt->timeout = tout;
para = tout;
if (mt->tb->tdev) {
ret = ioctl(mt->tb->tdev, IMADDTIMER, (long)&para);
if (ret < 0)
return ret;
mt->id = para;
} else {
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
mt->expires = (double)tv.tv_sec + (double)tv.tv_nsec / 1000000000.0;
mt->expires += (double)tout / 1000.0;
ret = 0;
mt->id = nextid++;
}
list_for_each_entry(check, &mt->tb->pending_timer, list) {
if (check == mt) {
eprint("%s: timer already in list!\n", __func__);
return ret;
}
}
list_add_tail(&mt->list, &mt->tb->pending_timer);
#ifdef DEBUG_TIMERS
printf("add timer done\n");
debug_list(mt->tb);
#endif
return ret;
}
int
del_timer(struct mtimer *mt)
{
int ret = 0;
#ifdef DEBUG_TIMERS
printf("del timer %p id=%d\n", mt, mt->id);
debug_list(mt->tb);
#endif
if (mt->id) {
list_del(&mt->list);
if (mt->tb->tdev)
ret = ioctl(mt->tb->tdev, IMDELTIMER, (long)&mt->id);
mt->id = 0;
}
#ifdef DEBUG_TIMERS
printf("del timer done\n");
debug_list(mt->tb);
#endif
return ret;
}
int
timer_pending(struct mtimer *mt)
{
return mt->id;
}
int expire_timer(struct timer_base *tb, int id, double *misdn_timeout)
{
struct mtimer *mt;
double now = 0.0;
int work = 0;
double timeout = 0.0;
/* if id is < 0, then check for expired timer */
if (id < 0) {
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
now = (double)tv.tv_sec + (double)tv.tv_nsec / 1000000000.0;
}
again:
list_for_each_entry(mt, &tb->pending_timer, list) {
#ifdef DEBUG_TIMERS
// printf("check timer in list %p\n", mt);
#endif
if (id >= 0 && mt->id == id) {
work |= 1;
list_del(&mt->list);
mt->id = 0;
#ifdef DEBUG_TIMERS
printf("calling expire function\n");
debug_list(tb);
#endif
mt->function(mt->data);
#ifdef DEBUG_TIMERS
printf("expire functiion finished\n");
debug_list(tb);
#endif
return work;
}
if (id < 0 && now >= mt->expires) {
work |= 1;
list_del(&mt->list);
mt->expires = 0.0;
#ifdef DEBUG_TIMERS
printf("calling expire function\n");
debug_list(tb);
#endif
mt->function(mt->data);
#ifdef DEBUG_TIMERS
printf("expire functiion finished\n");
debug_list(tb);
#endif
goto again;
}
/* we look for the next unexpired timer */
if (!timeout || (mt->expires - now) < timeout)
timeout = mt->expires - now;
}
/* if no timer has expired, also present the timeout for the next timer */
if (misdn_timeout) {
if (work)
*misdn_timeout = 0.0;
else
*misdn_timeout = timeout;
}
return work;
}

View File

@ -0,0 +1,110 @@
/*
*
* Three Party (3PTY) Supplementary Services ETS 300 188-2
*
* ECT Facility ie encode/decode
*
* Copyright 2009,2010 by Karsten Keil <kkeil@linux-pingi.de>
* Copyright 2012 by Andreas Eversberg <jolly@eversberg.eu>
*
* 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 "asn1.h"
#include "3pty.h"
/* ******************************************************************* */
/*!
* \brief Encode the Begin3PTY facility ie.
*
* \param Dest Where to put the encoding
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacBegin3PTY(__u8 * Dest, const struct asn1_parm *pc, const void *unused)
{
int Length;
__u8 *p;
__u8 *SeqStart;
switch (pc->comp) {
case CompInvoke:
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_Begin3PTY);
Length = encodeComponent_Length(Dest, p);
break;
case CompReturnResult:
p = encodeComponent_Head(Dest, asn1ComponentTag_Result);
p += encodeInt(p, ASN1_TAG_INTEGER, pc->u.retResult.invokeId);
SeqStart = p;
SeqStart[0] = ASN1_TAG_SEQUENCE;
p = &SeqStart[2];
p += encodeOperationValue(p, Fac_Begin3PTY);
/* sequence Length */
SeqStart[1] = p - &SeqStart[2];
Length = encodeComponent_Length(Dest, p);
break;
default:
Length = -1;
break;
} /* end switch */
return Length;
} /* end encodeFacEctLoopTest() */
/* ******************************************************************* */
/*!
* \brief Encode the End3PTY facility ie.
*
* \param Dest Where to put the encoding
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacEnd3PTY(__u8 * Dest, const struct asn1_parm *pc, const void *unused)
{
int Length;
__u8 *p;
__u8 *SeqStart;
switch (pc->comp) {
case CompInvoke:
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_End3PTY);
Length = encodeComponent_Length(Dest, p);
break;
case CompReturnResult:
p = encodeComponent_Head(Dest, asn1ComponentTag_Result);
p += encodeInt(p, ASN1_TAG_INTEGER, pc->u.retResult.invokeId);
SeqStart = p;
SeqStart[0] = ASN1_TAG_SEQUENCE;
p = &SeqStart[2];
p += encodeOperationValue(p, Fac_End3PTY);
/* sequence Length */
SeqStart[1] = p - &SeqStart[2];
Length = encodeComponent_Length(Dest, p);
break;
default:
Length = -1;
break;
} /* end switch */
return Length;
} /* end encodeFacEctLoopTest() */

View File

@ -0,0 +1,624 @@
/* $Id$
*
* Addressing-Data-Elements ETS 300 196-1 D.3
*
* Addressing-Data-Elements encode/decode
*/
#include "asn1.h"
#include <string.h>
/* ------------------------------------------------------------------- */
/* ******************************************************************* */
/*!
* \internal
* \brief Parse the NumberDigits PartyNumber argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param PartyNumber Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
static int ParseNumberDigits_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartyNumber *PartyNumber)
{
struct asn1ParseString Number;
int LengthConsumed;
Number.buf = (char *)PartyNumber->Number;
Number.maxSize = sizeof(PartyNumber->Number);
Number.length = 0;
LengthConsumed = ParseNumericString(pc, p, end, &Number);
PartyNumber->LengthOfNumber = Number.length;
return LengthConsumed;
} /* end ParseNumberDigits_Full() */
/* ******************************************************************* */
/*!
* \internal
* \brief Parse the NSAP PartyNumber argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param PartyNumber Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
static int ParseNSAPPartyNumber(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartyNumber *PartyNumber)
{
struct asn1ParseString Number;
int LengthConsumed;
Number.buf = (char *)PartyNumber->Number;
Number.maxSize = sizeof(PartyNumber->Number);
Number.length = 0;
LengthConsumed = ParseOctetString(pc, p, end, &Number);
PartyNumber->LengthOfNumber = Number.length;
return LengthConsumed;
} /* end ParseNSAPPartyNumber() */
/* ******************************************************************* */
/*!
* \internal
* \brief Encode the public or private network PartyNumber type.
*
* \param Dest Where to put the encoding
* \param Number
* \param LengthOfNumber
* \param TypeOfNumber
*
* \retval length
*/
static int encodeNetworkPartyNumber(__u8 * Dest, const __s8 * Number, __u8 LengthOfNumber, __u8 TypeOfNumber)
{
__u8 *p;
Dest[0] = ASN1_TAG_SEQUENCE;
p = &Dest[2];
p += encodeEnum(p, ASN1_TAG_ENUM, TypeOfNumber);
p += encodeNumericString(p, ASN1_TAG_NUMERIC_STRING, Number, LengthOfNumber);
/* length */
Dest[1] = p - &Dest[2];
return p - Dest;
} /* end encodeNetworkPartyNumber() */
/* ******************************************************************* */
/*!
* \internal
* \brief Parse the public or private network PartyNumber argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param PartyNumber Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
static int ParseNetworkPartyNumber(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartyNumber *PartyNumber)
{
unsigned int TypeOfNumber;
INIT;
XSEQUENCE_1(ParseEnum, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &TypeOfNumber);
PartyNumber->TypeOfNumber = TypeOfNumber;
XSEQUENCE_1(ParseNumberDigits_Full, ASN1_TAG_NUMERIC_STRING, ASN1_NOT_TAGGED, PartyNumber);
return p - beg;
} /* end ParseNetworkPartyNumber() */
/* ******************************************************************* */
/*!
* \brief Encode the PartyNumber type.
*
* \param Dest Where to put the encoding
* \param PartyNumber Number information to encode.
*
* \retval length
*/
int encodePartyNumber_Full(__u8 * Dest, const struct FacPartyNumber *PartyNumber)
{
int Length;
switch (PartyNumber->Type) {
case 0: /* Unknown PartyNumber */
Length =
encodeNumericString(Dest, ASN1_TAG_CONTEXT_SPECIFIC | 0, (const __s8 *)PartyNumber->Number,
PartyNumber->LengthOfNumber);
break;
case 1: /* Public PartyNumber */
Length =
encodeNetworkPartyNumber(Dest, (const __s8 *)PartyNumber->Number, PartyNumber->LengthOfNumber,
PartyNumber->TypeOfNumber);
Dest[0] &= ASN1_TAG_CONSTRUCTED;
Dest[0] |= ASN1_TAG_CONTEXT_SPECIFIC | 1;
break;
case 2: /* NSAP encoded PartyNumber */
Length =
encodeOctetString(Dest, ASN1_TAG_CONTEXT_SPECIFIC | 2, (const __s8 *)PartyNumber->Number,
PartyNumber->LengthOfNumber);
break;
case 3: /* Data PartyNumber (Not used) */
Length =
encodeNumericString(Dest, ASN1_TAG_CONTEXT_SPECIFIC | 3, (const __s8 *)PartyNumber->Number,
PartyNumber->LengthOfNumber);
break;
case 4: /* Telex PartyNumber (Not used) */
Length =
encodeNumericString(Dest, ASN1_TAG_CONTEXT_SPECIFIC | 4, (const __s8 *)PartyNumber->Number,
PartyNumber->LengthOfNumber);
break;
case 5: /* Private PartyNumber */
Length =
encodeNetworkPartyNumber(Dest, (const __s8 *)PartyNumber->Number, PartyNumber->LengthOfNumber,
PartyNumber->TypeOfNumber);
Dest[0] &= ASN1_TAG_CONSTRUCTED;
Dest[0] |= ASN1_TAG_CONTEXT_SPECIFIC | 5;
break;
case 8: /* National Standard PartyNumber (Not used) */
Length =
encodeNumericString(Dest, ASN1_TAG_CONTEXT_SPECIFIC | 8, (const __s8 *)PartyNumber->Number,
PartyNumber->LengthOfNumber);
break;
default:
Length = 0;
break;
} /* end switch */
return Length;
} /* end encodePartyNumber_Full() */
/* ******************************************************************* */
/*!
* \brief Parse the PartyNumber argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param PartyNumber Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParsePartyNumber_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartyNumber *PartyNumber)
{
INIT;
PartyNumber->Type = 0; /* Unknown PartyNumber */
XCHOICE_1(ParseNumberDigits_Full, ASN1_TAG_CONTEXT_SPECIFIC, 0, PartyNumber);
PartyNumber->Type = 1; /* Public PartyNumber */
XCHOICE_1(ParseNetworkPartyNumber, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 1, PartyNumber);
PartyNumber->Type = 2; /* NSAP encoded PartyNumber */
XCHOICE_1(ParseNSAPPartyNumber, ASN1_TAG_CONTEXT_SPECIFIC, 2, PartyNumber);
PartyNumber->Type = 3; /* Data PartyNumber (Not used) */
XCHOICE_1(ParseNumberDigits_Full, ASN1_TAG_CONTEXT_SPECIFIC, 3, PartyNumber);
PartyNumber->Type = 4; /* Telex PartyNumber (Not used) */
XCHOICE_1(ParseNumberDigits_Full, ASN1_TAG_CONTEXT_SPECIFIC, 4, PartyNumber);
PartyNumber->Type = 5; /* Private PartyNumber */
XCHOICE_1(ParseNetworkPartyNumber, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 5, PartyNumber);
PartyNumber->Type = 8; /* National Standard PartyNumber (Not used) */
XCHOICE_1(ParseNumberDigits_Full, ASN1_TAG_CONTEXT_SPECIFIC, 8, PartyNumber);
XCHOICE_DEFAULT;
} /* end ParsePartyNumber_Full() */
/* ******************************************************************* */
/*!
* \internal
* \brief Parse the User information string PartySubaddress argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param PartySubaddress Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
static int ParseUserSubaddressInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartySubaddress *PartySubaddress)
{
struct asn1ParseString Subaddress;
int LengthConsumed;
Subaddress.buf = (char *)PartySubaddress->u.UserSpecified.Information;
Subaddress.maxSize = sizeof(PartySubaddress->u.UserSpecified.Information);
Subaddress.length = 0;
LengthConsumed = ParseOctetString(pc, p, end, &Subaddress);
PartySubaddress->Length = Subaddress.length;
return LengthConsumed;
} /* end ParseUserSubaddressInfo() */
/* ******************************************************************* */
/*!
* \internal
* \brief Parse the User PartySubaddress argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param PartySubaddress Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
static int ParseUserSubaddress(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartySubaddress *PartySubaddress)
{
int OddCount;
INIT;
PartySubaddress->Type = 0; /* UserSpecified */
XSEQUENCE_1(ParseUserSubaddressInfo, ASN1_TAG_OCTET_STRING, ASN1_NOT_TAGGED, PartySubaddress);
if (p < end) {
XSEQUENCE_1(ParseBoolean, ASN1_TAG_BOOLEAN, ASN1_NOT_TAGGED, &OddCount);
PartySubaddress->u.UserSpecified.OddCount = OddCount;
PartySubaddress->u.UserSpecified.OddCountPresent = 1;
} else {
PartySubaddress->u.UserSpecified.OddCount = 0;
PartySubaddress->u.UserSpecified.OddCountPresent = 0;
}
return p - beg;
} /* end ParseUserSubaddress() */
/* ******************************************************************* */
/*!
* \internal
* \brief Parse the NSAP PartySubaddress argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param PartySubaddress Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
static int ParseNSAPSubaddress_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartySubaddress *PartySubaddress)
{
struct asn1ParseString Subaddress;
int LengthConsumed;
PartySubaddress->Type = 1; /* NSAP */
Subaddress.buf = (char *)PartySubaddress->u.Nsap;
Subaddress.maxSize = sizeof(PartySubaddress->u.Nsap);
Subaddress.length = 0;
LengthConsumed = ParseOctetString(pc, p, end, &Subaddress);
PartySubaddress->Length = Subaddress.length;
return LengthConsumed;
} /* end ParseNSAPSubaddress_Full() */
/* ******************************************************************* */
/*!
* \brief Encode the PartySubaddress type.
*
* \param Dest Where to put the encoding
* \param PartySubaddress Subaddress information to encode.
*
* \retval length
*/
int encodePartySubaddress_Full(__u8 * Dest, const struct FacPartySubaddress *PartySubaddress)
{
__u8 *p;
int Length;
switch (PartySubaddress->Type) {
case 0: /* UserSpecified */
Dest[0] = ASN1_TAG_SEQUENCE;
p = &Dest[2];
p += encodeOctetString(p, ASN1_TAG_OCTET_STRING, (const __s8 *)PartySubaddress->u.UserSpecified.Information,
PartySubaddress->Length);
if (PartySubaddress->u.UserSpecified.OddCountPresent) {
p += encodeBoolean(p, ASN1_TAG_BOOLEAN, PartySubaddress->u.UserSpecified.OddCount);
}
/* length */
Dest[1] = p - &Dest[2];
Length = p - Dest;
break;
case 1: /* NSAP */
Length =
encodeOctetString(Dest, ASN1_TAG_OCTET_STRING, (const __s8 *)PartySubaddress->u.Nsap, PartySubaddress->Length);
break;
default:
Length = 0;
break;
} /* end switch */
return Length;
} /* end encodePartySubaddress_Full() */
/* ******************************************************************* */
/*!
* \brief Parse the PartySubaddress argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param PartySubaddress Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParsePartySubaddress_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacPartySubaddress *PartySubaddress)
{
INIT;
XCHOICE_1(ParseUserSubaddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, PartySubaddress);
XCHOICE_1(ParseNSAPSubaddress_Full, ASN1_TAG_OCTET_STRING, ASN1_NOT_TAGGED, PartySubaddress);
XCHOICE_DEFAULT;
} /* end ParsePartySubaddress_Full() */
/* ******************************************************************* */
/*!
* \brief Encode the Address type.
*
* \param Dest Where to put the encoding
* \param Address Address information to encode.
*
* \retval length
*/
int encodeAddress_Full(__u8 * Dest, const struct FacAddress *Address)
{
__u8 *p;
Dest[0] = ASN1_TAG_SEQUENCE;
p = &Dest[2];
p += encodePartyNumber_Full(p, &Address->Party);
if (Address->Subaddress.Length) {
p += encodePartySubaddress_Full(p, &Address->Subaddress);
}
/* length */
Dest[1] = p - &Dest[2];
return p - Dest;
} /* end encodeAddress_Full() */
/* ******************************************************************* */
/*!
* \brief Parse the Address argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param Address Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParseAddress_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAddress *Address)
{
INIT;
XSEQUENCE_1(ParsePartyNumber_Full, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &Address->Party);
if (p < end) {
/* The optional subaddress must be present since there is something left. */
XSEQUENCE_1(ParsePartySubaddress_Full, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &Address->Subaddress);
} else {
Address->Subaddress.Length = 0; /* Subaddress not present */
}
return p - beg;
} /* end ParseAddress_Full() */
/* ******************************************************************* */
/*!
* \brief Encode the PresentedNumberUnscreened type.
*
* \param Dest Where to put the encoding
* \param Presented Number information to encode.
*
* \retval length
*/
int encodePresentedNumberUnscreened_Full(__u8 * Dest, const struct FacPresentedNumberUnscreened *Presented)
{
__u8 *p;
__u8 *TagStart;
p = Dest;
switch (Presented->Type) {
case 0: /* presentationAllowedNumber */
TagStart = p;
TagStart[0] = ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED | 0;
p = &TagStart[2];
p += encodePartyNumber_Full(p, &Presented->Unscreened);
/* tag Length */
TagStart[1] = p - &TagStart[2];
break;
case 1: /* presentationRestricted */
p += encodeNull(p, ASN1_TAG_CONTEXT_SPECIFIC | 1);
break;
case 2: /* numberNotAvailableDueToInterworking */
p += encodeNull(p, ASN1_TAG_CONTEXT_SPECIFIC | 2);
break;
case 3: /* presentationRestrictedNumber */
TagStart = p;
TagStart[0] = ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED | 3;
p = &TagStart[2];
p += encodePartyNumber_Full(p, &Presented->Unscreened);
/* tag Length */
TagStart[1] = p - &TagStart[2];
break;
default:
break;
} /* end switch */
return p - Dest;
} /* end encodePresentedNumberUnscreened_Full() */
/* ******************************************************************* */
/*!
* \brief Parse the PresentedNumberUnscreened argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param Presented Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParsePresentedNumberUnscreened_Full(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacPresentedNumberUnscreened *Presented)
{
INIT;
Presented->Type = 0;
XCHOICE_1(ParsePartyNumber_Full, ASN1_TAG_EXPLICIT | ASN1_TAG_CONTEXT_SPECIFIC, 0, &Presented->Unscreened);
Presented->Type = 1;
XCHOICE(ParseNull, ASN1_TAG_CONTEXT_SPECIFIC, 1);
Presented->Type = 2;
XCHOICE(ParseNull, ASN1_TAG_CONTEXT_SPECIFIC, 2);
Presented->Type = 3;
XCHOICE_1(ParsePartyNumber_Full, ASN1_TAG_EXPLICIT | ASN1_TAG_CONTEXT_SPECIFIC, 3, &Presented->Unscreened);
XCHOICE_DEFAULT;
} /* end ParsePresentedNumberUnscreened_Full() */
/* ******************************************************************* */
/*!
* \internal
* \brief Encode the AddressScreened type.
*
* \param Dest Where to put the encoding
* \param Address Address information to encode.
*
* \retval length
*/
static int encodeAddressScreened_Full(__u8 * Dest, const struct FacAddressScreened *Address)
{
__u8 *p;
Dest[0] = ASN1_TAG_SEQUENCE;
p = &Dest[2];
p += encodePartyNumber_Full(p, &Address->Party);
p += encodeEnum(p, ASN1_TAG_ENUM, Address->ScreeningIndicator);
if (Address->Subaddress.Length) {
p += encodePartySubaddress_Full(p, &Address->Subaddress);
}
/* length */
Dest[1] = p - &Dest[2];
return p - Dest;
} /* end encodeAddressScreened_Full() */
/* ******************************************************************* */
/*!
* \internal
* \brief Parse the AddressScreened argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param Address Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
static int ParseAddressScreened_Full(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAddressScreened *Address)
{
unsigned int Value;
INIT;
XSEQUENCE_1(ParsePartyNumber_Full, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &Address->Party);
XSEQUENCE_1(ParseEnum, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &Value);
Address->ScreeningIndicator = Value;
if (p < end) {
/* The optional subaddress must be present since there is something left. */
XSEQUENCE_1(ParsePartySubaddress_Full, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &Address->Subaddress);
} else {
Address->Subaddress.Length = 0; /* Subaddress not present */
}
return p - beg;
} /* end ParseAddressScreened_Full() */
/* ******************************************************************* */
/*!
* \brief Encode the PresentedAddressScreened type.
*
* \param Dest Where to put the encoding
* \param Presented Address information to encode.
*
* \retval length
*/
int encodePresentedAddressScreened_Full(__u8 * Dest, const struct FacPresentedAddressScreened *Presented)
{
__u8 *p;
__u8 *TagStart;
p = Dest;
switch (Presented->Type) {
case 0: /* presentationAllowedAddress */
TagStart = p;
p += encodeAddressScreened_Full(p, &Presented->Address);
TagStart[0] = ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED | 0;
break;
case 1: /* presentationRestricted */
p += encodeNull(p, ASN1_TAG_CONTEXT_SPECIFIC | 1);
break;
case 2: /* numberNotAvailableDueToInterworking */
p += encodeNull(p, ASN1_TAG_CONTEXT_SPECIFIC | 2);
break;
case 3: /* presentationRestrictedAddress */
TagStart = p;
p += encodeAddressScreened_Full(p, &Presented->Address);
TagStart[0] = ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED | 3;
break;
default:
break;
} /* end switch */
return p - Dest;
} /* end encodePresentedAddressScreened_Full() */
/* ******************************************************************* */
/*!
* \brief Parse the PresentedAddressScreened argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param Presented Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParsePresentedAddressScreened_Full(struct asn1_parm *pc, u_char * p, u_char * end,
struct FacPresentedAddressScreened *Presented)
{
INIT;
Presented->Type = 0;
XCHOICE_1(ParseAddressScreened_Full, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 0, &Presented->Address);
Presented->Type = 1;
XCHOICE(ParseNull, ASN1_TAG_CONTEXT_SPECIFIC, 1);
Presented->Type = 2;
XCHOICE(ParseNull, ASN1_TAG_CONTEXT_SPECIFIC, 2);
Presented->Type = 3;
XCHOICE_1(ParseAddressScreened_Full, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 3, &Presented->Address);
XCHOICE_DEFAULT;
} /* end ParsePresentedAddressScreened_Full() */
/* ------------------------------------------------------------------- */
/* end asn1_address.c */

View File

@ -0,0 +1,348 @@
/* $Id$
*
*/
#include "asn1.h"
#include <string.h>
// ======================================================================
// AOC EN 300 182-1 V1.3.3
int ParseAOCDCurrency(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
cur->chargeNotAvailable = 1;
cur->freeOfCharge = 0;
memset(cur->currency, 0, sizeof(cur->currency));
cur->currencyAmount = 0;
cur->multiplier = 0;
cur->typeOfChargingInfo = -1;
cur->billingId = -1;
XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail
cur->chargeNotAvailable = 0;
XCHOICE_1(ParseAOCDCurrencyInfo, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, cur);
XCHOICE_DEFAULT;
}
int ParseAOCDChargingUnit(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu)
{
INIT;
chu->chargeNotAvailable = 1;
chu->freeOfCharge = 0;
chu->recordedUnits = 0;
chu->typeOfChargingInfo = -1;
chu->billingId = -1;
memset(chu->chargeNumber, 0, sizeof(chu->chargeNumber));
XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail
chu->chargeNotAvailable = 0;
XCHOICE_1(ParseAOCDChargingUnitInfo, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, chu);
XCHOICE_DEFAULT;
}
// AOCECurrency
int ParseAOCECurrency(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
cur->chargeNotAvailable = 1;
cur->freeOfCharge = 0;
memset(cur->currency, 0, sizeof(cur->currency));
cur->currencyAmount = 0;
cur->multiplier = 0;
cur->typeOfChargingInfo = -1;
cur->billingId = -1;
XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail
cur->chargeNotAvailable = 0;
XCHOICE_1(ParseAOCECurrencyInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, cur);
XCHOICE_DEFAULT;
}
// AOCEChargingUnit
int ParseAOCEChargingUnit(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu)
{
INIT;
chu->chargeNotAvailable = 1;
chu->freeOfCharge = 0;
chu->recordedUnits = 0;
chu->typeOfChargingInfo = -1;
chu->billingId = -1;
memset(chu->chargeNumber, 0, sizeof(chu->chargeNumber));
XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail
XCHOICE_1(ParseAOCEChargingUnitInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, chu);
XCHOICE_DEFAULT;
}
// AOCDCurrencyInfo
int ParseAOCDSpecificCurrency(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
XSEQUENCE_1(ParseRecordedCurrency, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 1, cur);
XSEQUENCE_1(ParseTypeOfChargingInfo, ASN1_TAG_CONTEXT_SPECIFIC, 2, &cur->typeOfChargingInfo);
XSEQUENCE_OPT_1(ParseAOCDBillingId, ASN1_TAG_CONTEXT_SPECIFIC, 3, &cur->billingId);
return p - beg;
}
int ParseAOCDCurrencyInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
XCHOICE_1(ParseAOCDSpecificCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, cur);
cur->freeOfCharge = 1;
XCHOICE(ParseNull, ASN1_TAG_CONTEXT_SPECIFIC, 1); // freeOfCharge
cur->freeOfCharge = 0;
XCHOICE_DEFAULT;
}
// AOCDChargingUnitInfo
int ParseAOCDSpecificChargingUnits(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu)
{
INIT;
XSEQUENCE_1(ParseRecordedUnitsList, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 1, &chu->recordedUnits);
XSEQUENCE_1(ParseTypeOfChargingInfo, ASN1_TAG_CONTEXT_SPECIFIC, 2, &chu->typeOfChargingInfo);
XSEQUENCE_OPT_1(ParseAOCDBillingId, ASN1_TAG_CONTEXT_SPECIFIC, 3, &chu->billingId);
// p_L3L4(pc, CC_CHARGE | INDICATION, &recordedUnits);
return p - beg;
}
int ParseAOCDChargingUnitInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu)
{
INIT;
XCHOICE_1(ParseAOCDSpecificChargingUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, chu);
chu->freeOfCharge = 1;
XCHOICE(ParseNull, ASN1_TAG_CONTEXT_SPECIFIC, 1); // freeOfCharge
chu->freeOfCharge = 0;
XCHOICE_DEFAULT;
}
// RecordedCurrency
int ParseRecordedCurrency(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
XSEQUENCE_1(ParseCurrency, ASN1_TAG_CONTEXT_SPECIFIC, 1, (char *)cur->currency);
XSEQUENCE_1(ParseAmount, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 2, cur);
return p - beg;
}
// RecordedUnitsList
int ParseRecordedUnitsList(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *recordedUnits)
{
int i;
__u32 units;
INIT;
*recordedUnits = 0;
XSEQUENCE_1(ParseRecordedUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, recordedUnits);
for (i = 0; i < 31; i++) {
units = 0;
XSEQUENCE_OPT_1(ParseRecordedUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &units);
*recordedUnits += units;
}
return p - beg;
}
// TypeOfChargingInfo
int ParseTypeOfChargingInfo(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *typeOfChargingInfo)
{
return ParseEnum(pc, p, end, typeOfChargingInfo);
}
// RecordedUnits
int ParseRecordedUnitsChoice(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *recordedUnits)
{
INIT;
XCHOICE_1(ParseNumberOfUnits, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, recordedUnits);
XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // not available
XCHOICE_DEFAULT;
}
int ParseRecordedUnits(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *recordedUnits)
{
__u32 typeOfUnit;
INIT;
XSEQUENCE_1(ParseRecordedUnitsChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, recordedUnits);
XSEQUENCE_OPT_1(ParseTypeOfUnit, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &typeOfUnit);
return p - beg;
}
// AOCDBillingId
int ParseAOCDBillingId(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *billingId)
{
return ParseEnum(pc, p, end, billingId);
}
/* #if 0 */
// AOCECurrencyInfo
int ParseAOCESpecificCurrency(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
XSEQUENCE_1(ParseRecordedCurrency, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 1, cur);
XSEQUENCE_OPT_1(ParseAOCEBillingId, ASN1_TAG_CONTEXT_SPECIFIC, 2, &cur->billingId);
return p - beg;
}
int ParseAOCECurrencyInfoChoice(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
XCHOICE_1(ParseAOCESpecificCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, cur);
XCHOICE(ParseNull, ASN1_TAG_CONTEXT_SPECIFIC, 1); // freeOfCharge
XCHOICE_DEFAULT;
}
int ParseAOCECurrencyInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
XSEQUENCE_1(ParseAOCECurrencyInfoChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, cur);
XSEQUENCE_OPT_1(ParseChargingAssociation, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &cur->chargeAssoc);
XCHOICE_DEFAULT;
}
// AOCEChargingUnitInfo
int ParseAOCESpecificChargingUnits(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu)
{
INIT;
XSEQUENCE_1(ParseRecordedUnitsList, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 1, &chu->recordedUnits);
XSEQUENCE_OPT_1(ParseAOCEBillingId, ASN1_TAG_CONTEXT_SPECIFIC, 2, &chu->billingId);
return p - beg;
}
int ParseAOCEChargingUnitInfoChoice(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu)
{
INIT;
XCHOICE_1(ParseAOCESpecificChargingUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, chu);
XCHOICE(ParseNull, ASN1_TAG_CONTEXT_SPECIFIC, 1); // freeOfCharge
XCHOICE_DEFAULT;
}
int ParseAOCEChargingUnitInfo(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCChargingUnit *chu)
{
INIT;
XSEQUENCE_1(ParseAOCEChargingUnitInfoChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, chu);
XSEQUENCE_OPT_1(ParseChargingAssociation, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &chu->chargeAssoc);
return p - beg;
}
// AOCEBillingId
int ParseAOCEBillingId(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *billingId)
{
return ParseEnum(pc, p, end, billingId);
}
// Currency
int ParseCurrency(struct asn1_parm *pc, u_char * p, u_char * end, char *currency)
{
struct asn1ParseString str;
str.buf = currency;
str.maxSize = 11; /* sizeof(struct FacAOCCurrency.currency) */
return ParseIA5String(pc, p, end, &str);
}
// Amount
int ParseAmount(struct asn1_parm *pc, u_char * p, u_char * end, struct FacAOCCurrency *cur)
{
INIT;
XSEQUENCE_1(ParseCurrencyAmount, ASN1_TAG_CONTEXT_SPECIFIC, 1, &cur->currencyAmount);
XSEQUENCE_1(ParseMultiplier, ASN1_TAG_CONTEXT_SPECIFIC, 2, &cur->multiplier);
return p - beg;
}
// CurrencyAmount
int ParseCurrencyAmount(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *currencyAmount)
{
return ParseUnsignedInteger(pc, p, end, currencyAmount);
}
// Multiplier
int ParseMultiplier(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *multiplier)
{
return ParseEnum(pc, p, end, multiplier);
}
// TypeOfUnit
int ParseTypeOfUnit(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *typeOfUnit)
{
return ParseUnsignedInteger(pc, p, end, typeOfUnit);
}
// NumberOfUnits
int ParseNumberOfUnits(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *numberOfUnits)
{
return ParseUnsignedInteger(pc, p, end, numberOfUnits);
}
// Charging Association
int ParseChargingAssociation(struct asn1_parm *pc, u_char * p, u_char * end, struct ChargingAssociation *chargeAssoc)
{
INIT;
struct FacPartyNumber partyNumber;
partyNumber.LengthOfNumber = 0;
partyNumber.Number[0] = '\0';
XCHOICE_1(ParsePartyNumber_Full, ASN1_TAG_SEQUENCE, 0, &partyNumber);
if ((partyNumber.LengthOfNumber) && (partyNumber.LengthOfNumber <= 30)
&& (partyNumber.Number[0] != '\0'))
strncpy((char *)chargeAssoc->chargeNumber, (char *)partyNumber.Number, 30);
XCHOICE_1(ParseChargeIdentifier, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &chargeAssoc->chargeIdentifier);
XCHOICE_DEFAULT;
}
// ChargeIdentifier
int ParseChargeIdentifier(struct asn1_parm *pc, u_char * p, u_char * end, int *chargeIdentifier)
{
return ParseSignedInteger(pc, p, end, chargeIdentifier);
}

View File

@ -0,0 +1,13 @@
/* $Id: asn1_basic_service.c,v 1.2 2006/08/16 14:15:52 nadi Exp $
*
*/
#include "asn1.h"
// ======================================================================
// Basic Service Elements EN 300 196-1 D.6
int ParseBasicService(struct asn1_parm *pc, u_char * p, u_char * end, __u32 *basicService)
{
return ParseEnum(pc, p, end, basicService);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,590 @@
/*
* ASN1 components for supplementary services
*
* Copyright 2009,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 "asn1.h"
#include "diversion.h"
#include "ccbs.h"
#include "ect.h"
#include <string.h>
// ======================================================================
// Component EN 300 196-1 D.1
static const struct asn1OidConvert OIDConversion[] = {
/* *INDENT-OFF* */
/*
* Note the first value in oid.values[] is really the first two
* OID subidentifiers. They are compressed using this formula:
* First_Value = (First_Subidentifier * 40) + Second_Subidentifier
*/
/* {ccitt(0) identified-organization(4) etsi(0) 359 operations-and-errors(1)} */
{ FacOIDBase_CCBS, { 4, { 4, 0, 359, 1 } } },
/* {ccitt(0) identified-organization(4) etsi(0) 359 private-networks-operations-and-errors(2)} */
{ FacOIDBase_CCBS_T, { 4, { 4, 0, 359, 2 } } },
/* {ccitt(0) identified-organization(4) etsi(0) 1065 operations-and-errors(1)} */
{ FacOIDBase_CCNR, { 4, { 4, 0, 1065, 1 } } },
/* {ccitt(0) identified-organization(4) etsi(0) 1065 private-networks-operations-and-errors(2)} */
{ FacOIDBase_CCNR_T, { 4, { 4, 0, 1065, 2 } } },
/* {itu-t(0) identified-organization(4) etsi(0) 196 status-request-procedure(9)} */
{ FacOIDBase_StatusRequest, { 4, { 4, 0, 196, 9 } } },
/* {ccitt(0) identified-organization(4) etsi(0) 369 operations-and-errors(1)} */
{ FacOIDBase_ECT, { 4, { 4, 0, 369, 1 } } },
/* *INDENT-ON* */
};
const struct asn1OidConvert *FindOidByOidValue(int length, const __u16 oidValues[])
{
int index;
for (index = 0; index < sizeof(OIDConversion) / sizeof(OIDConversion[0]); ++index) {
if (OIDConversion[index].oid.numValues == length
&& memcmp(OIDConversion[index].oid.value, oidValues, length * sizeof(oidValues[0])) == 0) {
return &OIDConversion[index];
}
} /* end for */
return NULL;
} /* end FindOidByOidValue() */
const struct asn1OidConvert *FindOidByEnum(__u16 value)
{
int index;
for (index = 0; index < sizeof(OIDConversion) / sizeof(OIDConversion[0]); ++index) {
if (FAC_OID_BASE(OIDConversion[index].baseCode) <= value && value < FAC_OID_BASE(OIDConversion[index].baseCode + 1)) {
return &OIDConversion[index];
}
} /* end for */
return NULL;
} /* end FindOidByEnum() */
__u16 ConvertOidToEnum(const struct asn1Oid * oid, __u16 errorValue)
{
const struct asn1OidConvert *convert;
__u16 enumValue;
enumValue = errorValue;
if (oid->numValues) {
convert = FindOidByOidValue(oid->numValues - 1, oid->value);
if (convert) {
enumValue = FAC_OID_BASE(convert->baseCode) + oid->value[oid->numValues - 1];
}
}
return enumValue;
} /* end ConvertOidToEnum() */
int ConvertEnumToOid(struct asn1Oid *oid, __u16 enumValue)
{
int status;
const struct asn1OidConvert *convert;
status = 0; /* Assume failure */
convert = FindOidByEnum(enumValue);
if (convert) {
*oid = convert->oid;
if (oid->numValues < sizeof(oid->value) / sizeof(oid->value[0])) {
oid->value[oid->numValues] = enumValue - FAC_OID_BASE(convert->baseCode);
++oid->numValues;
status = 1; /* successful */
}
}
return status;
} /* end ConvertEnumToOid() */
static int ParseOperationOid(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *operationValue)
{
struct asn1Oid operationOid;
int rval;
CallASN1(rval, p, end, ParseOid(pc, p, end, &operationOid));
*operationValue = ConvertOidToEnum(&operationOid, Fac_None);
return rval;
} /* end ParseOperationOid() */
static int ParseOperationValue(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *operationValue)
{
INIT;
XCHOICE_1(ParseUnsignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, operationValue);
XCHOICE_1(ParseOperationOid, ASN1_TAG_OBJECT_IDENTIFIER, ASN1_NOT_TAGGED, operationValue);
XCHOICE_DEFAULT;
}
static int ParseInvokeComponent(struct asn1_parm *pc, u_char * p, u_char * end, int dummy)
{
signed int invokeId;
signed int linkedId;
unsigned int operationValue;
INIT;
pc->comp = CompInvoke;
XSEQUENCE_1(ParseSignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId);
pc->u.inv.invokeId = invokeId;
XSEQUENCE_OPT_1(ParseSignedInteger, ASN1_TAG_CONTEXT_SPECIFIC, 0, &linkedId);
XSEQUENCE_1(ParseOperationValue, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &operationValue);
pc->u.inv.operationValue = operationValue;
switch (operationValue) {
/* Diversion support */
case Fac_Begin3PTY:
case Fac_End3PTY:
/* No additional invoke parameters */
break;
case Fac_ActivationDiversion:
XSEQUENCE_1(ParseActivationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.ActivationDiversion);
break;
case Fac_DeactivationDiversion:
XSEQUENCE_1(ParseDeactivationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.DeactivationDiversion);
break;
case Fac_ActivationStatusNotificationDiv:
XSEQUENCE_1(ParseActivationStatusNotificationDiv, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED,
&pc->u.inv.o.ActivationStatusNotificationDiv);
break;
case Fac_DeactivationStatusNotificationDiv:
XSEQUENCE_1(ParseDeactivationStatusNotificationDiv, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED,
&pc->u.inv.o.DeactivationStatusNotificationDiv);
break;
case Fac_InterrogationDiversion:
XSEQUENCE_1(ParseInterrogationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED,
&pc->u.inv.o.InterrogationDiversion);
break;
case Fac_DiversionInformation:
XSEQUENCE_1(ParseDiversionInformation, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.DiversionInformation);
break;
case Fac_CallDeflection:
XSEQUENCE_1(ParseCallDeflection, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CallDeflection);
break;
case Fac_CallRerouteing:
XSEQUENCE_1(ParseCallRerouteing, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CallRerouteing);
break;
case Fac_InterrogateServedUserNumbers:
/* No additional invoke parameters */
break;
case Fac_DivertingLegInformation1:
XSEQUENCE_1(ParseDivertingLegInformation1, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED,
&pc->u.inv.o.DivertingLegInformation1);
break;
case Fac_DivertingLegInformation2:
XSEQUENCE_1(ParseDivertingLegInformation2, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED,
&pc->u.inv.o.DivertingLegInformation2);
break;
case Fac_DivertingLegInformation3:
XSEQUENCE_1(ParseDivertingLegInformation3, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED,
&pc->u.inv.o.DivertingLegInformation3);
break;
/* ECT support */
case Fac_EctExecute:
/* No additional invoke parameters */
break;
case Fac_ExplicitEctExecute:
XSEQUENCE_1(ParseExplicitEctExecute, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.ExplicitEctExecute);
break;
case Fac_RequestSubaddress:
/* No additional invoke parameters */
break;
case Fac_SubaddressTransfer:
XSEQUENCE_1(ParseSubaddressTransfer, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.SubaddressTransfer);
break;
case Fac_EctLinkIdRequest:
/* No additional invoke parameters */
break;
case Fac_EctInform:
XSEQUENCE_1(ParseEctInform, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.EctInform);
break;
case Fac_EctLoopTest:
XSEQUENCE_1(ParseEctLoopTest, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.EctLoopTest);
break;
/* AOC support */
#if 0
case Fac_ChargingRequest:
case Fac_AOCSCurrency:
case Fac_AOCSSpecialArr:
break;
#endif
case Fac_AOCDCurrency:
XSEQUENCE_1(ParseAOCDCurrency, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.AOCcur);
break;
case Fac_AOCDChargingUnit:
XSEQUENCE_1(ParseAOCDChargingUnit, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.AOCchu);
break;
case Fac_AOCECurrency:
XSEQUENCE_1(ParseAOCECurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.AOCcur);
break;
case Fac_AOCEChargingUnit:
XSEQUENCE_1(ParseAOCEChargingUnit, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.AOCchu);
break;
case Fac_StatusRequest:
XSEQUENCE_1(ParseStatusRequest, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.StatusRequest);
break;
/* CCBS/CCNR support */
case Fac_CallInfoRetain:
XSEQUENCE_1(ParseCallInfoRetain, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.CallInfoRetain);
break;
case Fac_CCBSRequest:
XSEQUENCE_1(ParseCCBSRequest, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSRequest);
break;
case Fac_CCBSDeactivate:
XSEQUENCE_1(ParseCCBSDeactivate, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSDeactivate);
break;
case Fac_CCBSInterrogate:
XSEQUENCE_1(ParseCCBSInterrogate, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSInterrogate);
break;
case Fac_CCBSErase:
XSEQUENCE_1(ParseCCBSErase, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSErase);
break;
case Fac_CCBSRemoteUserFree:
XSEQUENCE_1(ParseCCBSRemoteUserFree, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSRemoteUserFree);
break;
case Fac_CCBSCall:
XSEQUENCE_1(ParseCCBSCall, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSCall);
break;
case Fac_CCBSStatusRequest:
XSEQUENCE_1(ParseCCBSStatusRequest, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSStatusRequest);
break;
case Fac_CCBSBFree:
XSEQUENCE_1(ParseCCBSBFree, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSBFree);
break;
case Fac_EraseCallLinkageID:
XSEQUENCE_1(ParseEraseCallLinkageID, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.EraseCallLinkageID);
break;
case Fac_CCBSStopAlerting:
XSEQUENCE_1(ParseCCBSStopAlerting, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBSStopAlerting);
break;
case Fac_CCNRRequest:
XSEQUENCE_1(ParseCCNRRequest, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.inv.o.CCNRRequest);
break;
case Fac_CCNRInterrogate:
XSEQUENCE_1(ParseCCNRInterrogate, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CCNRInterrogate);
break;
/* CCBS-T/CCNR-T support */
case Fac_CCBS_T_Call:
case Fac_CCBS_T_Suspend:
case Fac_CCBS_T_Resume:
case Fac_CCBS_T_RemoteUserFree:
case Fac_CCBS_T_Available:
/* No additional invoke parameters */
break;
case Fac_CCBS_T_Request:
XSEQUENCE_1(ParseCCBS_T_Request, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CCBS_T_Request);
break;
case Fac_CCNR_T_Request:
XSEQUENCE_1(ParseCCNR_T_Request, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.CCNR_T_Request);
break;
default:
return -1;
}
return p - beg;
}
int ParseReturnResultComponentSequence(struct asn1_parm *pc, u_char * p, u_char * end, int dummy)
{
INIT;
XSEQUENCE_1(ParseOperationValue, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.retResult.operationValue);
pc->u.retResult.operationValuePresent = 1;
switch (pc->u.retResult.operationValue) {
/* 3PTY */
case Fac_Begin3PTY:
case Fac_End3PTY:
/* No additional result parameters */
break;
/* Diversion support */
case Fac_ActivationDiversion:
case Fac_DeactivationDiversion:
/* No additional result parameters */
break;
case Fac_InterrogationDiversion:
XSEQUENCE_1(ParseInterrogationDiversion_RES, ASN1_TAG_SET, ASN1_NOT_TAGGED,
&pc->u.retResult.o.InterrogationDiversion);
break;
case Fac_CallDeflection:
case Fac_CallRerouteing:
/* No additional result parameters */
break;
case Fac_InterrogateServedUserNumbers:
XSEQUENCE_1(ParseInterrogateServedUserNumbers_RES, ASN1_TAG_SET, ASN1_NOT_TAGGED,
&pc->u.retResult.o.InterrogateServedUserNumbers);
break;
/* ECT support */
case Fac_EctLinkIdRequest:
XSEQUENCE_1(ParseEctLinkIdRequest_RES, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.retResult.o.EctLinkIdRequest);
break;
case Fac_EctLoopTest:
XSEQUENCE_1(ParseEctLoopTest_RES, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.retResult.o.EctLoopTest);
break;
case Fac_StatusRequest:
XSEQUENCE_1(ParseStatusRequest_RES, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.retResult.o.StatusRequest);
break;
/* CCBS/CCNR support */
case Fac_CCBSDeactivate:
/* No additional result parameters */
break;
case Fac_CCBSStatusRequest:
XSEQUENCE_1(ParseCCBSStatusRequest_RES, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.retResult.o.CCBSStatusRequest);
break;
case Fac_CCBSRequest:
XSEQUENCE_1(ParseCCBSRequest_RES, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.retResult.o.CCBSRequest);
break;
case Fac_CCBSInterrogate:
XSEQUENCE_1(ParseCCBSInterrogate_RES, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.retResult.o.CCBSInterrogate);
break;
case Fac_CCNRRequest:
XSEQUENCE_1(ParseCCNRRequest_RES, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.retResult.o.CCNRRequest);
break;
case Fac_CCNRInterrogate:
XSEQUENCE_1(ParseCCNRInterrogate_RES, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.retResult.o.CCNRInterrogate);
break;
/* CCBS-T/CCNR-T support */
case Fac_CCBS_T_Request:
XSEQUENCE_1(ParseCCBS_T_Request_RES, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.retResult.o.CCBS_T_Request);
break;
case Fac_CCNR_T_Request:
XSEQUENCE_1(ParseCCNR_T_Request_RES, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &pc->u.retResult.o.CCNR_T_Request);
break;
default:
return -1;
}
return p - beg;
}
static int ParseReturnResultComponent(struct asn1_parm *pc, u_char * p, u_char * end, int dummy)
{
signed int invokeId;
INIT;
pc->comp = CompReturnResult;
XSEQUENCE_1(ParseSignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId);
pc->u.retResult.invokeId = invokeId;
pc->u.retResult.operationValuePresent = 0;
XSEQUENCE_OPT(ParseReturnResultComponentSequence, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
return p - beg;
}
static int ParseErrorOid(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *errorValue)
{
struct asn1Oid errorOid;
int rval;
CallASN1(rval, p, end, ParseOid(pc, p, end, &errorOid));
*errorValue = ConvertOidToEnum(&errorOid, FacError_Unknown);
return rval;
} /* end ParseErrorOid() */
static int ParseErrorValue(struct asn1_parm *pc, u_char * p, u_char * end, unsigned int *errorValue)
{
INIT;
XCHOICE_1(ParseUnsignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, errorValue);
XCHOICE_1(ParseErrorOid, ASN1_TAG_OBJECT_IDENTIFIER, ASN1_NOT_TAGGED, errorValue);
XCHOICE_DEFAULT;
}
int ParseReturnErrorComponent(struct asn1_parm *pc, u_char * p, u_char * end, int dummy)
{
int invokeId;
unsigned int errorValue;
const char *error;
char msg[20];
INIT;
pc->comp = CompReturnError;
XSEQUENCE_1(ParseSignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId);
XSEQUENCE_1(ParseErrorValue, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &errorValue);
pc->u.retError.invokeId = invokeId;
pc->u.retError.errorValue = errorValue;
switch (errorValue) {
case FacError_Gen_NotSubscribed:
error = "not subscribed";
break;
case FacError_Gen_NotAvailable:
error = "not available";
break;
case FacError_Gen_NotImplemented:
error = "not implemented";
break;
case FacError_Gen_InvalidServedUserNr:
error = "invalid served user nr";
break;
case FacError_Gen_InvalidCallState:
error = "invalid call state";
break;
case FacError_Gen_BasicServiceNotProvided:
error = "basic service not provided";
break;
case FacError_Gen_NotIncomingCall:
error = "not incoming call";
break;
case FacError_Gen_SupplementaryServiceInteractionNotAllowed:
error = "supplementary service interaction not allowed";
break;
case FacError_Gen_ResourceUnavailable:
error = "resource unavailable";
break;
case FacError_Div_InvalidDivertedToNr:
error = "invalid diverted-to nr";
break;
case FacError_Div_SpecialServiceNr:
error = "special service nr";
break;
case FacError_Div_DiversionToServedUserNr:
error = "diversion to served user nr";
break;
case FacError_Div_IncomingCallAccepted:
error = "incoming call accepted";
break;
case FacError_Div_NumberOfDiversionsExceeded:
error = "number of diversions exceeded";
break;
case FacError_Div_NotActivated:
error = "not activated";
break;
case FacError_Div_RequestAlreadyAccepted:
error = "request already accepted";
break;
case FacError_AOC_NoChargingInfoAvailable:
error = "no charging info available";
break;
case FacError_CCBS_InvalidCallLinkageID:
error = "invalid call linkage id";
break;
case FacError_CCBS_InvalidCCBSReference:
error = "invalid ccbs reference";
break;
case FacError_CCBS_T_LongTermDenial:
case FacError_CCBS_LongTermDenial:
error = "long term denial";
break;
case FacError_CCBS_T_ShortTermDenial:
case FacError_CCBS_ShortTermDenial:
error = "short term denial";
break;
case FacError_CCBS_IsAlreadyActivated:
error = "ccbs is already activated";
break;
case FacError_CCBS_AlreadyAccepted:
error = "already accepted";
break;
case FacError_CCBS_OutgoingCCBSQueueFull:
error = "outgoing ccbs queue full";
break;
case FacError_CCBS_CallFailureReasonNotBusy:
error = "call failure reason not busy";
break;
case FacError_CCBS_NotReadyForCall:
error = "not ready for call";
break;
case FacError_Unknown:
error = "unknown OID error code";
break;
default:
sprintf(msg, "(%d)", errorValue);
error = msg;
break;
}
dprint(DBGM_ASN1_DEC, "Decoded-Error: %s\n", error);
return p - beg;
} /* end of ParseReturnErrorComponent() */
int ParseProblemValue(struct asn1_parm *pc, u_char * p, u_char * end, asn1Problem prob)
{
int rval;
pc->u.reject.problem = prob;
rval = ParseUnsignedInteger(pc, p, end, &pc->u.reject.problemValue);
dprint(DBGM_ASN1_DEC, "ParseProblemValue: %d %d, rval:%d\n", prob, pc->u.reject.problemValue, rval);
return rval;
}
int ParseRejectProblem(struct asn1_parm *pc, u_char * p, u_char * end, int dummy)
{
INIT;
XCHOICE_1(ParseProblemValue, ASN1_TAG_CONTEXT_SPECIFIC, 0, GeneralP);
XCHOICE_1(ParseProblemValue, ASN1_TAG_CONTEXT_SPECIFIC, 1, InvokeP);
XCHOICE_1(ParseProblemValue, ASN1_TAG_CONTEXT_SPECIFIC, 2, ReturnResultP);
XCHOICE_1(ParseProblemValue, ASN1_TAG_CONTEXT_SPECIFIC, 3, ReturnErrorP);
XCHOICE_DEFAULT;
}
static int ParseRejectInvokeId(struct asn1_parm *pc, u_char * p, u_char * end, int dummy)
{
INIT;
pc->u.reject.invokeIdPresent = 1;
XCHOICE_1(ParseSignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &pc->u.reject.invokeId);
pc->u.reject.invokeIdPresent = 0;
pc->u.reject.invokeId = 0;
XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED);
XCHOICE_DEFAULT;
}
int ParseRejectComponent(struct asn1_parm *pc, u_char * p, u_char * end, int dummy)
{
INIT;
pc->comp = CompReject;
XSEQUENCE(ParseRejectInvokeId, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED);
if (pc->u.reject.invokeIdPresent) {
dprint(DBGM_ASN1_DEC, "ParseRejectComponent: invokeId %d\n", pc->u.reject.invokeId);
}
XSEQUENCE(ParseRejectProblem, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED);
return p - beg;
}
int ParseComponent(struct asn1_parm *pc, u_char * p, u_char * end)
{
INIT;
XCHOICE(ParseInvokeComponent, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 1);
XCHOICE(ParseReturnResultComponent, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 2);
XCHOICE(ParseReturnErrorComponent, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 3);
XCHOICE(ParseRejectComponent, ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED, 4);
XCHOICE_DEFAULT;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,403 @@
/*
*
* Explicit Call Transfer (ECT) Supplementary Services ETS 300 369-1
*
* ECT Facility ie encode/decode
*
* Copyright 2009,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 "asn1.h"
#include "ect.h"
/* ------------------------------------------------------------------- */
/* ******************************************************************* */
/*!
* \brief Encode the EctExecute facility ie.
*
* \param Dest Where to put the encoding
* \param EctExecute Information needed to encode in ie.
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacEctExecute(__u8 * Dest, const struct asn1_parm *pc, const void *val)
{
int Length;
__u8 *p;
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_EctExecute);
Length = encodeComponent_Length(Dest, p);
return Length;
} /* end encodeFacEctExecute() */
/* ******************************************************************* */
/*!
* \brief Encode the ExplicitEctExecute facility ie.
*
* \param Dest Where to put the encoding
* \param ExplicitEctExecute Information needed to encode in ie.
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacExplicitEctExecute(__u8 * Dest, const struct asn1_parm *pc, const struct FacExplicitEctExecute *ExplicitEctExecute)
{
int Length;
__u8 *p;
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_ExplicitEctExecute);
p += encodeInt(p, ASN1_TAG_INTEGER, ExplicitEctExecute->LinkID);
Length = encodeComponent_Length(Dest, p);
return Length;
} /* end encodeFacExplicitEctExecute() */
/* ******************************************************************* */
/*!
* \brief Parse the ExplicitEctExecute invoke argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param ExplicitEctExecute Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParseExplicitEctExecute(struct asn1_parm *pc, u_char * p, u_char * end, struct FacExplicitEctExecute *ExplicitEctExecute)
{
int LinkID;
int ret;
u_char *beg;
beg = p;
XSEQUENCE_1(ParseSignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &LinkID);
ExplicitEctExecute->LinkID = LinkID;
return p - beg;
} /* end ParseExplicitEctExecute() */
/* ******************************************************************* */
/*!
* \brief Encode the RequestSubaddress facility ie.
*
* \param Dest Where to put the encoding
* \param RequestSubaddress Information needed to encode in ie.
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacRequestSubaddress(__u8 * Dest, const struct asn1_parm *pc, const void *val)
{
int Length;
__u8 *p;
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_RequestSubaddress);
Length = encodeComponent_Length(Dest, p);
return Length;
} /* end encodeFacRequestSubaddress() */
/* ******************************************************************* */
/*!
* \brief Encode the SubaddressTransfer facility ie.
*
* \param Dest Where to put the encoding
* \param SubaddressTransfer Information needed to encode in ie.
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacSubaddressTransfer(__u8 * Dest, const struct asn1_parm *pc, const struct FacSubaddressTransfer *SubaddressTransfer)
{
int Length;
__u8 *p;
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_SubaddressTransfer);
p += encodePartySubaddress_Full(p, &SubaddressTransfer->Subaddress);
Length = encodeComponent_Length(Dest, p);
return Length;
} /* end encodeFacSubaddressTransfer() */
/* ******************************************************************* */
/*!
* \brief Parse the SubaddressTransfer invoke argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param SubaddressTransfer Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParseSubaddressTransfer(struct asn1_parm *pc, u_char * p, u_char * end, struct FacSubaddressTransfer *SubaddressTransfer)
{
int ret;
u_char *beg;
beg = p;
XSEQUENCE_1(ParsePartySubaddress_Full, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &SubaddressTransfer->Subaddress);
return p - beg;
} /* end ParseSubaddressTransfer() */
/* ******************************************************************* */
/*!
* \brief Encode the EctLinkIdRequest facility ie.
*
* \param Dest Where to put the encoding
* \param EctLinkIdRequest Information needed to encode in ie.
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacEctLinkIdRequest(__u8 * Dest, const struct asn1_parm *pc, const struct FacEctLinkIdRequest_RES *EctLinkIdRequest)
{
int Length;
__u8 *p;
__u8 *SeqStart;
switch (pc->comp) {
case CompInvoke:
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_EctLinkIdRequest);
/* No arguments */
Length = encodeComponent_Length(Dest, p);
break;
case CompReturnResult:
p = encodeComponent_Head(Dest, asn1ComponentTag_Result);
p += encodeInt(p, ASN1_TAG_INTEGER, pc->u.retResult.invokeId);
SeqStart = p;
SeqStart[0] = ASN1_TAG_SEQUENCE;
p = &SeqStart[2];
p += encodeOperationValue(p, Fac_EctLinkIdRequest);
p += encodeInt(p, ASN1_TAG_INTEGER, pc->u.retResult.o.EctLinkIdRequest.LinkID);
/* sequence Length */
SeqStart[1] = p - &SeqStart[2];
Length = encodeComponent_Length(Dest, p);
break;
default:
Length = -1;
break;
} /* end switch */
return Length;
} /* end encodeFacEctLinkIdRequest() */
/* ******************************************************************* */
/*!
* \brief Parse the EctLinkIdRequest result parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse parameters
* \param end End buffer position that must not go past.
* \param EctLinkIdRequest Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParseEctLinkIdRequest_RES(struct asn1_parm *pc, u_char * p, u_char * end, struct FacEctLinkIdRequest_RES *EctLinkIdRequest)
{
int LinkID;
int ret;
u_char *beg;
beg = p;
XSEQUENCE_1(ParseSignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &LinkID);
EctLinkIdRequest->LinkID = LinkID;
return p - beg;
} /* end ParseEctLinkIdRequest_RES() */
/* ******************************************************************* */
/*!
* \brief Encode the EctInform facility ie.
*
* \param Dest Where to put the encoding
* \param EctInform Information needed to encode in ie.
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacEctInform(__u8 * Dest, const struct asn1_parm *pc, const struct FacEctInform *EctInform)
{
int Length;
__u8 *p;
__u8 *SeqStart;
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_EctInform);
SeqStart = p;
SeqStart[0] = ASN1_TAG_SEQUENCE;
p = &SeqStart[2];
p += encodeEnum(p, ASN1_TAG_ENUM, EctInform->Status);
if (EctInform->RedirectionPresent) {
p += encodePresentedNumberUnscreened_Full(p, &EctInform->Redirection);
}
/* sequence Length */
SeqStart[1] = p - &SeqStart[2];
Length = encodeComponent_Length(Dest, p);
return Length;
} /* end encodeFacEctInform() */
/* ******************************************************************* */
/*!
* \brief Parse the EctInform invoke argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param EctInform Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParseEctInform(struct asn1_parm *pc, u_char * p, u_char * end, struct FacEctInform *EctInform)
{
unsigned int Status;
INIT;
XSEQUENCE_1(ParseEnum, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &Status);
EctInform->Status = Status;
if (p < end) {
XSEQUENCE_1(ParsePresentedNumberUnscreened_Full, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &EctInform->Redirection);
EctInform->RedirectionPresent = 1;
} else {
EctInform->RedirectionPresent = 0;
}
return p - beg;
} /* end ParseEctInform() */
/* ******************************************************************* */
/*!
* \brief Encode the EctLoopTest facility ie.
*
* \param Dest Where to put the encoding
* \param EctLoopTest Information needed to encode in ie.
*
* \retval length on success.
* \retval -1 on error.
*/
int encodeFacEctLoopTest(__u8 * Dest, const struct asn1_parm *pc, const struct FacEctLoopTest *EctLoopTest)
{
int Length;
__u8 *p;
__u8 *SeqStart;
switch (pc->comp) {
case CompInvoke:
p = encodeComponentInvoke_Head(Dest, pc->u.inv.invokeId, Fac_EctLoopTest);
p += encodeInt(p, ASN1_TAG_INTEGER, EctLoopTest->CallTransferID);
Length = encodeComponent_Length(Dest, p);
break;
case CompReturnResult:
p = encodeComponent_Head(Dest, asn1ComponentTag_Result);
p += encodeInt(p, ASN1_TAG_INTEGER, pc->u.retResult.invokeId);
SeqStart = p;
SeqStart[0] = ASN1_TAG_SEQUENCE;
p = &SeqStart[2];
p += encodeOperationValue(p, Fac_EctLoopTest);
p += encodeEnum(p, ASN1_TAG_ENUM, pc->u.retResult.o.EctLoopTest.LoopResult);
/* sequence Length */
SeqStart[1] = p - &SeqStart[2];
Length = encodeComponent_Length(Dest, p);
break;
default:
Length = -1;
break;
} /* end switch */
return Length;
} /* end encodeFacEctLoopTest() */
/* ******************************************************************* */
/*!
* \brief Parse the EctLoopTest invoke argument parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse arguments
* \param end End buffer position that must not go past.
* \param EctLoopTest Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParseEctLoopTest(struct asn1_parm *pc, u_char * p, u_char * end, struct FacEctLoopTest *EctLoopTest)
{
int CallTransferID;
int ret;
u_char *beg;
beg = p;
XSEQUENCE_1(ParseSignedInteger, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &CallTransferID);
EctLoopTest->CallTransferID = CallTransferID;
return p - beg;
} /* end ParseEctLoopTest() */
/* ******************************************************************* */
/*!
* \brief Parse the EctLoopTest result parameters.
*
* \param pc Complete component message storage data.
* \param p Starting buffer position to parse parameters
* \param end End buffer position that must not go past.
* \param EctLoopTest Parameter storage to fill.
*
* \retval length of buffer consumed
* \retval -1 on error.
*/
int ParseEctLoopTest_RES(struct asn1_parm *pc, u_char * p, u_char * end, struct FacEctLoopTest_RES *EctLoopTest)
{
unsigned int LoopResult;
int ret;
u_char *beg;
beg = p;
XSEQUENCE_1(ParseEnum, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &LoopResult);
EctLoopTest->LoopResult = LoopResult;
return p - beg;
} /* end ParseEctLoopTest_RES() */
/* ------------------------------------------------------------------- */
/* end asn1_ect.c */

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,9 @@
#include <stdlib.h>
#include <errno.h>
#include "options.h"
#include "../libdebug/debug.h"
#include "../liblogging/logging.h"
const char *selected_config_file = NULL;
typedef struct option {
struct option *next;
@ -47,7 +49,7 @@ char *options_strdup(const char *s)
o = malloc(sizeof(struct options_strdup_entry) + strlen(s));
if (!o) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "No mem!\n");
LOGP(DOPTIONS, LOGL_ERROR, "No mem!\n");
abort();
}
o->next = options_strdup_list;
@ -62,21 +64,21 @@ void option_add(int short_option, const char *long_option, int parameter_count)
option_t *option;
/* check if option already exists or is not allowed */
for (option = option_head; option; option = option->next) {
if (!strcmp(option->long_option, "config")) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Option '%s' is not allowed to add, please fix!\n", option->long_option);
if (!strcmp(long_option, "config") || !strcmp(long_option, "no-config")) {
LOGP(DOPTIONS, LOGL_ERROR, "Option '%s' is not allowed to add, please fix!\n", option->long_option);
abort();
}
for (option = option_head; option; option = option->next) {
if (option->short_option == short_option
|| !strcmp(option->long_option, long_option)) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Option '%s' added twice, please fix!\n", option->long_option);
LOGP(DOPTIONS, LOGL_ERROR, "Option '%s' added twice, please fix!\n", option->long_option);
abort();
}
}
option = calloc(1, sizeof(*option));
if (!option) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "No mem!\n");
LOGP(DOPTIONS, LOGL_ERROR, "No mem!\n");
abort();
}
@ -87,7 +89,7 @@ void option_add(int short_option, const char *long_option, int parameter_count)
option_tailp = &(option->next);
}
int options_config_file(int argc, char *argv[], const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]))
int options_config_file(int argc, char *argv[], const char *_config_file, int (*handle_options)(int short_option, int argi, char *argv[]))
{
static const char *home;
char config[256];
@ -101,21 +103,29 @@ int options_config_file(int argc, char *argv[], const char *config_file, int (*h
/* select for alternative config file */
if (argc > 2 && !strcmp(argv[1], "--config"))
config_file = argv[2];
selected_config_file = argv[2];
else
selected_config_file = _config_file;
/* select for alternative config file */
if (argc > 1 && !strcmp(argv[1], "--no-config")) {
selected_config_file = NULL;
return 1;
}
/* add home directory */
if (config_file[0] == '~' && config_file[1] == '/') {
if (selected_config_file[0] == '~' && selected_config_file[1] == '/') {
home = getenv("HOME");
if (home == NULL)
return 1;
sprintf(config, "%s/%s", home, config_file + 2);
sprintf(config, "%s/%s", home, selected_config_file + 2);
} else
strcpy(config, config_file);
strcpy(config, selected_config_file);
/* open config file */
fp = fopen(config, "r");
if (!fp) {
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file '%s' seems not to exist, using command line options only.\n", config);
LOGP(DOPTIONS, LOGL_INFO, "Config file '%s' seems not to exist, using command line options only.\n", config);
return 1;
}
@ -201,21 +211,21 @@ int options_config_file(int argc, char *argv[], const char *config_file, int (*h
/* search option */
for (option = option_head; option; option = option->next) {
if (opt[0] == option->short_option && opt[1] == '\0') {
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s' ('%s'), parameter%s\n", opt, option->long_option, params);
LOGP(DOPTIONS, LOGL_INFO, "Config file option '%s' ('%s'), parameter%s\n", opt, option->long_option, params);
break;
}
if (!strcmp(opt, option->long_option)) {
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s', parameter%s\n", opt, params);
LOGP(DOPTIONS, LOGL_INFO, "Config file option '%s', parameter%s\n", opt, params);
break;
}
}
if (!option) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d is not a valid option, use '-h' for help!\n", opt, config_file, line);
LOGP(DOPTIONS, LOGL_ERROR, "Given option '%s' in config file '%s' at line %d is not a valid option, use '-h' for help!\n", opt, selected_config_file, line);
rc = -EINVAL;
goto done;
}
if (option->parameter_count != i) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d requires %d parameter(s), use '-h' for help!\n", opt, config_file, line, option->parameter_count);
LOGP(DOPTIONS, LOGL_ERROR, "Given option '%s' in config file '%s' at line %d requires %d parameter(s), use '-h' for help!\n", opt, selected_config_file, line, option->parameter_count);
return -EINVAL;
}
rc = handle_options(option->short_option, 0, args);
@ -242,20 +252,22 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
/* --config */
if (!strcmp(argv[argi], "--config")) {
if (argi > 1) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' must be the first option specified, use '-h' for help!\n", argv[argi]);
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' must be the first option specified, use '-h' for help!\n", argv[argi]);
return -EINVAL;
}
if (argc <= 2) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' requires 1 parameter, use '-h' for help!\n", argv[argi]);
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' requires 1 parameter, use '-h' for help!\n", argv[argi]);
return -EINVAL;
}
argi += 1;
continue;
}
if (!strcmp(argv[argi], "--no-config"))
continue;
if (argv[argi][0] == '-') {
if (argv[argi][1] != '-') {
if (strlen(argv[argi]) != 2) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' exceeds one character, use '-h' for help!\n", argv[argi]);
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' exceeds one character, use '-h' for help!\n", argv[argi]);
return -EINVAL;
}
/* -x */
@ -265,9 +277,9 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
params[0] = '\0';
for (i = 0; i < option->parameter_count; i++)
sprintf(strchr(params, '\0'), " '%s'", argv[argi + 1 + i]);
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s'), parameter%s\n", argv[argi], option->long_option, params);
LOGP(DOPTIONS, LOGL_INFO, "Command line option '%s' ('--%s'), parameter%s\n", argv[argi], option->long_option, params);
} else
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s')\n", argv[argi], option->long_option);
LOGP(DOPTIONS, LOGL_INFO, "Command line option '%s' ('--%s')\n", argv[argi], option->long_option);
break;
}
}
@ -279,19 +291,19 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
params[0] = '\0';
for (i = 0; i < option->parameter_count; i++)
sprintf(strchr(params, '\0'), " '%s'", argv[argi + 1 + i]);
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s', parameter%s\n", argv[argi], params);
LOGP(DOPTIONS, LOGL_INFO, "Command line option '%s', parameter%s\n", argv[argi], params);
} else
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s'\n", argv[argi]);
LOGP(DOPTIONS, LOGL_INFO, "Command line option '%s'\n", argv[argi]);
break;
}
}
}
if (!option) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' is not a valid option, use '-h' for help!\n", argv[argi]);
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' is not a valid option, use '-h' for help!\n", argv[argi]);
return -EINVAL;
}
if (argi + option->parameter_count >= argc) {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' requires %d parameter(s), use '-h' for help!\n", argv[argi], option->parameter_count);
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' requires %d parameter(s), use '-h' for help!\n", argv[argi], option->parameter_count);
return -EINVAL;
}
rc = handle_options(option->short_option, argi + 1, argv);
@ -306,7 +318,7 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
/* no more options, so we check if there is an option after a non-option parameter */
for (i = argi; i < argc; i++) {
if (argv[i][0] == '-') {
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' behind command line parameter '%s' not allowed! Please put all command line options before command line parameter(s).\n", argv[i], argv[argi]);
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' behind command line parameter '%s' not allowed! Please put all command line options before command line parameter(s).\n", argv[i], argv[argi]);
return -EINVAL;
}
}

View File

@ -1,4 +1,5 @@
extern const char *selected_config_file;
char *options_strdup(const char *s);
void option_add(int short_option, const char *long_option, int parameter_count);
int options_config_file(int argc, char *argv[], const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]));

View File

@ -1,15 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libosmocc.a
libosmocc_a_SOURCES = \
message.c \
socket.c \
cause.c \
screen.c \
endpoint.c \
session.c \
sdp.c \
rtp.c \
helper.c

View File

@ -1,252 +0,0 @@
/* OSMO-CC Processing: convert causes
*
* (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
* 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 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 <stdint.h>
#include <arpa/inet.h>
#include "message.h"
#include "cause.h"
/* stolen from freeswitch */
/* map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 */
static uint8_t status2isdn_cause(uint16_t status)
{
switch (status) {
case 200:
return 16; //SWITCH_CAUSE_NORMAL_CLEARING;
case 401:
case 402:
case 403:
case 407:
case 603:
return 21; //SWITCH_CAUSE_CALL_REJECTED;
case 404:
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
case 485:
case 604:
return 3; //SWITCH_CAUSE_NO_ROUTE_DESTINATION;
case 408:
case 504:
return 102; //SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
case 410:
return 22; //SWITCH_CAUSE_NUMBER_CHANGED;
case 413:
case 414:
case 416:
case 420:
case 421:
case 423:
case 505:
case 513:
return 127; //SWITCH_CAUSE_INTERWORKING;
case 480:
return 180; //SWITCH_CAUSE_NO_USER_RESPONSE;
case 400:
case 481:
case 500:
case 503:
return 41; //SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE;
case 486:
case 600:
return 17; //SWITCH_CAUSE_USER_BUSY;
case 484:
return 28; //SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
case 488:
case 606:
return 88; //SWITCH_CAUSE_INCOMPATIBLE_DESTINATION;
case 502:
return 38; //SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
case 405:
return 63; //SWITCH_CAUSE_SERVICE_UNAVAILABLE;
case 406:
case 415:
case 501:
return 79; //SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED;
case 482:
case 483:
return 25; //SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR;
case 487:
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL;
default:
return 31; //SWITCH_CAUSE_NORMAL_UNSPECIFIED;
}
}
static uint16_t isdn2status_cause(uint8_t cause, uint8_t location)
{
switch (cause) {
case 1:
return 404;
case 2:
return 404;
case 3:
return 404;
case 17:
return 486;
case 18:
return 408;
case 19:
return 480;
case 20:
return 480;
case 21:
if (location == OSMO_CC_LOCATION_USER)
return 603;
return 403;
case 22:
//return 301;
return 410;
case 23:
return 410;
case 26:
return 404;
case 27:
return 502;
case 28:
return 484;
case 29:
return 501;
case 31:
return 480;
case 34:
return 503;
case 38:
return 503;
case 41:
return 503;
case 42:
return 503;
case 47:
return 503;
case 55:
return 403;
case 57:
return 403;
case 58:
return 503;
case 65:
return 488;
case 69:
return 501;
case 70:
return 488;
case 79:
return 501;
case 87:
return 403;
case 88:
return 503;
case 102:
return 504;
case 111:
return 500;
case 127:
return 500;
default:
return 468;
}
}
static uint8_t socket2isdn_cause(uint8_t sock)
{
switch (sock) {
case OSMO_CC_SOCKET_CAUSE_FAILED:
return 47;
case OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE:
return 41;
case OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH:
return 38;
case OSMO_CC_SOCKET_CAUSE_TIMEOUT:
return 41;
default:
return 31;
}
}
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause)
{
/* complete cause, from socket cause */
if (cause->socket_cause && cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0)
cause->isdn_cause = socket2isdn_cause(cause->socket_cause);
/* convert ISDN cause to SIP cause */
if (cause->isdn_cause && ntohs(cause->sip_cause_networkorder) == 0) {
cause->sip_cause_networkorder = htons(isdn2status_cause(cause->isdn_cause, cause->location));
}
/* convert SIP cause to ISDN cause */
if (ntohs(cause->sip_cause_networkorder) && cause->isdn_cause == 0) {
cause->isdn_cause = status2isdn_cause(ntohs(cause->sip_cause_networkorder));
}
/* no cause at all: use Normal Call Clearing */
if (cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0) {
cause->isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
cause->sip_cause_networkorder = htons(486);
}
}
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg)
{
void *ie;
uint8_t type;
uint16_t length;
void *value;
/* search for (all) cause IE and convert the values, if needed */
ie = msg->data;
while ((value = osmo_cc_msg_sep_ie(msg, &ie, &type, &length))) {
if (type == OSMO_CC_IE_CAUSE && length >= sizeof(struct osmo_cc_ie_cause)) {
osmo_cc_convert_cause(value);
}
}
}
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause)
{
/* first cause */
if (old_cause == 0)
return new_cause;
/* first prio: return 17 */
if (old_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY
|| new_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY)
return OSMO_CC_ISDN_CAUSE_USER_BUSY;
/* second prio: return 21 */
if (old_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED
|| new_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED)
return OSMO_CC_ISDN_CAUSE_CALL_REJECTED;
/* third prio: return other than 88 and 18 (what ever was first) */
if (old_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
&& old_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND)
return old_cause;
if (new_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
&& new_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND)
return new_cause;
/* fourth prio: return 88 */
if (old_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
|| new_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST)
return OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST;
/* fith prio: return 18 */
return OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND;
}

View File

@ -1,5 +0,0 @@
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause);
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg);
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause);

File diff suppressed because it is too large Load Diff

View File

@ -1,131 +0,0 @@
#ifndef OSMO_CC_ENDPOINT_H
#define OSMO_CC_ENDPOINT_H
#include "message.h"
#include "socket.h"
#include "cause.h"
/* special osmo-cc error codes */
#define OSMO_CC_RC_SEE_ERRNO -1
#define OSMO_CC_RC_VERSION_MISMATCH 1
#define OSMO_CC_ATTACH_TIMER 2
/* call control state */
enum osmo_cc_state {
OSMO_CC_STATE_IDLE = 0,
/* call states */
OSMO_CC_STATE_INIT_OUT, /* outgoing CC-SETUP-REQ sent */
OSMO_CC_STATE_INIT_IN, /* incoming CC-SETUP-IND received */
OSMO_CC_STATE_OVERLAP_OUT, /* received CC-SETUP-ACK-IND on outgoing call */
OSMO_CC_STATE_OVERLAP_IN, /* sent CC-SETUP-ACK-REQ on incoming call */
OSMO_CC_STATE_PROCEEDING_OUT, /* received CC-PROC-IND on outgoing call */
OSMO_CC_STATE_PROCEEDING_IN, /* sent CC-PROC-REQ on incoming call */
OSMO_CC_STATE_ALERTING_OUT, /* received CC-ALERT-IND on outgoing call */
OSMO_CC_STATE_ALERTING_IN, /* sent CC-ALERT-REQ on incoming call */
OSMO_CC_STATE_CONNECTING_OUT, /* received CC-SETUP-CNF on outgoing call */
OSMO_CC_STATE_CONNECTING_IN, /* sent CC-SETUP-RSP on incoming call */
OSMO_CC_STATE_ACTIVE, /* received or sent CC-SETUP-COMPL-* */
OSMO_CC_STATE_DISCONNECTING_OUT, /* sent CC-DISC-REQ */
OSMO_CC_STATE_DISCONNECTING_IN, /* received CC-DISC-IND */
OSMO_CC_STATE_DISC_COLLISION, /* received CC-DISC-IND after sending CC-DISC_REQ */
OSMO_CC_STATE_RELEASING_OUT, /* sent CC-REL-REQ */
/* attachment states */
OSMO_CC_STATE_ATTACH_SENT, /* outgoing CC-ATT-REQ sent to socket */
OSMO_CC_STATE_ATTACH_OUT, /* received CC-ATT-RSP on outgoing socket */
OSMO_CC_STATE_ATTACH_WAIT, /* wait for outgoing attachment after failure */
OSMO_CC_STATE_ATTACH_IN, /* incoming CC-ATT-REQ received from socket*/
};
/* sample type */
typedef int16_t osmo_cc_sample_t;
#define OSMO_CC_SAMPLE_MILLIWATT 23170 /* peak sine at -3 dB of full sample range */
#define OSMO_CC_SAMPLE_SPEECH 3672 /* peak speech at -16 dB of milliwatt */
#define OSMO_CC_SAMPLE_MIN -32768 /* lowest level */
#define OSMO_CC_SAMPLE_MAX 32767 /* highest level */
#include "session.h"
struct osmo_cc_call;
typedef struct osmo_cc_screen_list {
struct osmo_cc_screen_list *next;
int has_from_type;
uint8_t from_type;
int has_from_present;
uint8_t from_present;
char from[128];
int has_to_type;
uint8_t to_type;
int has_to_present;
uint8_t to_present;
char to[128];
} osmo_cc_screen_list_t;
/* endpoint instance */
typedef struct osmo_cc_endpoint {
struct osmo_cc_endpoint *next;
void *priv;
void (*ll_msg_cb)(struct osmo_cc_endpoint *ep, uint32_t callref, osmo_cc_msg_t *msg);
void (*ul_msg_cb)(struct osmo_cc_call *call, osmo_cc_msg_t *msg);
osmo_cc_msg_list_t *ll_queue; /* messages towards lower layer */
struct osmo_cc_call *call_list;
const char *local_name; /* name of interface */
const char *local_address; /* host+port */
const char *local_host;
uint16_t local_port;
const char *remote_address; /* host+port */
const char *remote_host;
uint16_t remote_port;
uint8_t serving_location;
osmo_cc_socket_t os;
osmo_cc_screen_list_t *screen_calling_in;
osmo_cc_screen_list_t *screen_called_in;
osmo_cc_screen_list_t *screen_calling_out;
osmo_cc_screen_list_t *screen_called_out;
int remote_auto; /* automatic remote address */
struct timer attach_timer; /* timer to retry attachment */
osmo_cc_session_config_t session_config; /* SDP/RTP default configuration */
} osmo_cc_endpoint_t;
extern osmo_cc_endpoint_t *osmo_cc_endpoint_list;
/* call process */
typedef struct osmo_cc_call {
struct osmo_cc_call *next;
osmo_cc_endpoint_t *ep;
enum osmo_cc_state state;
int lower_layer_released; /* when lower layer sent release, while upper layer gets a disconnect */
int upper_layer_released; /* when upper layer sent release, while lower layer gets a disconnect */
uint32_t callref;
osmo_cc_msg_list_t *sock_queue; /* messages from socket */
const char *attached_host; /* host and port from remote peer that attached to us */
uint16_t attached_port;
const char *attached_name; /* interface name from remote peer that attached to us */
} osmo_cc_call_t;
/* returns 0 if ok
* returns <0 for error as indicated
* returns >=1 to indicate osmo-cc error code
*/
void osmo_cc_help(void);
int osmo_cc_new(osmo_cc_endpoint_t *ep, const char *version, const char *name, uint8_t serving_location, void (*ll_msg_cb)(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg), void (*ul_msg_cb)(osmo_cc_call_t *call, osmo_cc_msg_t *msg), void *priv, int argc, const char *argv[]);
void osmo_cc_delete(struct osmo_cc_endpoint *ep);
int osmo_cc_handle(void);
osmo_cc_call_t *osmo_cc_call_by_callref(osmo_cc_endpoint_t *ep, uint32_t callref);
osmo_cc_call_t *osmo_cc_get_attached_interface(osmo_cc_endpoint_t *ep, const char *interface);
void osmo_cc_ll_msg(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg);
void osmo_cc_ul_msg(void *priv, uint32_t callref, osmo_cc_msg_t *msg);
osmo_cc_call_t *osmo_cc_call_new(osmo_cc_endpoint_t *ep);
void osmo_cc_call_delete(struct osmo_cc_call *call);
enum osmo_cc_session_addrtype osmo_cc_address_type(const char *address);
const char *osmo_cc_host_of_address(const char *address);
const char *osmo_cc_port_of_address(const char *address);
#include "rtp.h"
#include "sdp.h"
#include "screen.h"
#endif /* OSMO_CC_ENDPOINT_H */

Some files were not shown because too many files have changed in this diff Show More