/* C-Netz transaction handling * * (C) 2016 by Andreas Eversberg * All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "../common/sample.h" #include "../common/debug.h" #include "../common/timer.h" #include "../common/call.h" #include "../common/cause.h" #include "amps.h" //#include "database.h" static const char *trans_state_name(int state) { switch (state) { case 0: return "IDLE"; case TRANS_REGISTER_ACK: return "REGISTER ACK"; case TRANS_REGISTER_ACK_SEND: return "REGISTER ACK SEND"; case TRANS_CALL_MO_ASSIGN: return "CALL ASSIGN MOBILE ORIGINATING"; case TRANS_CALL_MO_ASSIGN_SEND: return "CALL ASSIGN MOBILE ORIGINATING SEND"; case TRANS_CALL_MT_ASSIGN: return "CALL ASSIGN MOBILE TERMINATING"; case TRANS_CALL_MT_ASSIGN_SEND: return "CALL ASSIGN MOBILE TERMINATING SEND"; case TRANS_CALL_MT_ALERT: return "CALL ALERT MOBILE TERMINATING"; case TRANS_CALL_MT_ALERT_SEND: return "CALL ALERT MOBILE TERMINATING SEND"; case TRANS_CALL_REJECT: return "CALL REJECT"; case TRANS_CALL_REJECT_SEND: return "CALL REJECT SEND"; case TRANS_CALL: return "CALL"; case TRANS_CALL_RELEASE: return "CALL RELEASE"; case TRANS_CALL_RELEASE_SEND: return "CALL RELEASE SEND"; case TRANS_PAGE: return "PAGE"; case TRANS_PAGE_SEND: return "PAGE SEND"; case TRANS_PAGE_REPLY: return "PAGE REPLY"; default: return ""; } } const char *trans_short_state_name(int state) { switch (state) { case 0: return "IDLE"; case TRANS_REGISTER_ACK: case TRANS_REGISTER_ACK_SEND: return "REGISTER"; case TRANS_CALL_MO_ASSIGN: case TRANS_CALL_MO_ASSIGN_SEND: case TRANS_CALL_MT_ASSIGN: case TRANS_CALL_MT_ASSIGN_SEND: return "ASSIGN"; case TRANS_CALL_MT_ALERT: case TRANS_CALL_MT_ALERT_SEND: return "ALERT"; case TRANS_CALL_REJECT: case TRANS_CALL_REJECT_SEND: return "REJECT"; case TRANS_CALL: return "CALL"; case TRANS_CALL_RELEASE: case TRANS_CALL_RELEASE_SEND: return "RELEASE"; case TRANS_PAGE: case TRANS_PAGE_SEND: case TRANS_PAGE_REPLY: return "PAGE"; default: return ""; } } /* create transaction */ transaction_t *create_transaction(amps_t *amps, enum amps_trans_state state, uint32_t min1, uint16_t min2, uint8_t msg_type, uint8_t ordq, uint8_t order, uint16_t chan) { sender_t *sender; transaction_t *trans = NULL; amps_t *search_amps; /* search transaction for this subsriber */ for (sender = sender_head; sender; sender = sender->next) { search_amps = (amps_t *) sender; /* search transaction for this callref */ trans = search_transaction_number(search_amps, min1, min2); if (trans) break; } if (trans) { const char *number = amps_min2number(trans->min1, trans->min2); int old_callref = trans->callref; amps_t *old_amps = trans->amps; PDEBUG(DTRANS, DEBUG_NOTICE, "Found alredy pending transaction for subscriber '%s', deleting!\n", number); destroy_transaction(trans); if (old_amps) /* should be... */ amps_go_idle(old_amps); if (old_callref) call_in_release(old_callref, CAUSE_NORMAL); } trans = calloc(1, sizeof(*trans)); if (!trans) { PDEBUG(DTRANS, DEBUG_ERROR, "No memory!\n"); return NULL; } timer_init(&trans->timer, transaction_timeout, trans); trans_new_state(trans, state); trans->min1 = min1; trans->min2 = min2; trans->msg_type = msg_type; trans->ordq = ordq; trans->order = order; trans->chan = chan; const char *number = amps_min2number(trans->min1, trans->min2); PDEBUG(DTRANS, DEBUG_INFO, "Created transaction for subscriber '%s'\n", number); link_transaction(trans, amps); return trans; } /* destroy transaction */ void destroy_transaction(transaction_t *trans) { unlink_transaction(trans); const char *number = amps_min2number(trans->min1, trans->min2); PDEBUG(DTRANS, DEBUG_INFO, "Destroying transaction for subscriber '%s'\n", number); timer_exit(&trans->timer); trans_new_state(trans, 0); free(trans); } /* link transaction to list */ void link_transaction(transaction_t *trans, amps_t *amps) { transaction_t **transp; /* attach to end of list, so first transaction is served first */ PDEBUG(DTRANS, DEBUG_DEBUG, "Linking transaction %p to amps %p\n", trans, amps); trans->amps = amps; trans->next = NULL; transp = &s->trans_list; while (*transp) transp = &((*transp)->next); *transp = trans; amps_display_status(); } /* unlink transaction from list */ void unlink_transaction(transaction_t *trans) { transaction_t **transp; /* unlink */ PDEBUG(DTRANS, DEBUG_DEBUG, "Unlinking transaction %p from amps %p\n", trans, trans->amps); transp = &trans->amps->trans_list; while (*transp && *transp != trans) transp = &((*transp)->next); if (!(*transp)) { PDEBUG(DTRANS, DEBUG_ERROR, "Transaction not in list, please fix!!\n"); abort(); } *transp = trans->next; trans->amps = NULL; amps_display_status(); } transaction_t *search_transaction_number(amps_t *amps, uint32_t min1, uint16_t min2) { transaction_t *trans = amps->trans_list; while (trans) { if (trans->min1 == min1 && trans->min2 == min2) { const char *number = amps_min2number(trans->min1, trans->min2); PDEBUG(DTRANS, DEBUG_DEBUG, "Found transaction for subscriber '%s'\n", number); return trans; } trans = trans->next; } return NULL; } transaction_t *search_transaction_callref(amps_t *amps, int callref) { transaction_t *trans = amps->trans_list; /* just in case, this should not happen */ if (!callref) return NULL; while (trans) { if (trans->callref == callref) { const char *number = amps_min2number(trans->min1, trans->min2); PDEBUG(DTRANS, DEBUG_DEBUG, "Found transaction for subscriber '%s'\n", number); return trans; } trans = trans->next; } return NULL; } void trans_new_state(transaction_t *trans, int state) { PDEBUG(DTRANS, DEBUG_INFO, "Transaction state %s -> %s\n", trans_state_name(trans->state), trans_state_name(state)); trans->state = state; amps_display_status(); } void amps_flush_other_transactions(amps_t *amps, transaction_t *trans) { /* flush after this very trans */ while (trans->next) { PDEBUG(DTRANS, DEBUG_NOTICE, "Kicking other pending transaction\n"); destroy_transaction(trans->next); } /* flush before this very trans */ while (amps->trans_list != trans) { PDEBUG(DTRANS, DEBUG_NOTICE, "Kicking other pending transaction\n"); destroy_transaction(amps->trans_list); } }