Home of libcapi20 based Linux programs implementing some type of ISDN based application.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
capi-hacks/capi.c

330 lines
8.0 KiB

/* CAPI based ISDN testing utility.
*
* (C) 2022 by Harald Welte <laforge@gnumonks.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <malloc.h>
#include <errno.h>
#include <talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <capi_debug.h>
#include "capiconn.h"
#include "bchan.h"
#define CM(x) (1<<(x))
#define CIPMASK_ALL 0x1FFF03FF
#define CIPMASK_VOICE (CM(1)|CM(4)|CM(5)|CM(16)|CM(26))
#define CIPMASK_DATA (CM(2)|CM(3))
void *g_ctx;
struct capi_inst {
unsigned int applid;
capiconn_context *cc_ctx;
struct osmo_fd ofd;
};
static const char *conninfo(capi_connection *p)
{
static char buf[1024];
capi_conninfo *cp = capiconn_getinfo(p);
char *callingnumber = "";
char *callednumber = "";
if (cp->callingnumber && cp->callingnumber[0] > 2)
callingnumber = (char *) cp->callingnumber+3;
if (cp->callednumber && cp->callednumber[0] > 1)
callednumber = (char *) cp->callednumber+2;
snprintf(buf, sizeof(buf),
"\"%s\" -> \"%s\" %s (pcli=0x%x/ncci=0x%x)",
callingnumber, callednumber,
cp->isincoming ? "incoming" : "outgoing",
cp->plci, cp->ncci
);
buf[sizeof(buf)-1] = 0;
return buf;
}
/***********************************************************************
* capiconn call-backs
***********************************************************************/
static void *cb_malloc(size_t size)
{
return talloc_size(g_ctx, size);
}
static void cb_free(void *buf)
{
talloc_free(buf);
}
static void cb_disconnected(capi_connection *cp, int localdisconnect, unsigned reason, unsigned reason_b3)
{
struct call_state *cst;
LOGP(DCAPI, LOGL_DEBUG, "%s(local=%d, reason=0x%04x, reason_b3=0x%04x)\n", __func__, localdisconnect, reason, reason_b3);
cst = capiconn_getpriv(cp);
if (!cst)
return;
if (cst->bch && cst->bch->ops.fini)
cst->bch->ops.fini(cst);
talloc_free(cst);
}
static void cb_incoming(capi_connection *cp, unsigned contr, unsigned cipvalue, char *called, char *calling)
{
struct bchan_handler *bch;
int rc;
/* we should call capiconn_{accept,ignore,reject} */
LOGP(DCAPI, LOGL_INFO, "incoming call: %s (CIP=%u) %s -> %s\n",
conninfo(cp), cipvalue, calling, called);
bch = bchan_handler_for_call(cipvalue, called);
if (bch) {
struct call_state *cst = talloc_zero(g_ctx, struct call_state);
if (!cst) {
capiconn_reject(cp);
return;
}
cst->bch = bch;
cst->cc = cp;
if (bch->ops.init) {
rc = bch->ops.init(cst);
if (rc < 0) {
talloc_free(cst);
capiconn_reject(cp);
}
}
rc = capiconn_accept(cp, bch->cfg.proto.b1, bch->cfg.proto.b2, bch->cfg.proto.b3,
0, 0, 0, (uint8_t *)bch->cfg.ncpi);
if (rc != CAPICONN_OK) {
if (cst->bch->ops.fini)
cst->bch->ops.fini(cst);
talloc_free(cst);
capiconn_reject(cp);
}
capiconn_setpriv(cp, cst);
LOGP(DCAPI, LOGL_NOTICE, "call CIP=%u MSN=%s routed to handler '%s'\n", cipvalue, called,
bch->name);
} else {
LOGP(DCAPI, LOGL_NOTICE, "call CIP=%u MSN=%s has no handler, ignoring\n", cipvalue, called);
capiconn_ignore(cp);
}
}
static void cb_connected(capi_connection *cp, _cstruct NCPI)
{
struct call_state *cst = capiconn_getpriv(cp);
struct bchan_handler *bch = cst->bch;
LOGP(DCAPI, LOGL_NOTICE, "connected: %s\n", conninfo(cp));
if (bch->ops.init) {
int rc = bch->ops.init(cst);
if (rc < 0)
capiconn_disconnect(cp, NULL);
}
}
/* user plane data received */
static void cb_datareceived(capi_connection *cp, unsigned char *data, unsigned int len)
{
struct call_state *cst = capiconn_getpriv(cp);
LOGP(DCAPI, LOGL_DEBUG, "%s(%p, %u)\n", __func__, data, len);
cst->bch->ops.rx_data(cst, data, len);
}
/* sent user plane data was confirmed */
static void cb_datasent(capi_connection *cp, unsigned char *data)
{
LOGP(DCAPI, LOGL_DEBUG, "%s(%p)\n", __func__, data);
}
/* capiconn wants to issue a CAPI_PUT_MESSAGE */
static void cb_put_message(unsigned int appid, unsigned char *msg)
{
int rc;
const char *str = capi20_cmd2str(CAPIMSG_COMMAND(msg), CAPIMSG_SUBCOMMAND(msg));
LOGP(DCAPI, LOGL_DEBUG, "Tx to CAPI: %s\n", str);
rc = capi20_put_message(appid, msg);
if (rc)
fprintf(stderr, "capi20_put_message: %s\n", capi_info2str(rc));
}
static void cb_debugmsg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
osmo_vlogp(DCAPI, LOGL_DEBUG, __FILE__, __LINE__, 0, fmt, ap);
va_end(ap);
LOGPC(DCAPI, LOGL_DEBUG, "\n");
}
static void cb_infomsg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
osmo_vlogp(DCAPI, LOGL_INFO, __FILE__, __LINE__, 0, fmt, ap);
va_end(ap);
LOGPC(DCAPI, LOGL_INFO, "\n");
}
static void cb_errmsg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
osmo_vlogp(DCAPI, LOGL_ERROR, __FILE__, __LINE__, 0, fmt, ap);
va_end(ap);
LOGPC(DCAPI, LOGL_ERROR, "\n");
}
static capiconn_callbacks callbacks = {
.malloc = cb_malloc,
.free = cb_free,
.disconnected = cb_disconnected,
.incoming = cb_incoming,
.connected = cb_connected,
.received = cb_datareceived,
.datasent = cb_datasent,
.chargeinfo = NULL,
.capi_put_message = cb_put_message,
.debugmsg = cb_debugmsg,
.infomsg = cb_infomsg,
.errmsg = cb_errmsg,
};
/***********************************************************************
* initialization / integration
***********************************************************************/
int bchan_call_tx(struct call_state *cst, const uint8_t *data, size_t len)
{
return capiconn_send(cst->cc, (uint8_t *)data, len);
}
/* debug print callback for capi20 to log via libosmocore logging framework */
static int cb_capi_dprintf(const char *file, int line, const char *func, const char *fmt, va_list va)
{
osmo_vlogp(DLCAPI20, LOGL_DEBUG, file, line, 0, fmt, va);
return 0;
}
/* osmocom select file descriptor call-back for capi20 file descriptor */
static int capifd_cb(struct osmo_fd *ofd, unsigned int what)
{
struct capi_inst *ci = ofd->data;
int rc;
if (what & OSMO_FD_READ) {
unsigned char *msg = NULL;
rc = capi20_get_message(ci->applid, &msg);
if (rc == 0) {
const char *str = capi20_cmd2str(CAPIMSG_COMMAND(msg), CAPIMSG_SUBCOMMAND(msg));
LOGP(DCAPI, LOGL_DEBUG, "Rx from CAPI: %s\n", str);
capiconn_inject(ci->applid, msg);
}
}
return 0;
}
struct capi_inst *capi_init(void *ctx)
{
int rc;
/* libcapi20 debug log print callback */
register_dbg_vprintf(cb_capi_dprintf);
struct capi_inst *ci = talloc_zero(ctx, struct capi_inst);
if (!ci)
return NULL;
rc = capi20_register(1, 8, 1280, &ci->applid);
if (rc != 0) {
fprintf(stderr, "Error in capi_register: %s\n", capi_info2str(rc));
talloc_free(ci);
exit(1);
}
ci->cc_ctx = capiconn_getcontext(ci->applid, &callbacks);
if (!ci->cc_ctx) {
fprintf(stderr, "get_coontext\n");
capi20_release(ci->applid);
talloc_free(ci);
exit(1);
}
struct capi_contrinfo cinfo = {0, 0, 0};
capiconn_addcontr(ci->cc_ctx, 1, &cinfo);
rc = capiconn_listen(ci->cc_ctx, 1, CIPMASK_ALL, 0);
if (rc) {
fprintf(stderr, "Error in capiconn_listen: %s\n", capi_info2str(rc));
capi20_release(ci->applid);
talloc_free(ci);
exit(1);
}
osmo_fd_setup(&ci->ofd, capi20_fileno(ci->applid), OSMO_FD_READ, capifd_cb, ci, 0);
osmo_fd_register(&ci->ofd);
return ci;
}
static const struct log_info_cat log_info_cat[] = {
[DCAPI] = {
.name = "DCAPI",
.description = "ISDN CAPI Interface",
.enabled = 1,
.loglevel = LOGL_DEBUG,
},
[DLCAPI20] = {
.name = "DLCAPI",
.description = "ISDN libcapi20",
.enabled = 1,
.loglevel = LOGL_DEBUG,
},
};
static const struct log_info log_info = {
.cat = log_info_cat,
.num_cat = ARRAY_SIZE(log_info_cat),
};
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 1, "capi-test");
osmo_init_logging2(ctx, &log_info);
log_set_use_color(osmo_stderr_target, 1);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
capi_init(ctx);
bchan_route_add(-1, "142", "raw_prbs");
bchan_route_add(-1, NULL, "raw_loop");
while (1) {
osmo_select_main(0);
}
}