+- B_STATE_DEACTIVATING
+The bchannel is in deactivating state, due to deactivation request.
+It may be linked to a Port class, that likes to reactivate it.
+
+- B_STATE_IDLE
+See above.
+After deactivating bchannel, and if not used, the bchannel becomes idle again.
+
+Also the bchannel may be exported, but only if the state is or becomes idle:
+
+- B_STATE_EXPORTING
+The bchannel assignment has been sent to the remove application.
+
+- B_STATE_REMOTE
+The bchannel assignment is acknowledged by the remote application.
+
+- B_STATE_IMPORTING
+The bchannel is re-imported by mISDN port object.
+
+- B_STATE_IDLE
+See above.
+After re-importing 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 Port class.
+
+- B_EVENT_ACTIVATED
+The bchannel beomes active.
+
+- B_EVENT_DROP
+The bchannel is not required by Port class anymore
+
+- B_EVENT_DEACTIVATED
+The bchannel becomes inactive.
+
+- B_EVENT_EXPORTED
+The bchannel is now used by remote application.
+
+- B_EVENT_IMPORTED
+The bchannel is not used by remote application.
+
+- B_EVENT_EXPORTREQUEST
+The bchannel shall be exported to the remote application.
+
+- B_EVENT_IMPORTREQUEST
+The bchannel is released from the remote application.
+
+All actions taken on these events depend on the current bchannel's state and if it is linked to a Port class.
+
+if an export request is receive by remote application, p_m_remote_* is set.
+the b_remote_*[index] indicates if and where the channel is exported to. (set from the point on, where export is initiated, until imported is acknowledged.)
+- set on export request from remote application (if port is assigned)
+- set on channel use, if requested by remote application (p_m_remote_*)
+- cleared on drop request
+
+the bchannel will be exported with ref and stack given. remote application uses the ref to link bchannel to the call.
+the bchannel will be imported with stack given only. remote application must store stack id with the bchannel process.
+the bchannel import/export is acknowledged with stack given.
+
+if exporting, b_remote_*[index] is set to the remote socket id.
+if importing has been acknowledged. b_remote_*[index] is cleared.
+
+*/
+
+/*
+ * process bchannel events
+ * - mISDNport is a pointer to the port's structure
+ * - i is the index of the bchannel
+ * - event is the B_EVENT_* value
+ * - port is the PmISDN class pointer
+ */
+void bchannel_event(struct mISDNport *mISDNport, int i, int event)
+{
+ class PmISDN *b_port = mISDNport->b_port[i];
+ int state = mISDNport->b_state[i];
+ int timer = -1; // no change
+ unsigned int p_m_remote_ref = 0;
+ unsigned int p_m_remote_id = 0;
+ int p_m_tx_gain = 0;
+ int p_m_rx_gain = 0;
+ char *p_m_pipeline = NULL;
+ unsigned char *p_m_crypt_key = NULL;
+ int p_m_crypt_key_len = 0;
+ int p_m_crypt_key_type = 0;
+ unsigned int portid = (mISDNport->portnum<<8) + i+1+(i>=15);
+
+ if (b_port) {
+ p_m_remote_id = b_port->p_m_remote_id;
+ p_m_remote_ref = b_port->p_m_remote_ref;
+ p_m_tx_gain = b_port->p_m_tx_gain;
+ p_m_rx_gain = b_port->p_m_rx_gain;
+ p_m_pipeline = b_port->p_m_pipeline;
+ p_m_crypt_key = b_port->p_m_crypt_key;
+ p_m_crypt_key_len = b_port->p_m_crypt_key_len;
+ p_m_crypt_key_type = /*b_port->p_m_crypt_key_type*/1;
+ }
+
+ switch(event) {
+ case B_EVENT_USE:
+ /* port must be linked in order to allow activation */
+ if (!b_port)
+ FATAL("bchannel must be linked to a Port class\n");
+ switch(state) {
+ case B_STATE_IDLE:
+ if (p_m_remote_ref) {
+ /* export bchannel */
+ message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type, 0);
+ chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "assign");
+ add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
+ end_trace();
+ state = B_STATE_EXPORTING;
+ mISDNport->b_remote_id[i] = p_m_remote_id;
+ mISDNport->b_remote_ref[i] = p_m_remote_ref;
+ } else {
+ /* create stack and send activation request */
+ if (_bchannel_create(mISDNport, i)) {
+ _bchannel_activate(mISDNport, i, 1, 0);
+ state = B_STATE_ACTIVATING;
+ timer = B_TIMER_ACTIVATING;
+ }
+ }
+ break;
+
+ case B_STATE_ACTIVATING:
+ case B_STATE_EXPORTING:
+ /* do nothing, because it is already activating */
+ break;
+
+ case B_STATE_DEACTIVATING:
+ case B_STATE_IMPORTING:
+ /* do nothing, because we must wait until we can reactivate */
+ break;
+
+ default:
+ /* problems that might ocurr:
+ * B_EVENT_USE is received when channel already in use.
+ * bchannel exported, but not freed by other port
+ */
+ PERROR("Illegal event %d at state %d, please correct.\n", event, state);
+ }
+ break;
+
+ case B_EVENT_EXPORTREQUEST:
+ /* special case where the bchannel is requested by remote */
+ if (!p_m_remote_ref) {
+ PERROR("export request without remote channel set, please correct.\n");
+ break;
+ }
+ switch(state) {
+ case B_STATE_IDLE:
+ /* in case, the bchannel is exported right after seize_bchannel */
+ /* export bchannel */
+ /* p_m_remote_id is set, when this event happens. */
+ message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type, 0);
+ chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "assign");
+ add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
+ end_trace();
+ state = B_STATE_EXPORTING;
+ mISDNport->b_remote_id[i] = p_m_remote_id;
+ mISDNport->b_remote_ref[i] = p_m_remote_ref;
+ break;
+
+ case B_STATE_ACTIVATING:
+ case B_STATE_EXPORTING:
+ /* do nothing, because it is already activating */
+ break;
+
+ case B_STATE_DEACTIVATING:
+ case B_STATE_IMPORTING:
+ /* do nothing, because we must wait until we can reactivate */
+ break;
+
+ case B_STATE_ACTIVE:
+ /* bchannel is active, so we deactivate */
+ _bchannel_activate(mISDNport, i, 0, 0);
+ state = B_STATE_DEACTIVATING;
+ timer = B_TIMER_DEACTIVATING;
+ break;
+
+ default:
+ /* problems that might ocurr:
+ * ... when channel already in use.
+ * bchannel exported, but not freed by other port
+ */
+ PERROR("Illegal event %d at state %d, please correct.\n", event, state);
+ }
+ break;
+
+ case B_EVENT_IMPORTREQUEST:
+ /* special case where the bchannel is released by remote */
+ if (p_m_remote_ref) {
+ PERROR("import request with remote channel set, please correct.\n");
+ break;
+ }
+ switch(state) {
+ case B_STATE_IDLE:
+ case B_STATE_ACTIVE:
+ /* bchannel is not exported */
+ break;
+
+ case B_STATE_ACTIVATING:
+ case B_STATE_EXPORTING:
+ /* do nothing because we must wait until bchanenl is active before deactivating */
+ break;
+
+ case B_STATE_REMOTE:
+ /* bchannel is exported, so we re-import */
+ message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0, 0);
+ chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "remove");
+ add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
+ end_trace();
+ state = B_STATE_IMPORTING;
+ break;
+
+ case B_STATE_DEACTIVATING:
+ case B_STATE_IMPORTING:
+ /* we may have taken an already deactivating bchannel, but do not require it anymore, so we do nothing */
+ break;
+
+ default:
+ PERROR("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 (b_port && !p_m_remote_id) {
+ /* bchannel is active and used by Port class, so we configure bchannel */
+ _bchannel_configure(mISDNport, i);
+ state = B_STATE_ACTIVE;
+ b_port->p_m_load = 0;
+ } else {
+ /* bchannel is active, but exported OR not used anymore (or has wrong stack config), so we deactivate */
+ _bchannel_activate(mISDNport, i, 0, 0);
+ state = B_STATE_DEACTIVATING;
+ timer = B_TIMER_DEACTIVATING;
+ }
+ break;
+
+ default:
+ PERROR("Illegal event %d at state %d, please correct.\n", event, state);
+ }
+ break;
+
+ case B_EVENT_EXPORTED:
+ switch(state) {
+ case B_STATE_EXPORTING:
+ if (b_port && p_m_remote_ref && p_m_remote_ref==mISDNport->b_remote_ref[i]) {
+ /* remote export done */
+ state = B_STATE_REMOTE;
+ } else {
+ /* bchannel is now exported, but we need bchannel back
+ * OR bchannel is not used anymore
+ * OR bchannel has been exported to an obsolete ref,
+ * so reimport, to later export to new remote */
+ message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0, 0);
+ chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "remove");
+ add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
+ end_trace();
+ state = B_STATE_IMPORTING;
+ }
+ break;
+
+ default:
+ PERROR("Illegal event %d at state %d, please correct.\n", event, state);
+ }
+ break;
+
+ case B_EVENT_DROP:
+ if (!b_port)
+ FATAL("bchannel must be linked to a Port class\n");
+ switch(state) {
+ case B_STATE_IDLE:
+ /* bchannel is idle due to an error, so we do nothing */
+ break;
+
+ case B_STATE_ACTIVATING:
+ case B_STATE_EXPORTING:
+ /* 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(mISDNport, i, 0, 0);
+ state = B_STATE_DEACTIVATING;
+ timer = B_TIMER_DEACTIVATING;
+ break;
+
+ case B_STATE_REMOTE:
+ /* bchannel is exported, so we re-import */
+ message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0, 0);
+ chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "remove");
+ add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
+ end_trace();
+ state = B_STATE_IMPORTING;
+ break;
+
+ case B_STATE_DEACTIVATING:
+ case B_STATE_IMPORTING:
+ /* we may have taken an already deactivating bchannel, but do not require it anymore, so we do nothing */
+ break;
+
+ default:
+ PERROR("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(mISDNport, i);
+ state = B_STATE_IDLE;
+ if (b_port) {
+ /* bchannel is now deactivate, but is requied by Port class, so we reactivate / export */
+ if (p_m_remote_ref) {
+ message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type, 0);
+ chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "assign");
+ add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
+ end_trace();
+ state = B_STATE_EXPORTING;
+ mISDNport->b_remote_id[i] = p_m_remote_id;
+ mISDNport->b_remote_ref[i] = p_m_remote_ref;
+ } else {
+ if (_bchannel_create(mISDNport, i)) {
+ _bchannel_activate(mISDNport, i, 1, 0);
+ state = B_STATE_ACTIVATING;
+ timer = B_TIMER_ACTIVATING;
+ }
+ }
+ }
+ break;
+
+ default:
+ PERROR("Illegal event %d at state %d, please correct.\n", event, state);
+ }
+ break;
+
+ case B_EVENT_IMPORTED:
+ switch(state) {
+ case B_STATE_IMPORTING:
+ state = B_STATE_IDLE;
+ mISDNport->b_remote_id[i] = 0;
+ mISDNport->b_remote_ref[i] = 0;
+ if (b_port) {
+ /* bchannel is now imported, but is requied by Port class, so we reactivate / export */
+ if (p_m_remote_ref) {
+ message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type, 0);
+ chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "assign");
+ add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff);
+ end_trace();
+ state = B_STATE_EXPORTING;
+ mISDNport->b_remote_id[i] = p_m_remote_id;
+ mISDNport->b_remote_ref[i] = p_m_remote_ref;
+ } else {
+ if (_bchannel_create(mISDNport, i)) {
+ _bchannel_activate(mISDNport, i, 1, 0);
+ state = B_STATE_ACTIVATING;
+ timer = B_TIMER_ACTIVATING;
+ }
+ }
+ }
+ break;
+
+ default:
+ /* ignore, because not assigned */
+ ;
+ }
+ 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(mISDNport, i, 1, 1);
+ timer = B_TIMER_ACTIVATING;
+ break;
+
+ case B_STATE_DEACTIVATING:
+ _bchannel_activate(mISDNport, i, 0, 1);
+ timer = B_TIMER_DEACTIVATING;
+ break;
+
+ default:
+ PERROR("Illegal event %d at state %d, please correct.\n", event, state);
+ }
+ break;
+
+ default:
+ PERROR("Illegal event %d, please correct.\n", event);
+ }
+
+ mISDNport->b_state[i] = state;
+ if (timer == 0)
+ unsched_timer(&mISDNport->b_timer[i]);
+ else if (timer > 0)
+ schedule_timer(&mISDNport->b_timer[i], timer, 0);
+}
+
+
+
+
+/*
+ * check for available channel and reserve+set it.
+ * give channel number or SEL_CHANNEL_ANY or SEL_CHANNEL_NO
+ * give exclusiv flag
+ * returns -(cause value) or x = channel x or 0 = no channel
+ * NOTE: no activation is done here
+ */
+int PmISDN::seize_bchannel(int channel, int exclusive)
+{
+ int i;
+
+ /* the channel is what we have */
+ if (p_m_b_channel == channel)
+ return(channel);
+
+ /* if channel already in use, release it */
+ if (p_m_b_channel)
+ drop_bchannel();
+
+ /* if CHANNEL_NO */
+ if (channel==CHANNEL_NO || channel==0)
+ return(0);
+
+ /* is channel in range ? */
+ if (channel==16
+ || (channel>p_m_mISDNport->b_num && channel<16)
+ || ((channel-1)>p_m_mISDNport->b_num && channel>16)) /* channel-1 because channel 16 is not counted */
+ return(-6); /* channel unacceptable */
+
+ /* request exclusive channel */
+ if (exclusive && channel>0) {
+ i = channel-1-(channel>16);
+ if (p_m_mISDNport->b_port[i])
+ return(-44); /* requested channel not available */
+ goto seize;
+ }
+
+ /* ask for channel */
+ if (channel>0) {
+ i = channel-1-(channel>16);
+ if (p_m_mISDNport->b_port[i] == NULL)
+ goto seize;
+ }
+
+ /* search for channel */
+ i = 0;
+ while(i < p_m_mISDNport->b_num) {
+ if (!p_m_mISDNport->b_port[i]) {
+ channel = i+1+(i>=15);
+ goto seize;
+ }
+ i++;
+ }
+ return(-34); /* no free channel */
+
+seize:
+ PDEBUG(DEBUG_BCHANNEL, "PmISDN(%s) seizing bchannel %d (index %d)\n", p_name, channel, i);
+
+ /* link Port, set parameters */
+ p_m_mISDNport->b_port[i] = this;
+ p_m_b_index = i;
+ p_m_b_channel = channel;
+ p_m_b_exclusive = exclusive;
+ p_m_mISDNport->b_mode[i] = p_m_b_mode;
+
+ /* reserve channel */
+ if (!p_m_b_reserve) {
+ p_m_b_reserve = 1;
+ p_m_mISDNport->b_reserved++;
+ }
+
+ return(channel);
+}
+
+/*
+ * drop reserved channel and unset it.
+ * deactivation is also done
+ */
+void PmISDN::drop_bchannel(void)
+{
+ /* unreserve channel */
+ if (p_m_b_reserve)
+ p_m_mISDNport->b_reserved--;
+ p_m_b_reserve = 0;
+
+ /* if not in use */
+ if (p_m_b_index < 0)
+ return;
+ if (!p_m_b_channel)
+ return;
+
+ PDEBUG(DEBUG_BCHANNEL, "PmISDN(%s) dropping bchannel\n", p_name);
+
+ if (p_m_mISDNport->b_state[p_m_b_index] != B_STATE_IDLE)
+ bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_DROP);
+ p_m_mISDNport->b_port[p_m_b_index] = NULL;
+ p_m_mISDNport->b_mode[p_m_b_index] = 0;
+ p_m_b_index = -1;
+ p_m_b_channel = 0;
+ p_m_b_exclusive = 0;
+}
+
+/* process bchannel export/import message from join */
+void message_bchannel_from_remote(class JoinRemote *joinremote, int type, unsigned int handle)
+{
+ class Endpoint *epoint;
+ class Port *port;
+ class PmISDN *isdnport;
+ struct mISDNport *mISDNport;
+ int i, ii;
+
+ switch(type) {
+ case BCHANNEL_REQUEST:
+ /* find the port object for the join object ref */
+ if (!(epoint = find_epoint_id(joinremote->j_epoint_id))) {
+ PDEBUG(DEBUG_BCHANNEL, "join %d has no endpoint (anymore)\n", joinremote->j_serial);
+ return;
+ }
+ if (!epoint->ep_portlist) {
+ PDEBUG(DEBUG_BCHANNEL, "join %d has no port (anymore in portlist)\n", joinremote->j_serial);
+ return;
+ }
+ if (epoint->ep_portlist->next) {
+ PERROR("join %d has enpoint %d with more than one port. this shall not happen to remote joins.\n", joinremote->j_serial, epoint->ep_serial);
+ }
+ if (!(port = find_port_id(epoint->ep_portlist->port_id))) {
+ PDEBUG(DEBUG_BCHANNEL, "join %d has no port (anymore as object)\n", joinremote->j_serial);
+ return;
+ }
+ if ((port->p_type&PORT_CLASS_MASK) != PORT_CLASS_mISDN) {
+ PERROR("join %d has port %d not of mISDN type. This shall not happen.\n", joinremote->j_serial, port->p_serial);
+ }
+ isdnport = (class PmISDN *)port;
+
+ /* assign */
+ if (isdnport->p_m_remote_id) {
+ PERROR("join %d recevied bchannel request from remote, but channel is already assinged.\n", joinremote->j_serial);
+ break;
+ }
+ mISDNport = isdnport->p_m_mISDNport;
+ i = isdnport->p_m_b_index;
+ chan_trace_header(mISDNport, isdnport, "MESSAGE_BCHANNEL (from remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "export request");
+ end_trace();
+ isdnport->p_m_remote_ref = joinremote->j_serial;
+ isdnport->p_m_remote_id = joinremote->j_remote_id;
+ if (mISDNport && i>=0) {
+ bchannel_event(mISDNport, i, B_EVENT_EXPORTREQUEST);
+ }
+ break;
+
+ case BCHANNEL_RELEASE:
+ case BCHANNEL_ASSIGN_ACK:
+ case BCHANNEL_REMOVE_ACK:
+ /* find mISDNport for stack ID */
+ mISDNport = mISDNport_first;
+ while(mISDNport) {
+ i = 0;
+ ii = mISDNport->b_num;
+ while(i < ii) {
+ if ((unsigned int)(mISDNport->portnum<<8)+i+1+(i>=15) == handle)
+ break;
+ i++;
+ }
+ if (i != ii)
+ break;
+ mISDNport = mISDNport->next;
+ }
+ if (!mISDNport) {
+ PERROR("received assign/remove ack for bchannel's handle=%x, but handle does not exist in any mISDNport structure.\n", handle);
+ break;
+ }
+
+ if (type!=BCHANNEL_RELEASE) {
+ /* ack */
+ chan_trace_header(mISDNport, mISDNport->b_port[i], "MESSAGE_BCHANNEL (from remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, (type==BCHANNEL_ASSIGN_ACK)?"assign_ack":"remove_ack");
+ end_trace();
+ bchannel_event(mISDNport, i, (type==BCHANNEL_ASSIGN_ACK)?B_EVENT_EXPORTED:B_EVENT_IMPORTED);
+ } else {
+ /* release */
+ isdnport = mISDNport->b_port[i];
+ chan_trace_header(mISDNport, isdnport, "MESSAGE_BCHANNEL (from remote application)", DIRECTION_NONE);
+ add_trace("type", NULL, "import request");
+ end_trace();
+ if (isdnport) {
+ isdnport->p_m_remote_ref = 0;
+ isdnport->p_m_remote_id = 0;
+ }
+ bchannel_event(mISDNport, i, B_EVENT_IMPORTREQUEST);
+ }
+ break;
+ default:
+ PERROR("received wrong bchannel message type %d from remote\n", type);
+ }
+}
+
+
+/*
+ * handler
+
+audio transmission procedure:
+-----------------------------
+
+* priority
+three sources of audio transmission:
+- crypto-data high priority
+- tones high priority (also high)
+- remote-data low priority
+
+* elapsed
+a variable that temporarily shows the number of samples elapsed since last transmission process.
+p_m_last_tv_* is used to store that last timestamp. this is used to calculate the time elapsed.
+
+* load
+a variable that is increased whenever data is transmitted.
+it is decreased while time elapses. it stores the number of samples that
+are currently loaded to dsp module.
+since clock in dsp module is the same clock for user space process, these
+times have no skew.
+
+* levels
+there are two levels:
+ISDN_LOAD will give the load that have to be kept in dsp.
+ISDN_MAXLOAD will give the maximum load before dropping.
+
+* procedure for low priority data
+see txfromup() for procedure
+in short: remote data is ignored during high priority tones
+
+* procedure for high priority data
+whenever load is below ISDN_LOAD, load is filled up to ISDN_LOAD
+if no more data is available, load becomes empty again.
+
+'load' variable:
+0 ISDN_LOAD ISDN_MAXLOAD
++--------------------+----------------------+
+| | |
++--------------------+----------------------+
+
+on empty load or on load below ISDN_LOAD, the load is inceased to ISDN_LOAD:
+0 ISDN_LOAD ISDN_MAXLOAD
++--------------------+----------------------+
+|TTTTTTTTTTTTTTTTTTTT| |
++--------------------+----------------------+
+
+on empty load, remote-audio causes the load with the remote audio to be increased to ISDN_LOAD.
+0 ISDN_LOAD ISDN_MAXLOAD
++--------------------+----------------------+
+|TTTTTTTTTTTTTTTTTTTTRRRRR |
++--------------------+----------------------+
+
+ */
+void PmISDN::update_load(void)
+{
+ /* don't trigger load event if: */
+ if (!p_tone_name[0] && !p_m_crypt_msg_loops && !p_m_inband_send_on)
+ return;
+
+ /* don't trigger load event if event already active */
+ if (p_m_loadtimer.active)
+ return;
+
+ schedule_timer(&p_m_loadtimer, 0, 0); /* no delay the first time */
+}
+
+int load_timer(struct lcr_timer *timer, void *instance, int index)
+{
+ class PmISDN *isdnport = (class PmISDN *)instance;
+
+ isdnport->load_tx();
+
+ return 0;
+}
+
+void PmISDN::load_tx(void)
+{
+ int elapsed = 0;
+ int ret;
+ struct timeval current_time;
+
+ /* get elapsed */
+ gettimeofday(¤t_time, NULL);
+ if (p_m_last_tv_sec) {
+ elapsed = 8000 * (current_time.tv_sec - p_m_last_tv_sec)
+ + 8 * (current_time.tv_usec/1000 - p_m_last_tv_msec);
+ }
+ /* set clock of last process! */
+ p_m_last_tv_sec = current_time.tv_sec;
+ p_m_last_tv_msec = current_time.tv_usec/1000;
+
+ /* process only if we have samples and we are active */
+ if (elapsed && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) {
+ /* update load */
+ if (elapsed < p_m_load)
+ p_m_load -= elapsed;
+ else
+ p_m_load = 0;
+
+ /* to send data, tone must be on */
+ if ((p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on) /* what tones? */
+ && (p_m_load < ISDN_LOAD) /* not too much load? */
+ && (p_state==PORT_STATE_CONNECT || p_m_mISDNport->tones || p_m_inband_send_on)) { /* connected or inband-tones? */
+ int tosend = ISDN_LOAD - p_m_load, length;
+ unsigned char buf[MISDN_HEADER_LEN+tosend];
+ struct mISDNhead *frm = (struct mISDNhead *)buf;
+ unsigned char *p = buf+MISDN_HEADER_LEN;
+
+ /* copy inband signalling (e.g. used by ss5) */
+ if (p_m_inband_send_on && tosend) {
+ tosend -= inband_send(p, tosend);
+ }
+
+ /* copy crypto loops */
+ while (p_m_crypt_msg_loops && tosend) {
+ /* how much do we have to send */
+ length = p_m_crypt_msg_len - p_m_crypt_msg_current;
+
+ /* clip tosend */
+ if (length > tosend)
+ length = tosend;
+
+ /* copy message (part) to buffer */
+ memcpy(p, p_m_crypt_msg+p_m_crypt_msg_current, length);
+
+ /* new position */
+ p_m_crypt_msg_current += length;
+ if (p_m_crypt_msg_current == p_m_crypt_msg_len) {
+ /* next loop */
+ p_m_crypt_msg_current = 0;
+ p_m_crypt_msg_loops--;
+ if (!p_m_crypt_msg_loops)
+ update_rxoff();
+// puts("eine loop weniger");
+ }
+
+ /* new length */
+ tosend -= length;
+ }
+
+ /* copy tones */
+ if (p_tone_name[0] && tosend) {
+ tosend -= read_audio(p, tosend);
+ }
+
+ /* send data */
+ if (ISDN_LOAD - p_m_load - tosend > 0) {
+ frm->prim = PH_DATA_REQ;
+ frm->id = 0;
+ ret = sendto(p_m_mISDNport->b_sock[p_m_b_index].fd, buf, MISDN_HEADER_LEN+ISDN_LOAD-p_m_load-tosend, 0, NULL, 0);
+ if (ret <= 0)
+ PERROR("Failed to send to socket %d (samples = %d)\n", p_m_mISDNport->b_sock[p_m_b_index].fd, ISDN_LOAD-p_m_load-tosend);
+ p_m_load += ISDN_LOAD - p_m_load - tosend;
+ }
+ }
+ }
+
+ if (p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on || p_m_load) {
+ schedule_timer(&p_m_loadtimer, 0, ISDN_TRANSMIT*125);
+ }
+}
+
+/* handle timeouts */
+static int mISDN_timeout(struct lcr_timer *timer, void *instance, int i)
+{
+ class PmISDN *isdnport = (class PmISDN *)instance;
+ struct lcr_msg *message;
+
+ PDEBUG(DEBUG_ISDN, "(%s) timeout after %d seconds detected (state=%d).\n", isdnport->p_name, isdnport->p_m_timeout.timeout.tv_sec, isdnport->p_state);
+ /* send timeout to endpoint */
+ message = message_create(isdnport->p_serial, ACTIVE_EPOINT(isdnport->p_epointlist), PORT_TO_EPOINT, MESSAGE_TIMEOUT);
+ message->param.state = isdnport->p_state;
+ message_put(message);
+
+ return 0;
+}
+
+
+/*
+ * whenever we get audio data from bchannel, we process it here
+ */
+void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len)
+{
+ unsigned int cont = *((unsigned int *)data);
+ unsigned char *data_temp;
+ unsigned int length_temp;
+ struct lcr_msg *message;
+ unsigned char *p;
+ int l;
+
+ if (hh->prim == PH_CONTROL_IND) {
+ if (len < 4) {
+ PERROR("SHORT READ OF PH_CONTROL INDICATION\n");
+ return;
+ }
+ if ((cont&(~DTMF_TONE_MASK)) == DTMF_TONE_VAL) {
+ chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
+ add_trace("DTMF", NULL, "%c", cont & DTMF_TONE_MASK);
+ end_trace();
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DTMF);
+ message->param.dtmf = cont & DTMF_TONE_MASK;
+ PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION DTMF digit '%c'\n", p_name, message->param.dtmf);
+ message_put(message);
+ return;
+ }
+ switch(cont) {
+ case DSP_BF_REJECT:
+ chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
+ add_trace("DSP-CRYPT", NULL, "error");
+ end_trace();
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CRYPT);
+ message->param.crypt.type = CC_ERROR_IND;
+ PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION reject of blowfish.\n", p_name);
+ message_put(message);
+ break;
+
+ case DSP_BF_ACCEPT:
+ chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
+ add_trace("DSP-CRYPT", NULL, "ok");
+ end_trace();
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CRYPT);
+ message->param.crypt.type = CC_ACTBF_CONF;
+ PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION accept of blowfish.\n", p_name);
+ message_put(message);
+ break;
+
+ default:
+ chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
+ add_trace("unknown", NULL, "0x%x", cont);
+ end_trace();
+ }
+ return;
+ }
+ if (hh->prim == PH_CONTROL_IND) {
+ switch(hh->id) {
+ default:
+ chan_trace_header(p_m_mISDNport, this, "BCHANNEL control", DIRECTION_IN);
+ add_trace("unknown", NULL, "0x%x", hh->id);
+ end_trace();
+ }
+ return;
+ }
+ if (hh->prim == PH_DATA_REQ || hh->prim == DL_DATA_REQ) {
+ if (!p_m_txdata) {
+ /* if tx is off, it may happen that fifos send us pending informations, we just ignore them */
+ PDEBUG(DEBUG_BCHANNEL, "PmISDN(%s) ignoring tx data, because 'txdata' is turned off\n", p_name);
+ return;
+ }
+ /* see below (same condition) */
+ if (p_state!=PORT_STATE_CONNECT
+ && !p_m_mISDNport->tones)
+ return;
+// printf(".");fflush(stdout);return;
+ if (p_record)
+ record(data, len, 1); // from up
+ return;
+ }
+ if (hh->prim != PH_DATA_IND && hh->prim != DL_DATA_IND) {
+ PERROR("Bchannel received unknown primitve: 0x%x\n", hh->prim);
+ return;
+ }
+
+ /* inband is processed */
+ if (p_m_inband_receive_on)
+ inband_receive(data, len);
+
+ /* calls will not process any audio data unless
+ * the call is connected OR tones feature is enabled.
+ */
+#ifndef DEBUG_COREBRIDGE
+ if (p_state!=PORT_STATE_CONNECT
+ && !p_m_mISDNport->tones)
+ return;
+#endif
+
+#if 0
+ /* the bearer capability must be audio in order to send and receive
+ * audio prior or after connect.
+ */
+ if (!(p_bearerinfo.capability&CLASS_CAPABILITY_AUDIO) && p_state!=PORT_STATE_CONNECT)
+ return;