/* layer 1/2 handling * * (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 . */ /* Transmit algorithm: * * Transmit data is taken from jitter buffer. * * When data is received from b-channel the first time, two chunks of data are transmitted to ISDN interface in one block. * * When more data is received from b-channel, this extra chunk is transmitted to ISDN interface. * * This way the FIFO is kept with data all the time, to prevent underrund in hope that this application does not stall. * * The function that receives data and transmits data is bchannel_rx_tx(). * * The jitter of received chunk is checked and debug is displayed accordingly. * */ #include #include #include #include #include #include #include #include #include #include #include "../libdebug/debug.h" #include "../libg711/g711.h" #include "isdn.h" #include "dss1.h" #include "ie.h" #include "bridge.h" #ifndef u_char #define u_char unsigned char #endif #include #include #include "../libmisdn/socket.h" #ifndef container_of #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #endif #define B_TIMER_ACTIVATING 1 // seconds #define B_TIMER_DEACTIVATING 1 // seconds /* * Channel selection */ /* set default out_channel */ static void default_out_channel(isdn_t *isdn_ep) { struct select_channel *selchannel; selchannel = calloc(1, sizeof(struct select_channel)); if (isdn_ep->ntmode) selchannel->channel = CHANNEL_FREE; else selchannel->channel = CHANNEL_ANY; isdn_ep->out_channel = selchannel; /* additional channel selection for multipoint NT ports */ if (!isdn_ep->ptp && isdn_ep->ntmode) { selchannel->next = calloc(1, sizeof(struct select_channel)); selchannel = selchannel->next; selchannel->channel = CHANNEL_NO; // call waiting } } /* set default in_channel */ static void default_in_channel(isdn_t *isdn_ep) { struct select_channel *selchannel; selchannel = calloc(1, sizeof(struct select_channel)); selchannel->channel = CHANNEL_FREE; isdn_ep->in_channel = selchannel; } /* parse string for a positive number */ static int get_number(char *value) { int val = 0; char text[10]; val = atoi(value); sprintf(text, "%d", val); if (!strcmp(value, text)) return val; return -1; } /* remove element from buffer * and return pointer to next element in buffer */ static char *get_separated(char *buffer) { while(*buffer) { if (*buffer==',' || *buffer<=32) { /* separate */ *buffer++ = '\0'; while((*buffer>'\0' && *buffer<=32) || *buffer==',') buffer++; return(buffer); } buffer++; } return(buffer); } /* parse outgoing channel list */ static int parse_out_channel(isdn_t *isdn_ep, const char *_list) { char list[strlen(_list) + 1]; struct select_channel *selchannel, **selchannel_p = &isdn_ep->out_channel; int val; char *p, *el; strcpy(list, _list); p = list; while(*p) { el = p; p = get_separated(p); if (!strcasecmp(el, "force")) { isdn_ep->out_channel_exclusive = 1; if (isdn_ep->out_channel) { PDEBUG(DISDN, DEBUG_ERROR, "Error outgoing channel string: value 'force' may only appear as first element in list.\n"); return(-1); } } else if (!strcasecmp(el, "any")) { val = CHANNEL_ANY; goto selchannel; } else if (!strcasecmp(el, "free")) { val = CHANNEL_FREE; goto selchannel; } else if (!strcasecmp(el, "no")) { val = CHANNEL_NO; goto selchannel; } else { val = get_number(el); if (val == -1) { PDEBUG(DISDN, DEBUG_ERROR, "Error outgoing channel string: expecting a comma separated list of 'force', 'any', 'free', 'no' and any channel number.\n"); return(-1); } if (val<1 || val==16 || val>126) { PDEBUG(DISDN, DEBUG_ERROR, "Error outgoing channel string: channel '%d' out of range.\n", val); return(-1); } selchannel: /* add to select-channel list */ selchannel = calloc(1, sizeof(struct select_channel)); /* set value */ selchannel->channel = val; /* tail port */ *selchannel_p = selchannel; selchannel_p = &((*selchannel_p)->next); } } return(0); } /* parse incoming channel list */ static int parse_in_channel(isdn_t *isdn_ep, const char *_list) { char list[strlen(_list) + 1]; struct select_channel *selchannel, **selchannel_p = &isdn_ep->in_channel; int val; char *p, *el; strcpy(list, _list); p = list; while(*p) { el = p; p = get_separated(p); if (isdn_ep->in_channel) if (isdn_ep->in_channel->channel == CHANNEL_FREE) { PDEBUG(DISDN, DEBUG_ERROR, "Error incoming channel list: values behind 'free' keyword have no effect.\n"); return(-1); } if (!strcasecmp(el, "free")) { val = CHANNEL_FREE; goto selchannel; } else { val = get_number(el); if (val == -1) { PDEBUG(DISDN, DEBUG_ERROR, "Error incoming channel list: expectng a comma separated list of channel numbers and 'free'.\n"); return(-1); } if (val<1 || val==16 || val>126) { PDEBUG(DISDN, DEBUG_ERROR, "Error incoming channel list: channel '%d' out of range.\n", val); return(-1); } selchannel: /* add to select-channel list */ selchannel = calloc(1, sizeof(struct select_channel)); /* set value */ selchannel->channel = val; /* tail port */ *selchannel_p = selchannel; selchannel_p = &((*selchannel_p)->next); } } return(0); } /* hunt bchannel for incoming setup */ int hunt_bchannel_in(isdn_t *isdn_ep, int channel, int exclusive) { struct select_channel *selchannel; int i; PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (incoming call)\n"); if (exclusive<0) exclusive = 0; if (channel == CHANNEL_NO) PDEBUG(DISDN, DEBUG_DEBUG, " -> request = no-channel\n"); else if (channel > 0) PDEBUG(DISDN, DEBUG_DEBUG, " -> request = channel %d%s\n", channel, exclusive?" (forced)":""); else PDEBUG(DISDN, DEBUG_DEBUG, " -> request = any channel\n"); if (channel==CHANNEL_NO && !isdn_ep->ntmode) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = incoming call-waiting not supported for TE-mode\n"); return(-6); // channel unacceptable } if (channel <= 0) /* not given, no channel, whatever.. */ channel = CHANNEL_ANY; /* any channel */ if (isdn_ep->b_reserved >= isdn_ep->b_num) { // of out chan.. PDEBUG(DISDN, DEBUG_DEBUG, " -> result = all channels are reserved\n"); return(-34); // no channel } if (channel == CHANNEL_ANY) goto get_from_list; if (channel > 0) { /* check for given channel in selection list */ selchannel = isdn_ep->in_channel; while(selchannel) { if (selchannel->channel == channel || selchannel->channel == CHANNEL_FREE) break; selchannel = selchannel->next; } if (!selchannel) channel = 0; /* exclusive channel requests must be in the list */ if (exclusive) { /* no exclusive channel */ if (!channel) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = exclusively requested channel not in list\n"); return(-6); // channel unacceptable } /* get index for channel */ i = channel-1-(channel>=17); if (i < 0 || i >= isdn_ep->b_num || channel == 16) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = exclusively requested channel outside interface range\n"); return(-6); // channel unacceptable } /* check if busy */ if (isdn_ep->b_call[i] == NULL) goto use_channel; PDEBUG(DISDN, DEBUG_DEBUG, " -> result = exclusively requested channel is busy\n"); return(-6); // channel unacceptable } /* requested channels in list will be used */ if (channel) { /* get index for channel */ i = channel-1-(channel>=17); if (i < 0 || i >= isdn_ep->b_num || channel == 16) { PDEBUG(DISDN, DEBUG_DEBUG, " -> quested channel %d outside interface range\n", channel); } else /* if inside range (else) check if available */ if (isdn_ep->b_call[i] == NULL) goto use_channel; } /* if channel is not available or not in list, it must be searched */ get_from_list: /* check for first free channel in list */ channel = 0; selchannel = isdn_ep->in_channel; while(selchannel) { switch(selchannel->channel) { case CHANNEL_FREE: /* free channel */ PDEBUG(DISDN, DEBUG_DEBUG, " -> hunting free channel\n"); if (isdn_ep->b_reserved >= isdn_ep->b_num) break; /* all channel in use or reserved */ /* find channel */ i = 0; while(i < isdn_ep->b_num) { if (isdn_ep->b_call[i] == NULL) { channel = i+1+(i>=15); break; } i++; } break; default: PDEBUG(DISDN, DEBUG_DEBUG, " -> hunting channel %d\n", selchannel->channel); if (selchannel->channel<1 || selchannel->channel==16) break; /* invalid channels */ i = selchannel->channel-1-(selchannel->channel>=17); if (i >= isdn_ep->b_num) break; /* channel not in port */ if (isdn_ep->b_call[i] == NULL) { channel = selchannel->channel; break; } break; } if (channel) break; /* found channel */ selchannel = selchannel->next; } if (!channel) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel available\n"); return(-6); // channel unacceptable } } use_channel: PDEBUG(DISDN, DEBUG_DEBUG, " -> result = channel available\n"); PDEBUG(DISDN, DEBUG_DEBUG, " -> connect to channel %d\n", channel); return(channel); } /* hunt bchannel for outgoing setup */ int hunt_bchannel_out(isdn_t *isdn_ep, int *channel, int *exclusive) { struct select_channel *selchannel; int i; PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (outgoing call)\n"); /* see if link is up on PTP*/ if (isdn_ep->l2hold && isdn_ep->l2link<1) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = layer 2 is down\n"); return -27; } /* check for channel form selection list */ *exclusive = isdn_ep->out_channel_exclusive; *channel = 0; selchannel = isdn_ep->out_channel; while(selchannel) { switch(selchannel->channel) { case CHANNEL_FREE: /* free channel */ if (isdn_ep->b_reserved >= isdn_ep->b_num) break; /* all channel in use or reserved */ /* find channel */ i = 0; while(i < isdn_ep->b_num) { if (isdn_ep->b_call[i] == NULL) { *channel = i+1+(i>=15); PDEBUG(DISDN, DEBUG_DEBUG, " -> result = free channel %d\n", *channel); break; } i++; } if (*channel) break; PDEBUG(DISDN, DEBUG_DEBUG, " -> no free channel found\n"); break; case CHANNEL_ANY: /* don't ask for channel */ if (isdn_ep->b_reserved >= isdn_ep->b_num) { PDEBUG(DISDN, DEBUG_DEBUG, " -> cannot ask for any channel, because all channel are reserved\n"); break; /* all channel in use or reserved */ } PDEBUG(DISDN, DEBUG_DEBUG, " -> result = any channel\n"); *channel = CHANNEL_ANY; break; case CHANNEL_NO: /* call waiting */ PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel\n"); *channel = CHANNEL_NO; break; default: if (selchannel->channel<1 || selchannel->channel==16) { PDEBUG(DISDN, DEBUG_DEBUG, " -> channel %d is out of range\n", selchannel->channel); break; /* invalid channels */ } i = selchannel->channel-1-(selchannel->channel>=17); if (i >= isdn_ep->b_num) { PDEBUG(DISDN, DEBUG_DEBUG, " -> channel %d is out of range\n", selchannel->channel); break; /* channel not in port */ } if (isdn_ep->b_call[i] == NULL) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = channel %d\n", selchannel->channel); *channel = selchannel->channel; break; } break; } if (*channel) break; /* found channel */ selchannel = selchannel->next; } if (*channel) return 0; /* if channel was found, return channel */ PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel found\n"); return -34; } /* open channel of incoming setup */ int open_bchannel_in(call_t *call, int channel, int exclusive) { int rc; rc = seize_bchannel(call, channel, exclusive); if (rc < 0) return rc; bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE); return 0; } /* open channel of outgoing setup */ int open_bchannel_out(call_t *call, unsigned int cmd, int channel, int exclusive) { int rc; /* correct exclusive to 0, if no explicit channel was given */ if (exclusive < 0 || channel <= 0) exclusive = 0; /* select scenario */ if (call->b_channel && call->b_exclusive) { /*** we gave an exclusive channel (or if we are done) ***/ /* if not first reply, we are done */ if (call->state != ISDN_STATE_OUT_SETUP) return(0); PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (reply from outgoing call)\n"); PDEBUG(DISDN, DEBUG_DEBUG, " -> request = %d (forced)\n", call->b_channel); PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply = %d\n":" -> reply = (none)\n", channel); /* if give channel not accepted or not equal */ if (channel != -1 && call->b_channel != channel) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = forced channel not accepted\n"); return -44; } rc = seize_bchannel(call, channel, 1); // exclusively if (rc < 0) { PDEBUG(DISDN, DEBUG_DEBUG, "result = requested channel not available anymore\n"); return -47; } PDEBUG(DISDN, DEBUG_DEBUG, " -> result = channel was accepted\n"); /* activate our exclusive channel */ bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE); } else if (call->b_channel) { /*** we gave a non-exclusive channel ***/ /* if not first reply, we are done */ if (call->state != ISDN_STATE_OUT_SETUP) return(0); PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (reply from outgoing call)\n"); PDEBUG(DISDN, DEBUG_DEBUG, " -> request = %d (suggest)\n", call->b_channel); PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply = %d\n":" -> reply = (none)\n", channel); /* if channel was accepted as given */ if (channel==-1 || call->b_channel==channel) { call->b_exclusive = 1; // we are done /* if channel was accepted, seize_bchannel shall simply return, because given channel is already set */ rc = seize_bchannel(call, call->b_channel, 1); // exclusively if (rc < 0) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel not available\n"); return -47; } PDEBUG(DISDN, DEBUG_DEBUG, " -> result = channel was accepted as given\n"); /* activate channel accepted by remote */ bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE); return(0); } /* if channel value is faulty */ if (channel <= 0) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = illegal reply\n"); return -111; // protocol error } /* if channel was not accepted, try to get a different one */ rc = seize_bchannel(call, channel, 1); // exclusively if (rc < 0) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel not available\n"); return -47; } PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel accepted\n"); /* activate channel given by remote */ bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE); } else if (call->b_reserve) { /*** we sent 'any channel acceptable' ***/ /* if not first reply, we are done */ if (call->state != ISDN_STATE_OUT_SETUP) return(0); PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (reply from outgoing call)\n"); PDEBUG(DISDN, DEBUG_DEBUG, " -> request = any\n"); PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply = %d\n":" -> reply = (none)\n", channel); /* if no channel was replied */ if (channel <= 0) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel, protocol error\n"); return -111; // protocol error } /* we will see, if our received channel is available */ rc = seize_bchannel(call, channel, 1); // exclusively if (rc < 0) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel not available\n"); return -47; } PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel accepted\n"); /* activate channel given by remote */ bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE); } else { /*** we sent 'no channel available' ***/ /* if not the first reply, but a connect, we are forced */ if (cmd == MT_CONNECT && call->state != ISDN_STATE_OUT_SETUP) { PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (connect reply from outgoing call)\n"); PDEBUG(DISDN, DEBUG_DEBUG, " -> request = no channel\n"); PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply %d%s\n":" -> reply (none)\n", channel, exclusive?" (forced)":""); if (channel > 0) { goto use_from_connect; } rc = seize_bchannel(call, CHANNEL_ANY, 0); // any channel if (rc < 0) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel available during call-waiting\n"); return -34; } PDEBUG(DISDN, DEBUG_DEBUG, " -> result = using channel %d\n", call->b_channel); call->b_exclusive = 1; // we are done /* activate channel given by remote */ bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE); return(0); } /* if not first reply, we are done */ if (call->state != ISDN_STATE_OUT_SETUP) return(0); PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (reply from outgoing call)\n"); PDEBUG(DISDN, DEBUG_DEBUG, " -> request = no channel\n"); PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply = %d\n":" -> reply = (none)\n", channel); /* if first reply has no channel, we are done */ if (channel <= 0) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel until connect\n"); return(0); } /* we will see, if our received channel is available */ use_from_connect: rc = seize_bchannel(call, channel, exclusive); if (rc < 0) { PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel not available\n"); return -47; } PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel accepted\n"); call->b_exclusive = 1; // we are done /* activate channel given by remote */ bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE); } return(0); } /* * ISDN instance */ /* create isdn interface instance */ isdn_t *isdn_create(void) { isdn_t *isdn_ep; isdn_ep = calloc(1, sizeof(*isdn_ep)); if (!isdn_ep) { PDEBUG(DISDN, DEBUG_ERROR, "No memory!\n"); abort(); } if (pthread_mutex_init(&isdn_ep->upqueue_lock, NULL)) { PDEBUG(DISDN, DEBUG_ERROR, "Cannot init mutex!\n"); abort(); } PDEBUG(DISDN, DEBUG_DEBUG, "ISDN instance created\n"); return isdn_ep; } /* destroy isdn interface instance and free all resource */ 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->l2sock) close(isdn_ep->l2sock); if (isdn_ep->l2inst) mISDN_base_release(isdn_ep->l2inst); /* free msn list */ while (isdn_ep->msn_list) { msn = isdn_ep->msn_list; isdn_ep->msn_list = msn->next; free(msn); } pthread_mutex_destroy(&isdn_ep->upqueue_lock); timer_exit(&isdn_ep->l2establish_timer); timer_exit(&isdn_ep->clock_timer); free(isdn_ep); PDEBUG(DISDN, DEBUG_DEBUG, "ISDN instance destroyed\n"); } static void clock_timeout(void *data); /* initialization and configuration to isdn interface instance */ 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 rc; void *mui; 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"; } /* store settings */ isdn_ep->law = law; isdn_ep->portname = strdup(portname); isdn_ep->ntmode = ntmode; isdn_ep->ptp = ptp; isdn_ep->l1hold = layer1hold; isdn_ep->l2hold = layer2hold; isdn_ep->timeouts = timeouts; isdn_ep->tx_delay = tx_delay; isdn_ep->local_tones = local_tones; isdn_ep->serving_location = serving_location; /* channel selection list */ if (channel_out) { rc = parse_out_channel(isdn_ep, channel_out); if (rc < 0) return rc; } else default_out_channel(isdn_ep); if (channel_in) { rc = parse_in_channel(isdn_ep, channel_in); if (rc < 0) return rc; } else default_in_channel(isdn_ep); PDEBUG(DISDN, DEBUG_DEBUG, "ISDN instance initialized\n"); return 0; } /* add MSN number */ void isdn_add_msn(isdn_t *isdn_ep, const char *msn) { struct msn_list *m, **m_p; m = calloc(1, sizeof(*m) + strlen(msn) + 1); if (!m) { PDEBUG(DISDN, DEBUG_ERROR, "No memory!\n"); abort(); } m_p = &isdn_ep->msn_list; while (*m_p) m_p = &((*m_p)->next); *m_p = m; strcpy(m->msn, msn); PDEBUG(DISDN, DEBUG_DEBUG, "added MSDN number '%s'\n", msn); } /* * call instance */ call_t *call_create(isdn_t *isdn_ep, int direction, int channel, int exclusive, int mode) { call_t *call, **call_p; call = calloc(1, sizeof(*call)); if (!call) { PDEBUG(DISDN, DEBUG_ERROR, "No memory!\n"); abort(); } call_p = &isdn_ep->call_list; while (*call_p) call_p = &((*call_p)->next); *call_p = call; call->isdn_ep = isdn_ep; call->direction = direction; call->b_index = -1; call->b_channel = 0; call->b_exclusive = 0; call->b_reserve = 0; call->b_mode = mode; call->hold = 0; call->isdn_tone.tone = 0; /* if any channel requested by constructor */ if (channel == CHANNEL_ANY) { /* reserve channel */ call->b_reserve = 1; isdn_ep->b_reserved++; } /* reserve channel */ if (channel > 0) // only if constructor was called with a channel resevation seize_bchannel(call, channel, exclusive); PDEBUG(DISDN, DEBUG_DEBUG, "Created new call on port #%d:%s\n", isdn_ep->portnum, isdn_ep->portname); return call; } void call_create_jitter(call_t *call, int data) { isdn_t *isdn_ep = call->isdn_ep; int rc; /* allocate jitter buffer */ if (isdn_ep->tx_delay) rc = jitter_create(&call->tx_dejitter, "tx", 8000, sizeof(uint8_t), (double)isdn_ep->tx_delay / 1000.0, (double)isdn_ep->tx_delay / 1000.0 * 2.0, JITTER_FLAG_NONE); else if (data) rc = jitter_create(&call->tx_dejitter, "tx", 8000, sizeof(uint8_t), JITTER_DATA); else rc = jitter_create(&call->tx_dejitter, "tx", 8000, sizeof(uint8_t), JITTER_AUDIO); if (rc < 0) abort(); rc = jitter_create(&call->conf_dejitter, "conference", 8000, sizeof(int16_t), JITTER_AUDIO); if (rc < 0) abort(); } void call_destroy(call_t *call) { call_t **call_p; /* be sure to clean up bridge on server */ bridge_socket_client_update(call, 0); /* free jitter buffer */ jitter_destroy(&call->tx_dejitter); jitter_destroy(&call->conf_dejitter); /* remove B-channel relation */ drop_bchannel(call); /* free session description */ if (call->cc_session) osmo_cc_free_session(call->cc_session); free((char *)call->sdp); /* detach */ call_p = &call->isdn_ep->call_list; while (*call_p) { if (*call_p == call) break; call_p = &((*call_p)->next); } *call_p = call->next; free(call); PDEBUG(DISDN, DEBUG_DEBUG, "destroyed call instance\n"); } /* * bchannel handling */ /* send control information to the channel (HFC hardware bridging) */ static void im_control(int sock, uint32_t op, uint32_t channel, uint32_t p1, uint32_t p2) { struct mISDN_ctrl_req cq; int rc; if (sock < 0) return; PDEBUG(DISDN, DEBUG_DEBUG, "sending IMCTRLREQ 0x%08x, %d, 0x%02x, 0x%02x\n", op, channel, p1, p2); cq.op = op; cq.channel = channel; cq.p1 = p1; cq.p2 = p2; rc = ioctl(sock, IMCTRLREQ, &cq); if (rc < 0) PDEBUG(DISDN, DEBUG_ERROR, "Failed to send IMCTRLREQ to socket %d (errno=%d:%s)\n", sock, errno, strerror(errno)); } static int bchannel_kernel_sock_receive_cb(struct osmo_fd *ofd, unsigned int when); /* create B-channel stack */ static int bchannel_create(isdn_t *isdn_ep, int index) { int channel = index + 1 + (index >= 15); struct sockaddr_mISDN addr; int rc; memset(&addr, 0, sizeof(addr)); if (isdn_ep->l2sock) { if (isdn_ep->b_sock[index].ofd.fd > 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!!!\n"); } rc = socket(PF_ISDN, SOCK_DGRAM, (isdn_ep->b_mode[index] == B_MODE_HDLC) ? ISDN_P_B_HDLC : ISDN_P_B_RAW); 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].ofd.fd = rc; /* register */ isdn_ep->b_sock[index].isdn_ep = isdn_ep; isdn_ep->b_sock[index].index = index; isdn_ep->b_sock[index].ofd.cb = bchannel_kernel_sock_receive_cb; isdn_ep->b_sock[index].ofd.data = &isdn_ep->b_sock[index]; isdn_ep->b_sock[index].ofd.when = OSMO_FD_READ; osmo_fd_register(&isdn_ep->b_sock[index].ofd); /* bind socket to bchannel */ addr.family = AF_ISDN; addr.dev = isdn_ep->portnum; addr.channel = channel; rc = bind(isdn_ep->b_sock[index].ofd.fd, (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); /* unregister and close */ osmo_fd_unregister(&isdn_ep->b_sock[index].ofd); close(isdn_ep->b_sock[index].ofd.fd); return -errno; } } PDEBUG(DISDN, DEBUG_DEBUG, "created socket #%d for B-channel %d\n", isdn_ep->b_sock[index].ofd.fd, channel); return 0; } /* activate or deactivate B-channel */ static void bchannel_activate(isdn_t *isdn_ep, int index, int activate, int timeout) { struct mISDNhead act; int channel = index + 1 + (index >= 15); int rc; if (isdn_ep->l2sock) { if (isdn_ep->b_sock[index].ofd.fd <= 0) return; act.prim = (activate) ? PH_ACTIVATE_REQ : PH_DEACTIVATE_REQ; act.id = 0; rc = sendto(isdn_ep->b_sock[index].ofd.fd, &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].ofd.fd, 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" : ""); /* reset rx buffer */ isdn_ep->b_buffer_pos[index] = 0; } /* configure B-channel */ static void bchannel_configure(isdn_t *isdn_ep, int index) { int channel = index + 1 + (index >= 15); call_t *call; int handle; if (isdn_ep->b_sock[index].ofd.fd <= 0) return; handle = isdn_ep->b_sock[index].ofd.fd; call = isdn_ep->b_call[index]; /* set PCM bridge features */ if (call->bridge_enabled) im_control(handle, MISDN_CTRL_HFC_PCM_CONN, channel, call->bridge_slot_tx | (call->bridge_bank_tx << 8), call->bridge_slot_rx | (call->bridge_bank_rx << 8)); else im_control(handle, MISDN_CTRL_HFC_PCM_DISC, channel, 0, 0); } void bchannel_bridge(call_t *call, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank) { /* bridge enabled */ call->bridge_enabled = pcm_bridge; call->bridge_bank_tx = tx_bank; call->bridge_slot_tx = tx_slot; call->bridge_bank_rx = rx_bank; call->bridge_slot_rx = rx_slot; bchannel_configure(call->isdn_ep, call->b_index); } void bchannel_tone(call_t *call, int tone) { if (call->b_index < 0) return; int mode = call->isdn_ep->b_mode[call->b_index]; if (mode != B_MODE_TRANSPARENT) return; isdn_tone_set(&call->isdn_tone, tone); } /* destroy B-channel stack */ static void bchannel_destroy(isdn_t *isdn_ep, int index) { int channel = index + 1 + (index >= 15); if (isdn_ep->l2sock) { if (isdn_ep->b_sock[index].ofd.fd <= 0) return; PDEBUG(DISDN, DEBUG_DEBUG, "destroyed socket #%d for B-channel %d\n", isdn_ep->b_sock[index].ofd.fd, channel); /* unregister and close */ osmo_fd_unregister(&isdn_ep->b_sock[index].ofd); close(isdn_ep->b_sock[index].ofd.fd); isdn_ep->b_sock[index].ofd.fd = 0; } if (isdn_ep->l2inst) { PDEBUG(DISDN, DEBUG_DEBUG, "destroyed B-channel %d\n", channel); } } /* * bchannel procedure * ------------------ * * A bchannel goes through the following states in this order: * * - B_STATE_IDLE * No one is using the bchannel. * It is available and not linked to call instance, nor reserved. * * - B_STATE_ACTIVATING * The bchannel stack is created and an activation request is sent. * It MAY be linked to a call, but already unlinked due to call release. * * - B_STATE_ACTIVE * The bchannel is active and configured for the need of the call. * Also it is linked to a call, otherwise it would be deactivated. * * - B_STATE_DEACTIVATING * The bchannel is in deactivating state, due to deactivation request. * It may be linked to a call, that likes to reactivate it. * * - B_STATE_IDLE * See above. * After deactivating bchannel, and if not used, the bchannel becomes idle again. * * A bchannel can have the following events: * * - B_EVENT_USE * A bchannel is required by a call instance. * * - B_EVENT_ACTIVATED * The bchannel beomes active. * * - B_EVENT_DROP * The bchannel is not required by a call anymore * * - B_EVENT_DEACTIVATED * The bchannel becomes inactive. * * All actions taken on these events depend on the current bchannel's state and * whether it is linked to a call instance. * */ /* process bchannel events */ void bchannel_event(isdn_t *isdn_ep, int index, int event) { call_t *call; int state; int channel; int timer = -1; // no change channel = index + 1 + (index >= 15); call = isdn_ep->b_call[index]; state = isdn_ep->b_state[index]; PDEBUG(DISDN, DEBUG_DEBUG, "handling event %d at state %d for B-channel %d\n", event, state, channel); switch(event) { case B_EVENT_USE: /* call must be linked in order to allow activation */ if (!call) { PDEBUG(DISDN, DEBUG_ERROR, "bchannel must be linked to a call instance\n"); abort(); } switch(state) { case B_STATE_IDLE: /* create stack and send activation request */ if (bchannel_create(isdn_ep, index) == 0) { bchannel_activate(isdn_ep, index, 1, 0); state = B_STATE_ACTIVATING; timer = B_TIMER_ACTIVATING; } break; case B_STATE_ACTIVATING: /* do nothing, because it is already activating */ break; default: /* problems that might occur: * B_EVENT_USE is received when channel already in use. */ PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state); } break; case B_EVENT_ACTIVATED: timer = 0; switch(state) { case B_STATE_ACTIVATING: if (call) { /* bchannel is active and used by a call instance, so we configure bchannel */ bchannel_configure(isdn_ep, index); state = B_STATE_ACTIVE; call->b_rx_time = 0.0; call->b_transmitting = 0; //FIXME: init buffer state for delay } else { /* bchannel is active, but not used anymore (or has wrong stack config), so we deactivate */ bchannel_activate(isdn_ep, index, 0, 0); state = B_STATE_DEACTIVATING; timer = B_TIMER_DEACTIVATING; } break; default: PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state); } break; case B_EVENT_DROP: if (!call) { PDEBUG(DISDN, DEBUG_ERROR, "bchannel must be linked to a call instance\n"); abort(); } switch(state) { case B_STATE_IDLE: /* bchannel is idle due to an error, so we do nothing */ break; case B_STATE_ACTIVATING: /* do nothing because we must wait until bchanenl is active before deactivating */ break; case B_STATE_ACTIVE: /* bchannel is active, so we deactivate */ bchannel_activate(isdn_ep, index, 0, 0); state = B_STATE_DEACTIVATING; timer = B_TIMER_DEACTIVATING; break; case B_STATE_DEACTIVATING: /* we may have taken an already deactivating bchannel, but do not require it anymore, so we do nothing */ break; default: PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state); } break; case B_EVENT_DEACTIVATED: timer = 0; switch(state) { case B_STATE_IDLE: /* ignore due to deactivation confirm after unloading */ break; case B_STATE_DEACTIVATING: bchannel_destroy(isdn_ep, index); state = B_STATE_IDLE; if (call) { /* bchannel is now deactivate, but is required by call instance, so we reactivate */ if (bchannel_create(isdn_ep, index) == 0) { bchannel_activate(isdn_ep, index, 1, 0); state = B_STATE_ACTIVATING; timer = B_TIMER_ACTIVATING; } } break; default: PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state); } break; case B_EVENT_TIMEOUT: timer = 0; switch(state) { case B_STATE_IDLE: /* ignore due to deactivation confirm after unloading */ break; case B_STATE_ACTIVATING: bchannel_activate(isdn_ep, index, 1, 1); timer = B_TIMER_ACTIVATING; break; case B_STATE_DEACTIVATING: bchannel_activate(isdn_ep, index, 0, 1); timer = B_TIMER_DEACTIVATING; break; default: PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state); } break; default: PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d, please correct.\n", event); } isdn_ep->b_state[index] = state; if (timer == 0) timer_stop(&isdn_ep->b_timer[index]); else if (timer > 0) timer_start(&isdn_ep->b_timer[index], timer); } static void bchannel_rx_tx(isdn_t *isdn_ep, int index, struct mISDNhead *hh, unsigned char *data, int len); static void bchannel_confirm(isdn_t *isdn_ep, int index); /* handle frames from B-channel (kernel socket) */ static int bchannel_kernel_sock_receive_cb(struct osmo_fd *ofd, unsigned int when) { struct isdn_b_sock *b_sock = ofd->data; int index = b_sock->index; isdn_t *isdn_ep = b_sock->isdn_ep; int channel = index + 1 + (index >= 15); unsigned char buffer[2048+MISDN_HEADER_LEN]; struct mISDNhead *hh = (struct mISDNhead *)buffer; int rc; if (!(when & OSMO_FD_READ)) { PDEBUG(DISDN, DEBUG_ERROR, "this should never happen!\n"); return 0; } rc = recv(ofd->fd, buffer, sizeof(buffer), 0); if (rc < 0) { if (errno == EAGAIN) return 0; PDEBUG(DISDN, DEBUG_ERROR, "read error B-channel data (socket #%d errno=%d:%s)\n", ofd->fd, errno, strerror(errno)); return 0; } if (rc < (int)MISDN_HEADER_LEN) { PDEBUG(DISDN, DEBUG_ERROR, "read short frame, got %d, expected %d\n", rc, (int)MISDN_HEADER_LEN); return 0; } switch (hh->prim) { /* we don't care about confirms, we use rx data to sync tx */ case PH_DATA_CNF: if (isdn_ep->b_call[index]) bchannel_confirm(isdn_ep, index); break; /* we receive audio data, we respond to it AND we send tones */ case PH_DATA_IND: case DL_DATA_IND: case PH_DATA_REQ: case DL_DATA_REQ: case PH_CONTROL_IND: if (isdn_ep->b_call[index]) bchannel_rx_tx(isdn_ep, index, hh, buffer + MISDN_HEADER_LEN, rc - MISDN_HEADER_LEN); else PDEBUG(DISDN, DEBUG_DEBUG, "b-channel is not associated to a call (channel %d), ignoring.\n", channel); break; case PH_ACTIVATE_IND: case DL_ESTABLISH_IND: case PH_ACTIVATE_CNF: case DL_ESTABLISH_CNF: PDEBUG(DISDN, DEBUG_DEBUG, "DL_ESTABLISH confirm: bchannel is now activated (channel %d).\n", channel); bchannel_event(isdn_ep, index, B_EVENT_ACTIVATED); break; case PH_DEACTIVATE_IND: case DL_RELEASE_IND: case PH_DEACTIVATE_CNF: case DL_RELEASE_CNF: PDEBUG(DISDN, DEBUG_DEBUG, "DL_RELEASE confirm: bchannel is now de-activated (channel %d).\n", channel); bchannel_event(isdn_ep, index, B_EVENT_DEACTIVATED); break; default: PDEBUG(DISDN, DEBUG_ERROR, "child message not handled: prim(0x%x) channel(%d) msg->len(%d)\n", hh->prim, channel, rc - (int)MISDN_HEADER_LEN); } 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_CNF: if (isdn_ep->b_call[index]) bchannel_confirm(isdn_ep, index); break; case PH_PRIM_DATA_IND: if (isdn_ep->b_call[index]) { struct mISDNhead hh = { .prim = PH_DATA_IND }; bchannel_rx_tx(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(void *data) { struct b_timer_inst *ti = data; bchannel_event(ti->isdn_ep, ti->index, B_EVENT_TIMEOUT); } /* * check for available channel and reserve+set it. * give channel number or SEL_CHANNEL_ANY or SEL_CHANNEL_NO * give exclusive flag * returns -(cause value) or x = channel x or 0 = no channel * NOTE: no activation is done here */ int seize_bchannel(call_t *call, int channel, int exclusive) { isdn_t *isdn_ep; int index; isdn_ep = call->isdn_ep; /* the channel is what we already have */ if (call->b_channel == channel) return channel; /* if channel already in use, release it */ if (call->b_channel) drop_bchannel(call); /* if CHANNEL_NO */ if (channel == CHANNEL_NO || channel == 0) return 0; /* is channel out of range ? */ if (channel == 16 || (channel > isdn_ep->b_num && channel < 16) || ((channel - 1) > isdn_ep->b_num && channel > 16)) /* channel-1 because channel 16 is not counted */ return -6; /* channel unacceptable */ /* request exclusive channel */ if (exclusive && channel > 0) { index = channel - 1 - (channel > 16); if (isdn_ep->b_call[index]) return -44; /* requested channel not available */ goto seize; } /* ask for channel */ if (channel > 0) { index = channel - 1 - (channel > 16); if (!isdn_ep->b_call[index]) goto seize; } /* search for channel */ for (index = 0; index < isdn_ep->b_num; index++) { if (!isdn_ep->b_call[index]) { channel = index + 1 + (index >= 15); goto seize; } } return -34; /* no free channel */ seize: PDEBUG(DISDN, DEBUG_DEBUG, "seizing bchannel %d (index %d)\n", channel, index); /* link Port, set parameters */ isdn_ep->b_call[index] = call; isdn_ep->b_mode[index] = call->b_mode; call->b_index = index; call->b_channel = channel; call->b_exclusive = exclusive; /* reserve channel */ if (!call->b_reserve) { call->b_reserve = 1; isdn_ep->b_reserved++; } return channel; } /* * drop reserved channel and unset it. * deactivation is also done */ void drop_bchannel(call_t *call) { isdn_t *isdn_ep; isdn_ep = call->isdn_ep; /* unreserve channel */ if (call->b_reserve) { isdn_ep->b_reserved--; call->b_reserve = 0; } /* if not in use */ if (call->b_index < 0) return; if (!call->b_channel) return; PDEBUG(DISDN, DEBUG_DEBUG, "dropping bchannel %d (index %d)\n", call->b_channel, call->b_index); if (isdn_ep->b_state[call->b_index] != B_STATE_IDLE) bchannel_event(isdn_ep, call->b_index, B_EVENT_DROP); isdn_ep->b_call[call->b_index] = NULL; isdn_ep->b_mode[call->b_index] = 0; call->b_index = -1; call->b_channel = 0; call->b_exclusive = 0; } static void send_to_rtp(call_t *call, unsigned char *data, int len) { call_t *other; if (!call || !call->audio_path) return; if (call->conference_3pty) { int16_t *audio_local; int audio_len; int16_t audio_mix[len], audio_remote_active[len], audio_remote_hold[len]; int32_t spl; int i; /* there should be no call on hold with audio coming from */ if (call->hold) return; /* convert local audio from interface to samples */ if (call->isdn_ep->law == 'a') g711_decode_alaw_flipped(data, len, (uint8_t **)&audio_local, &audio_len, NULL); else g711_decode_ulaw_flipped(data, len, (uint8_t **)&audio_local, &audio_len, NULL); // don't free audio, because we need that later when encoding /* convert remote RTP to samples */ jitter_load(&call->conf_dejitter, audio_remote_active, len); /* search other party on hold */ other = call->isdn_ep->call_list; while (other) { if (other != call && other->l3_ces == call->l3_ces && other->hold && other->conference_3pty) break; other = other->next; } /* convert remote RTP to samples */ if (other) jitter_load(&other->conf_dejitter, audio_remote_hold, len); else memset(audio_remote_hold, 0, sizeof(*audio_remote_hold) * len); /* mix audio for local interface and forward */ for (i = 0; i < len; i++) { spl = (int32_t)audio_remote_active[i] + (int32_t)audio_remote_hold[i]; /* both remote parties */ if (spl < -32767) spl = -32767; if (spl > 32767) spl = 32767; audio_mix[i] = spl; } if (call->isdn_ep->law == 'a') g711_encode_alaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len, NULL); else g711_encode_ulaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len, NULL); if (call->b_index >= 0 && call->b_transmitting) { jitter_save(&call->tx_dejitter, data, len, 0, 0, 0, 0); } free(data); /* mix audio for (active) remote interface and forward */ for (i = 0; i < len; i++) { spl = audio_local[i] + audio_remote_hold[i]; /* local + remote (on hold) party */ if (spl < -32767) spl = -32767; if (spl > 32767) spl = 32767; audio_mix[i] = spl; } if (call->isdn_ep->law == 'a') g711_encode_alaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len, NULL); else g711_encode_ulaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len, NULL); osmo_cc_rtp_send(call->codec, data, len, 0, 1, len, call); free(data); /* mix audio for (hold) remote interface and forward */ if (other) { for (i = 0; i < len; i++) { spl = audio_local[i] + audio_remote_active[i]; /* local + remote (active) party */ if (spl < -32767) spl = -32767; if (spl > 32767) spl = 32767; audio_mix[i] = spl; } if (call->isdn_ep->law == 'a') g711_encode_alaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len, NULL); else g711_encode_ulaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len, NULL); osmo_cc_rtp_send(other->codec, data, len, 0, 1, len, other); free(data); } free(audio_local); return; } /* bridging, don't forward */ if (call->bridge_enabled) return; osmo_cc_rtp_send(call->codec, data, len, 0, 1, len, call); } /* receive audio and control from B-channel, transmit data from jitter buffer accoring to received length */ static void bchannel_rx_tx(isdn_t *isdn_ep, int index, struct mISDNhead *hh, unsigned char *data, int len) { uint8_t *buffer = isdn_ep->b_buffer[index]; int *buffer_pos = &(isdn_ep->b_buffer_pos[index]); call_t *call = isdn_ep->b_call[index]; int i; if (hh->prim == PH_CONTROL_IND) { if (len < 4) { PDEBUG(DISDN, DEBUG_ERROR, "SHORT READ OF PH_CONTROL INDICATION\n"); return; } return; } if (hh->prim != PH_DATA_IND) return; if (!call) { PDEBUG(DISDN, DEBUG_DEBUG, "Dropping b-channel data from channel without call.\n"); return; } if (isdn_ep->b_state[index] != B_STATE_ACTIVE) { PDEBUG(DISDN, DEBUG_DEBUG, "Dropping b-channel data from inactive channel.\n"); return; } /* no transmission when bridging and playing no tones */ if (call->bridge_enabled && !call->isdn_tone.tone) { call->b_transmitting = 0; *buffer_pos = 0; return; } /* check jitter of receved stream from ISDN */ double now = get_time(); if (call->b_rx_time) { double elapsed = now - call->b_rx_time; if ((int)(elapsed * 8000.0) - len > len / 4) PDEBUG(DISDN, DEBUG_DEBUG, "Data received %d samples, time elapsed %d samples\n", len, (int)(elapsed * 8000.0)); } call->b_rx_time = now; /* add to buffer and send via RTP */ for (i = 0; i < len; i++) { buffer[(*buffer_pos)++] = data[i]; if (*buffer_pos == 160) { *buffer_pos = 0; send_to_rtp(call, buffer, 160); } } /* prepare ISDN TX buffer */ unsigned char buf[MISDN_HEADER_LEN + len + len]; struct mISDNhead *frm = (struct mISDNhead *)buf; int offset; int rc = 0; frm->prim = PH_DATA_REQ; frm->id = 0; if (!call->b_transmitting) { PDEBUG(DISDN, DEBUG_DEBUG, "First received b-channel data, filling FIFO with double data of %d bytes.\n", len * 2); memset(buf + MISDN_HEADER_LEN, 0xff, len); offset = len; } else { // PDEBUG(DISDN, DEBUG_DEBUG, "More received b-channel data, filling FIFO with data of %d bytes.\n", len); offset = 0; } /* load from TX jitter buffer and optionally overload with tones */ jitter_load(&call->tx_dejitter, buf + MISDN_HEADER_LEN + offset, len); isdn_tone_copy(&call->isdn_tone, buf + MISDN_HEADER_LEN + offset, len); /* forward to interface */ 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, offset + len); else rc = send(call->isdn_ep->b_sock[call->b_index].ofd.fd, buf, MISDN_HEADER_LEN + offset + 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].ofd.fd, errno, strerror(errno)); else call->b_transmitting = 1; } /* receive confirm from bchannel */ static void bchannel_confirm(isdn_t *isdn_ep, int index) { call_t *call = isdn_ep->b_call[index]; if (!call) { PDEBUG(DISDN, DEBUG_DEBUG, "Ignoring b-channel confirm of channel without call.\n"); return; } double now = get_time(); if (call->b_rx_time) { double elapsed = now - call->b_rx_time; if (elapsed > 4) PDEBUG(DISDN, DEBUG_DEBUG, "Data confirmed, time elapsed %d samples\n", (int)(elapsed * 8000.0)); } } /* send audio from RTP to B-channel's jitter buffer */ void rtp_receive(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len) { call_t *call = codec->media->session->priv; /* conference parties store their audio in jitter buffer */ if (call->conference_3pty) { int16_t *audio; int audio_len; /* alaw/ulaw to linear */ if (call->isdn_ep->law == 'a') g711_decode_alaw_flipped(data, len, (uint8_t **)&audio, &audio_len, NULL); else g711_decode_ulaw_flipped(data, len, (uint8_t **)&audio, &audio_len, NULL); /* enqueue data to jitter buffer */ jitter_save(&call->conf_dejitter, audio, len, 1, sequence_number, timestamp, ssrc); free(audio); return; } /* bridging, don't forward */ if (call->bridge_enabled) return; /* not yet b_transmitting on bchannel */ if (!call->b_transmitting) return; /* ignore voice, if call is on hold */ if (call->hold) return; /* no conference, just forward to ISDN interface */ jitter_save(&call->tx_dejitter, data, len, 1, sequence_number, timestamp, ssrc); } /* * isdn stack handling */ int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3_msg *l3m) { isdn_t *isdn_ep = (isdn_t *)ml3->priv; struct mbuffer *mb; /* queue message, create, if required */ if (!l3m) { l3m = alloc_l3_msg(); if (!l3m) { PDEBUG(DISDN, DEBUG_ERROR, "No memory for layer 3 message\n"); abort(); } } mb = container_of(l3m, struct mbuffer, l3); l3m->type = cmd; l3m->pid = pid; pthread_mutex_lock(&isdn_ep->upqueue_lock); mqueue_tail(&isdn_ep->upqueue, mb); pthread_mutex_unlock(&isdn_ep->upqueue_lock); /* wake main thread */ char wakeup = 0; write(isdn_ep->upqueue_pipe[1], &wakeup, 1); return 0; } int mISDN_getportbyname(isdn_t *isdn_ep, int cnt, const char *portname) { struct mISDN_devinfo devinfo; int port = 0, rc; memset(&devinfo, 0, sizeof(devinfo)); /* resolve name */ while (port < cnt) { devinfo.id = port; 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)) break; port++; } if (port == cnt) return -EIO; return port; } static void l2establish_timeout(void *data); static int isdn_upqueue_cb(struct osmo_fd *ofd, unsigned int what); /* open mISDN port */ int isdn_open(isdn_t *isdn_ep) { const char *portname = isdn_ep->portname; int portnum; int ptp = isdn_ep->ptp; int force_nt = isdn_ep->ntmode; int l1hold = isdn_ep->l1hold; int l2hold = isdn_ep->l2hold; int i, cnt; int pri, bri; int nt, te; struct mISDN_devinfo devinfo; unsigned int protocol, prop; int rc = 0; /* queue must be initializes, because l3-thread may send messages during open_layer3() */ mqueue_init(&isdn_ep->upqueue); isdn_ep->upqueue_initialized = 1; rc = pipe(isdn_ep->upqueue_pipe); if (rc < 0) { PDEBUG(DISDN, DEBUG_ERROR, "Failed to create pipe errno=%d:%s. (Please fix!)\n", errno, strerror(errno)); goto error; } isdn_ep->upqueue_ofd.fd = isdn_ep->upqueue_pipe[0]; isdn_ep->upqueue_ofd.cb = isdn_upqueue_cb; isdn_ep->upqueue_ofd.data = isdn_ep; isdn_ep->upqueue_ofd.when = OSMO_FD_READ; osmo_fd_register(&isdn_ep->upqueue_ofd); /* port number given ? */ for (i = 0; i < (int)strlen(portname); i++){ if (portname[i] < '0' || portname[i] > '9') break; } if (i == (int)strlen(portname)) portnum = atoi(portname); else portnum = -1; memset(&devinfo, 0, sizeof(devinfo)); /* check port counts */ 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; } if (cnt <= 0) { PDEBUG(DISDN, DEBUG_ERROR, "Found no card. Please be sure to load card drivers.\n"); goto error; } if (portnum < 0) { 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; } // note: 'portnum' has now the port number } if (portnum > cnt || portnum < 0) { PDEBUG(DISDN, DEBUG_ERROR, "Port (%d) given at 'ports' (options.conf) is out of existing port range (%d-%d)\n", portnum, 0, cnt); goto error; } /* get port attributes */ pri = bri = nt = te = 0; devinfo.id = portnum; 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; } if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) { bri = 1; te = 1; } if (devinfo.Dprotocols & (1 << ISDN_P_NT_S0)) { bri = 1; nt = 1; } if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) { pri = 1; te = 1; } if (devinfo.Dprotocols & (1 << ISDN_P_NT_E1)) { pri = 1; nt = 1; } if (force_nt && !nt) { PDEBUG(DISDN, DEBUG_ERROR, "Port %d does not support NT-mode\n", portnum); goto error; } if (bri && pri) { PDEBUG(DISDN, DEBUG_ERROR, "Port %d supports BRI and PRI?? What kind of controller is that?. (Can't use this!)\n", portnum); goto error; } if (!bri && !pri) { PDEBUG(DISDN, DEBUG_ERROR, "Port %d does not support BRI nor PRI\n", portnum); goto error; } if (!nt && !te) { PDEBUG(DISDN, DEBUG_ERROR, "Port %d does not support NT-mode nor TE-mode!\n", portnum); goto error; } /* set NT by turning off TE */ if (force_nt && nt) te = 0; /* if TE an NT is supported (and not forced to NT), turn off NT */ if (te && nt) nt = 0; /* check for continuous channelmap with no bchannel on slot 16 */ if (test_channelmap(0, devinfo.channelmap)) { PDEBUG(DISDN, DEBUG_ERROR, "Port %d provides channel 0, but we cannot access it!\n", portnum); goto error; } i = 1; while(i < (int)devinfo.nrbchan + 1) { if (i == 16) { if (test_channelmap(i, devinfo.channelmap)) { PDEBUG(DISDN, DEBUG_ERROR, "Port %d provides bchannel 16. Please upgrade mISDN, if this port is mISDN loopback interface.\n", portnum); goto error; } } else { if (!test_channelmap(i, devinfo.channelmap)) { PDEBUG(DISDN, DEBUG_ERROR, "Port %d has no channel on slot %d!\n", portnum, i); goto error; } } i++; } timer_init(&isdn_ep->clock_timer, clock_timeout, isdn_ep); timer_init(&isdn_ep->l2establish_timer, l2establish_timeout, isdn_ep); isdn_ep->l1link = -1; isdn_ep->l2link = -1; /* if pri, must set PTP */ if (pri) ptp = 1; /* set l2hold */ switch (l2hold) { case -1: // off l2hold = 0; break; case 1: // on l2hold = 1; break; default: if (ptp) l2hold = 1; else l2hold = 0; break; } /* allocate resources of port */ protocol = (nt) ? L3_PROTOCOL_DSS1_NET : L3_PROTOCOL_DSS1_USER; prop = (1 << MISDN_FLG_L2_CLEAN); if (ptp) // ptp forced prop |= (1 << MISDN_FLG_PTP); if (nt) // supports hold/retrieve on nt-mode prop |= (1 << MISDN_FLG_NET_HOLD); if (l1hold) // supports layer 1 hold prop |= (1 << MISDN_FLG_L1_HOLD); if (l2hold) // supports layer 2 hold prop |= (1 << MISDN_FLG_L2_HOLD); /* open layer 3 */ isdn_ep->ml3 = open_layer3(portnum, protocol, prop, do_layer3, isdn_ep); if (!isdn_ep->ml3) { PDEBUG(DISDN, DEBUG_ERROR, "open_layer3() failed for port %d\n", portnum); goto error; } 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); isdn_ep->portname = strdup(devinfo.name); if (!strncmp(isdn_ep->portname, "hfc-4s.", 7) || !strncmp(isdn_ep->portname, "hfc-8s.", 7) || !strncmp(isdn_ep->portname, "hfc-e1.", 7)) { /* cards that support hardware bridging */ isdn_ep->bridge_possible = 1; isdn_ep->bridge_cardnum = strtoul(isdn_ep->portname + 7, NULL, 10); isdn_ep->bridge_portnum = portnum; PDEBUG(DISDN, DEBUG_INFO, "Port can use HFC bridge of card %d.\n", isdn_ep->bridge_cardnum); } isdn_ep->ntmode = nt; isdn_ep->pri = pri; isdn_ep->ptp = ptp; isdn_ep->l1hold = l1hold; isdn_ep->l2hold = l2hold; PDEBUG(DISDN, DEBUG_DEBUG, "Port has %d b-channels.\n", isdn_ep->b_num); for (i = 0; i < isdn_ep->b_num; i++) { isdn_ep->b_state[i] = B_STATE_IDLE; timer_init(&isdn_ep->b_timer[i], b_timer_timeout, &isdn_ep->b_timer_inst); isdn_ep->b_timer_inst[i].isdn_ep = isdn_ep; isdn_ep->b_timer_inst[i].index = i; } /* if ptp, pull up the link */ if (isdn_ep->l2hold && (isdn_ep->ptp || !isdn_ep->ntmode)) { PDEBUG(DISDN, DEBUG_DEBUG, "reqzest layer 2 to be establised for tei 0\n"); timer_start(&isdn_ep->l2establish_timer, 5.0); /* 5 seconds */ } /* for nt-mode ptmp the link is always up */ if (isdn_ep->ntmode && !isdn_ep->ptp) isdn_ep->l2link = 1; return 0; error: isdn_close(isdn_ep); return -EINVAL; } /* close mISDN port */ void isdn_close(isdn_t *isdn_ep) { int i; struct select_channel *selchannel; /* free bchannels */ for (i = 0; i < isdn_ep->b_num; i++) { if (isdn_ep->b_sock[i].ofd.fd > 0) { bchannel_destroy(isdn_ep, i); PDEBUG(DISDN, DEBUG_DEBUG, "freeing %s port %s bchannel (index %d).\n", (isdn_ep->ntmode) ? "NT" : "TE", isdn_ep->portname, i); } timer_exit(&isdn_ep->b_timer[i]); } timer_exit(&isdn_ep->l2establish_timer); /* close layer 3, if open */ if (isdn_ep->ml3) { close_layer3(isdn_ep->ml3); } /* purge and remove upqueue */ if (isdn_ep->upqueue_initialized) mqueue_purge(&isdn_ep->upqueue); if (isdn_ep->upqueue_pipe[0] > 0) { osmo_fd_unregister(&isdn_ep->upqueue_ofd); close(isdn_ep->upqueue_pipe[0]); isdn_ep->upqueue_pipe[0] = 0; } if (isdn_ep->upqueue_pipe[1] > 0) { close(isdn_ep->upqueue_pipe[1]); isdn_ep->upqueue_pipe[1] = 0; } if (isdn_ep->portname) { free(isdn_ep->portname); isdn_ep->portname = NULL; } while (isdn_ep->in_channel) { selchannel = isdn_ep->in_channel; isdn_ep->in_channel = selchannel->next; free(selchannel); } while (isdn_ep->out_channel) { selchannel = isdn_ep->out_channel; isdn_ep->out_channel = selchannel->next; free(selchannel); } } /* receive message from ISDN protocol stack (out of the upqueue) */ static int isdn_upqueue_cb(struct osmo_fd *ofd, unsigned __attribute__((unused)) int what) { isdn_t *isdn_ep = ofd->data; struct mbuffer *mb; struct l3_msg *l3m; char wakeup = 0; read(ofd->fd, &wakeup, 1); again: /* handle queued up-messages (d-channel) */ pthread_mutex_lock(&isdn_ep->upqueue_lock); mb = mdequeue(&isdn_ep->upqueue); pthread_mutex_unlock(&isdn_ep->upqueue_lock); if (!mb) return 0; l3m = &mb->l3; switch(l3m->type) { case MPH_ACTIVATE_IND: if (isdn_ep->l1link != 1) { PDEBUG(DISDN, DEBUG_INFO, "layer 1 becomes active\n"); isdn_ep->l1link = 1; } break; case MPH_DEACTIVATE_IND: if (isdn_ep->l1link != 0) { PDEBUG(DISDN, DEBUG_INFO, "layer 1 becomes inactive\n"); isdn_ep->l1link = 0; } break; case MPH_INFORMATION_IND: switch (l3m->pid) { case L1_SIGNAL_LOS_ON: PDEBUG(DISDN, DEBUG_DEBUG, "received LOS\n"); isdn_ep->los = 1; break; case L1_SIGNAL_LOS_OFF: PDEBUG(DISDN, DEBUG_DEBUG, "LOS is gone\n"); isdn_ep->los = 0; break; case L1_SIGNAL_AIS_ON: PDEBUG(DISDN, DEBUG_DEBUG, "received AIS\n"); isdn_ep->ais = 1; break; case L1_SIGNAL_AIS_OFF: PDEBUG(DISDN, DEBUG_DEBUG, "AIS is gone\n"); isdn_ep->ais = 0; break; case L1_SIGNAL_RDI_ON: PDEBUG(DISDN, DEBUG_DEBUG, "received RDI\n"); isdn_ep->rdi = 1; break; case L1_SIGNAL_RDI_OFF: PDEBUG(DISDN, DEBUG_DEBUG, "RDI is gone\n"); isdn_ep->rdi = 0; break; case L1_SIGNAL_SLIP_TX: isdn_ep->slip_tx++; PDEBUG(DISDN, DEBUG_DEBUG, "received TX slip #%d\n", isdn_ep->slip_tx); break; case L1_SIGNAL_SLIP_RX: isdn_ep->slip_rx++; PDEBUG(DISDN, DEBUG_DEBUG, "received RX slip #%d\n", isdn_ep->slip_rx); break; } break; case MT_L2ESTABLISH: PDEBUG(DISDN, DEBUG_INFO, "layer 2 becomes active (tei = %d)\n", l3m->pid); isdn_ep->l2link = 1; if (l3m->pid < 128) isdn_ep->l2mask[l3m->pid >> 3] |= (1 << (l3m->pid & 7)); if ((!isdn_ep->ntmode || isdn_ep->ptp) && l3m->pid < 127) { if (timer_running(&isdn_ep->l2establish_timer)) timer_stop(&isdn_ep->l2establish_timer); } break; case MT_L2RELEASE: if (l3m->pid < 128) isdn_ep->l2mask[l3m->pid >> 3] &= ~(1 << (l3m->pid & 7)); if (!timer_running(&isdn_ep->l2establish_timer)) { PDEBUG(DISDN, DEBUG_INFO, "layer 2 becomes inactive (tei = %d)\n", l3m->pid); /* down if not nt-ptmp */ if (!isdn_ep->ntmode || isdn_ep->ptp) isdn_ep->l2link = 0; } if ((!isdn_ep->ntmode || isdn_ep->ptp) && l3m->pid < 127) { if (!timer_running(&isdn_ep->l2establish_timer) && isdn_ep->l2hold) { timer_start(&isdn_ep->l2establish_timer, 5.0); /* 5 seconds */ isdn_ep->ml3->to_layer3(isdn_ep->ml3, MT_L2ESTABLISH, 0, NULL); } } break; default: /* l3-data is sent to DSS1 handling */ dss1_receive(isdn_ep, l3m->type, l3m->pid, l3m); } /* free message */ free_l3_msg(l3m); goto again; } /* l2 establish timer fires */ static void l2establish_timeout(void *data) { isdn_t *isdn_ep = data; if (isdn_ep->l2hold && (isdn_ep->ptp || !isdn_ep->ntmode)) { PDEBUG(DISDN, DEBUG_DEBUG, "the L2 establish timer expired, we try to establish the link portnum=%d.\n", isdn_ep->portnum); isdn_ep->ml3->to_layer3(isdn_ep->ml3, MT_L2ESTABLISH, 0, NULL); timer_start(&isdn_ep->l2establish_timer, 5.0); /* 5 seconds */ } } /* * local clock to generate hold tone */ #define COMFORT_NOISE (0.02 * SPEECH_LEVEL) /* audio level of comfort noise (relative to ISDN level) */ #define CHUNK_DURATION 0.020 #define CHUNK_LENGTH 160 static void clock_timeout(void *data) { isdn_t *isdn_ep = data; int len = CHUNK_LENGTH; call_t *call; int any = 0; call = isdn_ep->call_list; while (call) { if ((call->state == ISDN_STATE_SUSPENDED || call->hold) && call->codec) { int16_t noise[len]; uint8_t *data; int i; int16_t r; for (i = 0; i < len; i++) { r = random(); noise[i] = (double)r * COMFORT_NOISE; } if (call->isdn_ep->law == 'a') g711_encode_alaw_flipped((uint8_t *)noise, len * 2, &data, &len, NULL); else g711_encode_ulaw_flipped((uint8_t *)noise, len * 2, &data, &len, NULL); osmo_cc_rtp_send(call->codec, data, len, 0, 1, len, call); free(data); any = 1; } call = call->next; } /* restart clock if there is still any call that requires it */ if (any) { double now; /* restart timer to wait for next 20ms */ now = get_time(); /* if there is too much jitter or if last_time_clock is not set */ if (now - isdn_ep->last_time_clock >= 0.1) isdn_ep->last_time_clock = now; /* advance clock */ isdn_ep->last_time_clock += CHUNK_DURATION; /* if there is too much jitter */ if (isdn_ep->last_time_clock < now) isdn_ep->last_time_clock = now; timer_start(&isdn_ep->clock_timer, isdn_ep->last_time_clock - now); } } void enable_hold_clock(isdn_t *isdn_ep) { double now; /* already running, so keep running without restart */ if (timer_running(&isdn_ep->clock_timer)) return; /* start timer to wait for 20ms */ now = get_time(); isdn_ep->last_time_clock = now + CHUNK_DURATION; timer_start(&isdn_ep->clock_timer, CHUNK_DURATION); }