diff --git a/src/isdn/Makefile.am b/src/isdn/Makefile.am index d319da0..85300fa 100644 --- a/src/isdn/Makefile.am +++ b/src/isdn/Makefile.am @@ -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,6 +7,8 @@ osmo_cc_misdn_endpoint_SOURCES = \ ie.c \ dss1.c \ isdn.c \ + ph_socket.c \ + ph_driver.c \ main.c osmo_cc_misdn_endpoint_LDADD = \ @@ -18,5 +20,8 @@ osmo_cc_misdn_endpoint_LDADD = \ ../libjitter/libjitter.a \ ../libosmocc/libosmocc.a \ ../libg711/libg711.a \ - -lmisdn + ../libmisdn/libmisdn.a \ + ../libmisdnuser/libmisdnuser.a + +# -lmisdn diff --git a/src/isdn/isdn.c b/src/isdn/isdn.c index 04d2d13..ea9acf6 100644 --- a/src/isdn/isdn.c +++ b/src/isdn/isdn.c @@ -37,6 +37,7 @@ #endif #include #include +#include "../libmisdn/socket.h" #ifndef container_of #define container_of(ptr, type, member) ({ \ @@ -655,16 +656,18 @@ void isdn_destroy(isdn_t *isdn_ep) { struct msn_list *msn; + /* destroy all calls */ + while (isdn_ep->call_list) + call_destroy(isdn_ep->call_list); + /* remove stack instance */ isdn_close(isdn_ep); /* close mISDN socket */ - if (isdn_ep->socket > 0) - close(isdn_ep->socket); - - /* destroy all calls */ - while (isdn_ep->call_list) - call_destroy(isdn_ep->call_list); + if (isdn_ep->l2sock) + close(isdn_ep->l2sock); + if (isdn_ep->l2inst) + mISDN_base_release(isdn_ep->l2inst); /* free msn list */ while (isdn_ep->msn_list) { @@ -683,17 +686,30 @@ void isdn_destroy(isdn_t *isdn_ep) } /* initialization and configuration to isdn interface instance */ -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 tx_gain, int rx_gain, const char *pipeline, int dtmf, int local_tones, int serving_location) { int rc; + void *mui; - /* open mISDN socket */ - rc = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); - if (rc < 0) { - PDEBUG(DISDN, DEBUG_ERROR, "Cannot open mISDN due to errno=%d:%s. (Does your Kernel support socket based mISDN? Protocol family is %d.)\n", errno, strerror(errno), PF_ISDN); - return -errno; + if (!ph_socket) { + /* open mISDN socket */ + rc = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); + if (rc < 0) { + PDEBUG(DISDN, DEBUG_ERROR, "Cannot open mISDN due to errno=%d:%s. (Does your Kernel support socket based mISDN? Protocol family is %d.)\n", errno, strerror(errno), PF_ISDN); + return -errno; + } + isdn_ep->l2sock = rc; + } else { + isdn_ep->ph_socket = ph_socket; + /* open mISDN user space */ + rc = mISDN_base_create(&mui, ISDN_P_BASE); + if (rc < 0) { + PDEBUG(DISDN, DEBUG_ERROR, "Cannot open mISDN due to errno=%d:%s. (Please fix!)\n", errno, strerror(errno)); + return -errno; + } + isdn_ep->l2inst = mui; + portname = "0"; } - isdn_ep->socket = rc; /* store settings */ isdn_ep->law = law; @@ -907,38 +923,40 @@ static int bchannel_create(isdn_t *isdn_ep, int index) memset(&addr, 0, sizeof(addr)); - if (isdn_ep->b_sock[index] > 0) { - PDEBUG(DISDN, DEBUG_ERROR, "Socket already created for index %d\n", index); - return -EIO; + if (isdn_ep->l2sock) { + if (isdn_ep->b_sock[index] > 0) { + PDEBUG(DISDN, DEBUG_ERROR, "Socket already created for index %d\n", index); + return -EIO; + } + + /* open socket */ + if (isdn_ep->b_mode[index] == B_MODE_HDLC) { + PDEBUG(DISDN, DEBUG_NOTICE, "Use B-Channel with HDLC l!!!\n"); + } + rc = socket(PF_ISDN, SOCK_DGRAM, (isdn_ep->b_mode[index] == B_MODE_HDLC) ? ISDN_P_B_L2DSPHDLC : ISDN_P_B_L2DSP); + if (rc < 0) { + PDEBUG(DISDN, DEBUG_ERROR, "Failed to open bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDN_dsp.ko?\n", index); + return(0); + } + isdn_ep->b_sock[index] = rc; + + /* set nonblocking io */ + flags = fcntl(isdn_ep->b_sock[index], F_GETFL); + flags |= O_NONBLOCK; + fcntl(isdn_ep->b_sock[index], F_SETFL, flags); + + /* bind socket to bchannel */ + addr.family = AF_ISDN; + addr.dev = isdn_ep->portnum; + addr.channel = channel; + rc = bind(isdn_ep->b_sock[index], (struct sockaddr *)&addr, sizeof(addr)); + if (rc < 0) { + PDEBUG(DISDN, DEBUG_ERROR, "Failed to bind bchannel-socket for index %d with mISDN-DSP layer (errno=%d). Did you load mISDN_dsp.ko?\n", index, errno); + close(isdn_ep->b_sock[index]); + return -errno; + } } - /* open socket */ - if (isdn_ep->b_mode[index] == B_MODE_HDLC) { - PDEBUG(DISDN, DEBUG_NOTICE, "Use B-Channel with HDLC l!!!\n"); - } - rc = socket(PF_ISDN, SOCK_DGRAM, (isdn_ep->b_mode[index] == B_MODE_HDLC) ? ISDN_P_B_L2DSPHDLC : ISDN_P_B_L2DSP); - if (rc < 0) { - PDEBUG(DISDN, DEBUG_ERROR, "Failed to open bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDN_dsp.ko?\n", index); - return(0); - } - isdn_ep->b_sock[index] = rc; - - /* set nonblocking io */ - flags = fcntl(isdn_ep->b_sock[index], F_GETFL); - flags |= O_NONBLOCK; - fcntl(isdn_ep->b_sock[index], F_SETFL, flags); - - /* bind socket to bchannel */ - addr.family = AF_ISDN; - addr.dev = isdn_ep->portnum; - addr.channel = channel; - rc = bind(isdn_ep->b_sock[index], (struct sockaddr *)&addr, sizeof(addr)); - if (rc < 0) { - PDEBUG(DISDN, DEBUG_ERROR, "Failed to bind bchannel-socket for index %d with mISDN-DSP layer (errno=%d). Did you load mISDN_dsp.ko?\n", index, errno); - close(isdn_ep->b_sock[index]); - return -errno; - } - PDEBUG(DISDN, DEBUG_DEBUG, "created socket #%d for B-channel %d\n", isdn_ep->b_sock[index], channel); return 0; @@ -951,15 +969,25 @@ static void bchannel_activate(isdn_t *isdn_ep, int index, int activate, int time int channel = index + 1 + (index >= 15); int rc; + if (isdn_ep->l2sock) { + if (isdn_ep->b_sock[index] <= 0) + return; - if (isdn_ep->b_sock[index] <= 0) - return; + act.prim = (activate) ? PH_ACTIVATE_REQ : PH_DEACTIVATE_REQ; + act.id = 0; + rc = sendto(isdn_ep->b_sock[index], &act, MISDN_HEADER_LEN, 0, NULL, 0); + if (rc < 0) + PDEBUG(DISDN, DEBUG_ERROR, "Failed to send to socket #%d, of B-channel %d\n", isdn_ep->b_sock[index], channel); + } - act.prim = (activate) ? PH_ACTIVATE_REQ : PH_DEACTIVATE_REQ; - act.id = 0; - rc = sendto(isdn_ep->b_sock[index], &act, MISDN_HEADER_LEN, 0, NULL, 0); - if (rc < 0) - PDEBUG(DISDN, DEBUG_ERROR, "Failed to send to socket #%d, of B-channel %d\n", isdn_ep->b_sock[index], channel); + if (isdn_ep->l2inst) { + uint8_t mode = PH_MODE_TRANS; + if (isdn_ep->b_mode[index] == B_MODE_HDLC) { + PDEBUG(DISDN, DEBUG_NOTICE, "Use B-Channel with HDLC!!!\n"); + mode = PH_MODE_HDLC; + } + ph_socket_tx_msg(isdn_ep->ph_socket, channel, (activate) ? PH_PRIM_ACT_REQ : PH_PRIM_DACT_REQ, &mode, 1); + } PDEBUG(DISDN, DEBUG_DEBUG, "%s B-channel %d%s\n", (activate) ? "activating" : "deactivating", channel, (timeout) ? " after timeout recovery" : ""); @@ -1077,13 +1105,19 @@ static void bchannel_destroy(isdn_t *isdn_ep, int index) { int channel = index + 1 + (index >= 15); - if (isdn_ep->b_sock[index] <= 0) - return; + if (isdn_ep->l2sock) { + if (isdn_ep->b_sock[index] <= 0) + return; - PDEBUG(DISDN, DEBUG_DEBUG, "destroyed socket #%d for B-channel %d\n", isdn_ep->b_sock[index], channel); + PDEBUG(DISDN, DEBUG_DEBUG, "destroyed socket #%d for B-channel %d\n", isdn_ep->b_sock[index], channel); - close(isdn_ep->b_sock[index]); - isdn_ep->b_sock[index] = 0; + close(isdn_ep->b_sock[index]); + isdn_ep->b_sock[index] = 0; + } + + if (isdn_ep->l2inst) { + PDEBUG(DISDN, DEBUG_DEBUG, "destroyed B-channel %d\n", channel); + } } /* @@ -1287,8 +1321,8 @@ void bchannel_event(isdn_t *isdn_ep, int index, int event) static void bchannel_receive(isdn_t *isdn_ep, int index, struct mISDNhead *hh, unsigned char *data, int len); -/* handle frames from B-channel */ -static int bchannel_work(isdn_t *isdn_ep, int index) +/* handle frames from B-channel (kernel socket) */ +static int bchannel_kernel_sock_work(isdn_t *isdn_ep, int index) { int channel = index + 1 + (index >= 15); unsigned char buffer[2048+MISDN_HEADER_LEN]; @@ -1320,7 +1354,7 @@ static int bchannel_work(isdn_t *isdn_ep, int index) if (isdn_ep->b_call[index]) bchannel_receive(isdn_ep, index, hh, buffer + MISDN_HEADER_LEN, rc - MISDN_HEADER_LEN); else - PDEBUG(DISDN, DEBUG_DEBUG, "b-channel is not associated to an ISDNPort (channel %d), ignoring.\n", channel); + PDEBUG(DISDN, DEBUG_DEBUG, "b-channel is not associated to a call (channel %d), ignoring.\n", channel); break; case PH_ACTIVATE_IND: @@ -1346,6 +1380,34 @@ static int bchannel_work(isdn_t *isdn_ep, int index) return 1; } +/* handle data frames from B-channel (ph_socket) */ +void bchannel_ph_sock_receive(void *priv, int channel, uint8_t prim, uint8_t *data, int length) +{ + isdn_t *isdn_ep = (isdn_t *)priv; + int index = channel - 1 - (channel > 16); + + if (index < 0 || index > 127) + return; + + switch (prim) { + case PH_PRIM_DATA_IND: + if (isdn_ep->b_call[index]) { + struct mISDNhead hh = { .prim = PH_DATA_IND }; + bchannel_receive(isdn_ep, index, &hh, data, length); + } else + PDEBUG(DISDN, DEBUG_DEBUG, "b-channel is not associated to a call (channel %d), ignoring.\n", channel); + break; + case PH_PRIM_ACT_IND: + PDEBUG(DISDN, DEBUG_DEBUG, "PH_PRIM_ACT_IND: bchannel is now activated (channel %d).\n", channel); + bchannel_event(isdn_ep, index, B_EVENT_ACTIVATED); + break; + case PH_PRIM_DACT_IND: + PDEBUG(DISDN, DEBUG_DEBUG, "PH_PRIM_DACT_IND: bchannel is now deactivated (channel %d).\n", channel); + bchannel_event(isdn_ep, index, B_EVENT_DEACTIVATED); + break; + } +} + static void b_timer_timeout(struct timer *timer) { struct b_timer_inst *ti = timer->priv; @@ -1516,11 +1578,14 @@ static void send_to_rtp(call_t *call, unsigned char *data, int len) if (call->b_index >= 0) { unsigned char buf[MISDN_HEADER_LEN + len]; struct mISDNhead *frm = (struct mISDNhead *)buf; - int rc; + int rc = 0; memcpy(buf + MISDN_HEADER_LEN, data, len); frm->prim = PH_DATA_REQ; frm->id = 0; - rc = send(call->isdn_ep->b_sock[call->b_index], buf, MISDN_HEADER_LEN + len, 0); + if (call->isdn_ep->ph_socket) + ph_socket_tx_msg(call->isdn_ep->ph_socket, call->b_channel, PH_PRIM_DATA_REQ, buf + MISDN_HEADER_LEN, len); + else + rc = send(call->isdn_ep->b_sock[call->b_index], buf, MISDN_HEADER_LEN + len, 0); if (rc < 0) PDEBUG(DISDN, DEBUG_ERROR, "write error B-channel data (socket #%d errno=%d:%s)\n", call->isdn_ep->b_sock[call->b_index], errno, strerror(errno)); } @@ -1629,11 +1694,14 @@ void bchannel_send(struct osmo_cc_session_codec *codec, uint16_t __attribute__(( if (call->b_index >= 0) { unsigned char buf[MISDN_HEADER_LEN + len]; struct mISDNhead *frm = (struct mISDNhead *)buf; - int rc; + int rc = 0; memcpy(buf + MISDN_HEADER_LEN, data, len); frm->prim = PH_DATA_REQ; frm->id = 0; - rc = send(call->isdn_ep->b_sock[call->b_index], buf, MISDN_HEADER_LEN + len, 0); + if (call->isdn_ep->ph_socket) + ph_socket_tx_msg(call->isdn_ep->ph_socket, call->b_channel, PH_PRIM_DATA_REQ, buf + MISDN_HEADER_LEN, len); + else + rc = send(call->isdn_ep->b_sock[call->b_index], buf, MISDN_HEADER_LEN + len, 0); if (rc < 0) PDEBUG(DISDN, DEBUG_ERROR, "write error B-channel data (socket #%d errno=%d:%s)\n", call->isdn_ep->b_sock[call->b_index], errno, strerror(errno)); } @@ -1667,7 +1735,7 @@ int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3 return 0; } -int mISDN_getportbyname(int sock, int cnt, const char *portname) +int mISDN_getportbyname(isdn_t *isdn_ep, int cnt, const char *portname) { struct mISDN_devinfo devinfo; int port = 0, rc; @@ -1677,7 +1745,10 @@ int mISDN_getportbyname(int sock, int cnt, const char *portname) /* resolve name */ while (port < cnt) { devinfo.id = port; - rc = ioctl(sock, IMGETDEVINFO, &devinfo); + if (isdn_ep->l2sock) + rc = ioctl(isdn_ep->l2sock, IMGETDEVINFO, &devinfo); + if (isdn_ep->l2inst) + rc = mISDN_base_ioctl(isdn_ep->l2inst, IMGETDEVINFO, &devinfo); if (rc < 0) return rc; if (!strcasecmp(devinfo.name, portname)) @@ -1706,7 +1777,7 @@ int isdn_open(isdn_t *isdn_ep) int nt, te; struct mISDN_devinfo devinfo; unsigned int protocol, prop; - int rc; + int rc = 0; /* queue must be initializes, because l3-thread may send messages during open_layer3() */ mqueue_init(&isdn_ep->upqueue); @@ -1725,7 +1796,10 @@ int isdn_open(isdn_t *isdn_ep) memset(&devinfo, 0, sizeof(devinfo)); /* check port counts */ - rc = ioctl(isdn_ep->socket, IMGETCOUNT, &cnt); + if (isdn_ep->l2sock) + rc = ioctl(isdn_ep->l2sock, IMGETCOUNT, &cnt); + if (isdn_ep->l2inst) + rc = mISDN_base_ioctl(isdn_ep->l2inst, IMGETCOUNT, &cnt); if (rc < 0) { PDEBUG(DISDN, DEBUG_ERROR, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed errno = %d)\n", errno); goto error; @@ -1736,7 +1810,7 @@ int isdn_open(isdn_t *isdn_ep) goto error; } if (portnum < 0) { - portnum = mISDN_getportbyname(isdn_ep->socket, cnt, portname); + portnum = mISDN_getportbyname(isdn_ep, cnt, portname); if (portnum < 0) { PDEBUG(DISDN, DEBUG_ERROR, "Port name '%s' not found, use 'misdn_info' tool to list all existing ports.\n", portname); goto error; @@ -1751,7 +1825,10 @@ int isdn_open(isdn_t *isdn_ep) /* get port attributes */ pri = bri = nt = te = 0; devinfo.id = portnum; - rc = ioctl(isdn_ep->socket, IMGETDEVINFO, &devinfo); + if (isdn_ep->l2sock) + rc = ioctl(isdn_ep->l2sock, IMGETDEVINFO, &devinfo); + if (isdn_ep->l2inst) + rc = mISDN_base_ioctl(isdn_ep->l2inst, IMGETDEVINFO, &devinfo); if (rc < 0) { PDEBUG(DISDN, DEBUG_ERROR, "Cannot get device information for port %d. (ioctl IMGETDEVINFO failed errno=%d)\n", portnum, errno); goto error; @@ -1861,6 +1938,8 @@ int isdn_open(isdn_t *isdn_ep) } isdn_ep->b_num = devinfo.nrbchan; + if (!isdn_ep->b_num) + isdn_ep->b_num = (pri) ? 30 : 2; isdn_ep->portnum = portnum; if (isdn_ep->portname) free(isdn_ep->portname); @@ -1888,7 +1967,8 @@ int isdn_open(isdn_t *isdn_ep) if (isdn_ep->ntmode && !isdn_ep->ptp) isdn_ep->l2link = 1; - PDEBUG(DISDN, DEBUG_DEBUG, "using 'mISDN_dsp.o' module\n"); + if (isdn_ep->l2sock) + PDEBUG(DISDN, DEBUG_DEBUG, "using 'mISDN_dsp.o' module\n"); return 0; @@ -2064,7 +2144,7 @@ void isdn_bchannel_work(isdn_t *isdn_ep) for (i = 0; i < isdn_ep->b_num; i++) { do { if (isdn_ep->b_sock[i] > 0) - w = bchannel_work(isdn_ep, i); + w = bchannel_kernel_sock_work(isdn_ep, i); else w = 0; } while (w); diff --git a/src/isdn/isdn.h b/src/isdn/isdn.h index 8abc49d..ade717e 100644 --- a/src/isdn/isdn.h +++ b/src/isdn/isdn.h @@ -5,6 +5,7 @@ #include "../libosmocc/helper.h" #include "../libsample/sample.h" #include "../libjitter/jitter.h" +#include "ph_socket.h" #define B_MODE_TRANSPARENT 0 #define B_MODE_HDLC 1 @@ -91,7 +92,9 @@ typedef struct isdn { int pri; int l1hold; int l2hold; - int socket; + int l2sock; + void *l2inst; + ph_socket_t *ph_socket; pthread_mutex_t upqueue_lock; struct mqueue upqueue; int upqueue_initialized; @@ -180,7 +183,7 @@ 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 tx_gain, int rx_gain, const char *pipeline, int dtmf, int local_tones, int serving_location); 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); diff --git a/src/isdn/main.c b/src/isdn/main.c index 2ec711b..24c820a 100644 --- a/src/isdn/main.c +++ b/src/isdn/main.c @@ -30,12 +30,15 @@ #include #include "isdn.h" #include "dss1.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; @@ -72,7 +75,10 @@ static void print_help() printf(" --ulaw\n"); printf(" Use U-LAW for b-channel coding instead of alaw.\n"); printf(" -p --port | \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 \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"); @@ -149,6 +155,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); @@ -193,6 +200,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; @@ -295,7 +307,11 @@ 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; g711_init(); @@ -314,16 +330,42 @@ int main(int argc, char *argv[]) if (argi <= 0) return argi; - /* misdn init and debug */ - rc = check_mISDN_dsp(); - if (rc) + 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; + } + + /* check for DSP (kernel only) */ + if (misdn_kernel) { + rc = check_mISDN_dsp(); + if (rc) + 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); + /* 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, tx_gain, rx_gain, pipeline, dtmf, local_tones, serving_location); if (rc) { PDEBUG(DISDN, DEBUG_ERROR, "mISDN initializing failed!\n"); goto error; @@ -335,7 +377,10 @@ int main(int argc, char *argv[]) goto error; } - 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); + while ((p = strchr(portname, '/'))) + portname = p + 1; + + rc = osmo_cc_new(&isdn_ep->cc_ep, OSMO_CC_VERSION, portname, serving_location, cc_message, NULL, isdn_ep, cc_argc, cc_argv); if (rc < 0) goto error; @@ -349,10 +394,19 @@ int main(int argc, char *argv[]) process_timer(); isdn_bchannel_work(isdn_ep); isdn_rtp_work(isdn_ep); + if (misdn_user) { + /* run workers of mISDN stacks in user space */ + mISDN_work(); + } do { w = 0; w |= osmo_cc_handle(); w |= isdn_dchannel_work(isdn_ep); + if (misdn_user) { + /* run workers of mISDN stacks in user space */ + w |= ph_socket_work(&ph_drv.ph_socket); + w |= work_layer3(isdn_ep->ml3); + } } while (w); usleep(1000); } @@ -372,6 +426,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; diff --git a/src/isdn/ph_driver.c b/src/isdn/ph_driver.c new file mode 100644 index 0000000..6e5d632 --- /dev/null +++ b/src/isdn/ph_driver.c @@ -0,0 +1,312 @@ +/* ph-socket driver for user space mISDN + * + * (C) 2020 by Andreas Eversberg + * 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 . + */ + + +#include +#include +#include "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; +} + +/* 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 ret = 0; + struct dchannel *dch; + + + memset(drv, 0, sizeof(*drv)); + drv->priv = priv; + drv->pri = pri; + drv->nt = nt; + + /* socket client */ + ph_socket_init(&drv->ph_socket, drv, socket_name, 0); + + /* allocate dchannel structure */ + dch = kzalloc(sizeof(*dch), GFP_KERNEL); + if (!dch) { + ret = -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 */ + ret = mISDN_register_device(&dch->dev, NULL, socket_name); + if (ret) { + kfree(dch); + return ret; + } + + /* link */ + drv->dch = dch; + + return 0; + +error: + exit_ph_socket_driver(drv); + + return ret; +} + +/* 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 */ +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_ENABLE: + printk(KERN_DEBUG "PH-SOCKET Interface available\n"); + drv->enabled = 1; + break; + case PH_CTRL_DISABLE: + 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; + default: + printk(KERN_ERR "%s: Rejecting unknown message 0x%02x from PH-socket!\n", __func__, prim); + } +} + diff --git a/src/isdn/ph_driver.h b/src/isdn/ph_driver.h new file mode 100644 index 0000000..08b321f --- /dev/null +++ b/src/isdn/ph_driver.h @@ -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); + diff --git a/src/libmisdn/core.c b/src/libmisdn/core.c index f5d188f..618004d 100644 --- a/src/libmisdn/core.c +++ b/src/libmisdn/core.c @@ -209,7 +209,7 @@ get_free_devid(void) int mISDN_register_device(struct mISDNdevice *dev, - void __attribute__((unused)) *parent, char *name) + void __attribute__((unused)) *parent, const char *name) { int err; diff --git a/src/libmisdn/mISDNif.h b/src/libmisdn/mISDNif.h index be66933..b99ccc1 100644 --- a/src/libmisdn/mISDNif.h +++ b/src/libmisdn/mISDNif.h @@ -627,7 +627,7 @@ _queue_data(struct mISDNchannel *ch, u_int prim, /* global register/unregister functions */ extern int mISDN_register_device(struct mISDNdevice *, - void *parent, char *name); + 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 *);