+- B_STATE_ACTIVE
+The bchannel is active and cofigured to the Port class needs.
+Also it is linked to a Port class, otherwhise it would be deactivated.
+
+- 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];
+ double timer = mISDNport->b_timer[i];
+ 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);
+ 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);
+ state = B_STATE_ACTIVATING;
+ timer = now_d + 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);
+ 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);
+ state = B_STATE_DEACTIVATING;
+ timer = now_d + 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);
+ 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);
+ state = B_STATE_DEACTIVATING;
+ timer = now_d + 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);
+ 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);
+ state = B_STATE_DEACTIVATING;
+ timer = now_d + 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);
+ 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);
+ 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);
+ state = B_STATE_ACTIVATING;
+ timer = now_d + 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);
+ 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);
+ state = B_STATE_ACTIVATING;
+ timer = now_d + 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);
+ timer = now_d + B_TIMER_ACTIVATING;
+ break;
+
+ case B_STATE_DEACTIVATING:
+ _bchannel_activate(mISDNport, i, 0);
+ timer = now_d + 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;
+ mISDNport->b_timer[i] = timer;
+}
+
+
+
+
+/*
+ * 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)