/* dss1user.c * * Author Karsten Keil * * Copyright 2007 by Karsten Keil * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE * version 2.1 as published by the Free Software Foundation. * * This code 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 LESSER GENERAL PUBLIC LICENSE for more details. * */ #include #include #include #include #include #include #include "helper.h" #include "dss1.h" #include "layer3.h" #include "debug.h" static int dss1man(l3_process_t *, u_int, struct l3_msg *); static int l3dss1_message(l3_process_t *pc, u_char mt) { struct l3_msg *l3m; int ret = 0; if (!(l3m = MsgStart(pc, mt))) return -ENOMEM; SendMsg(pc, l3m, -1); return(ret); } static void l3dss1_message_cause(l3_process_t *pc, u_char mt, u_char cause) { struct l3_msg *l3m; unsigned char c[2]; if (!(l3m = MsgStart(pc, mt))) return; c[0] = 0x80 | CAUSE_LOC_USER; c[1] = 0x80 | cause; add_layer3_ie(l3m, IE_CAUSE, 2, c); SendMsg(pc, l3m, -1); } static void l3dss1_status_send(l3_process_t *pc, u_char cause) { struct l3_msg *l3m; unsigned char c[2]; if (!(l3m = MsgStart(pc, MT_STATUS))) return; c[0] = 0x80 | CAUSE_LOC_USER; c[1] = 0x80 | cause; add_layer3_ie(l3m, IE_CAUSE, 2, c); c[0] = pc->state & 0x3f; add_layer3_ie(l3m, IE_CALL_STATE, 1, c); SendMsg(pc, l3m, -1); } static void l3dss1_msg_without_setup(l3_process_t *pc, u_char cause) { /* This routine is called if here was no SETUP made (checks in dss1up and in * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code * MT_STATUS_ENQUIRE in the NULL state is handled too */ switch (cause) { case 81: /* invalid callreference */ case 88: /* incomp destination */ case 96: /* mandory IE missing */ case 100: /* invalid IE contents */ case 101: /* incompatible Callstate */ l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); break; } release_l3_process(pc); } static int ie_ALERTING[] = {IE_BEARER, IE_NOTIFY, IE_CHANNEL_ID | IE_MANDATORY_1, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_REDIRECTION_NR, IE_HLC, IE_USER_USER, -1}; static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_NOTIFY, IE_CHANNEL_ID | IE_MANDATORY_1, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_REDIRECTION_NR, IE_HLC, -1}; static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL, IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1}; static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLED_PN, -1}; static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS | IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; /* a RELEASE_COMPLETE with errors don't require special actions static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; */ static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_DISPLAY, -1}; static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER | IE_MANDATORY, IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN, IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIRECTING_NR, IE_LLC, IE_HLC, IE_USER_USER, -1}; static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1}; static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1}; static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_FACILITY, IE_DISPLAY, -1}; static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_HOLD[] = {IE_DISPLAY, -1}; static int ie_HOLD_ACKNOWLEDGE[] = {IE_DISPLAY, -1}; static int ie_HOLD_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_RETRIEVE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_DISPLAY, -1}; static int ie_RETRIEVE_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_DISPLAY, -1}; static int ie_RETRIEVE_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; /* not used * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY, * IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1}; * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND | * IE_MANDATORY, -1}; * static int ie_REGISTER[] = {IE_FACILITY, IE_DISPLAY, -1}; */ static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1}; static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1}; struct ie_len { int ie; int len; }; static struct ie_len max_ie_len[] = { // not implemented {IE_SEGMENT, 4}, {IE_BEARER, 12}, {IE_CAUSE, 32}, {IE_CALL_ID, 10}, {IE_CALL_STATE, 3}, {IE_CHANNEL_ID, 34}, {IE_FACILITY, 255}, {IE_PROGRESS, 4}, {IE_NET_FAC, 255}, {IE_NOTIFY, 255}, /* 3-* Q.932 Section 9 */ {IE_DISPLAY, 82}, {IE_DATE, 8}, {IE_KEYPAD, 34}, {IE_SIGNAL, 3}, {IE_INFORATE, 6}, {IE_E2E_TDELAY, 11}, {IE_TDELAY_SEL, 5}, {IE_PACK_BINPARA, 3}, {IE_PACK_WINSIZE, 4}, {IE_PACK_SIZE, 4}, {IE_CUG, 7}, {IE_REV_CHARGE, 3}, {IE_CONNECT_PN, 24}, {IE_CONNECT_SUB, 23}, {IE_CALLING_PN, 24}, {IE_CALLING_SUB, 23}, {IE_CALLED_PN, 24}, {IE_CALLED_SUB, 23}, {IE_REDIRECTING_NR, 25}, {IE_REDIRECTION_NR, 24}, {IE_TRANS_SEL, 255}, {IE_RESTART_IND, 3}, {IE_LLC, 18}, {IE_HLC, 5}, {IE_USER_USER, 131}, {-1,0}, }; static int ie_in_set(l3_process_t *pc, u_char ie, int *checklist) { int ret = 1; while (*checklist != -1) { if ((*checklist & 0xff) == ie) { if (ie & 0x80) return(-ret); else return(ret); } ret++; checklist++; } return 0; } static int check_infoelements(l3_process_t *pc, struct l3_msg *l3m, int *checklist, int mt) { unsigned char **v_ie, ie; int i, l, pos; int err_len = 0, err_compr = 0, err_ureg = 0; v_ie = &l3m->bearer_capability; for (i = 0; i < IE_COUNT; i++) { if (v_ie[i]) { ie = l3_pos2ie(i); if (!(pos = ie_in_set(pc, ie, checklist))) { eprint("Received IE %x not allowed (mt=%x)\n", ie, mt); err_ureg++; } l = *v_ie[i]; if (l > max_ie_len[i].len) err_len++; } } if (l3m->comprehension_req) { err_compr++; } if (err_compr) return Q931_ERROR_COMPREH; if (err_ureg) return Q931_ERROR_UNKNOWN; if (err_len) return Q931_ERROR_IELEN; return 0; } /* verify if a message type exists and contain no IE error */ static int l3dss1_check_messagetype_validity(l3_process_t *pc, int mt) { switch (mt) { case MT_ALERTING: case MT_CALL_PROCEEDING: case MT_CONNECT: case MT_CONNECT_ACKNOWLEDGE: case MT_DISCONNECT: case MT_INFORMATION: case MT_FACILITY: case MT_NOTIFY: case MT_PROGRESS: case MT_RELEASE: case MT_RELEASE_COMPLETE: case MT_SETUP: case MT_SETUP_ACKNOWLEDGE: case MT_RESUME_ACKNOWLEDGE: case MT_RESUME_REJECT: case MT_SUSPEND_ACKNOWLEDGE: case MT_SUSPEND_REJECT: case MT_USER_INFORMATION: case MT_RESTART: case MT_RESTART_ACKNOWLEDGE: case MT_CONGESTION_CONTROL: case MT_STATUS: case MT_STATUS_ENQUIRY: case MT_HOLD: case MT_HOLD_ACKNOWLEDGE: case MT_HOLD_REJECT: case MT_RETRIEVE: case MT_RETRIEVE_ACKNOWLEDGE: case MT_RETRIEVE_REJECT: case MT_REGISTER: break; case MT_RESUME: /* RESUME only in user->net */ case MT_SUSPEND: /* SUSPEND only in user->net */ default: l3dss1_status_send(pc, CAUSE_MT_NOTIMPLEMENTED); return 1; } return 0; } static void l3dss1_std_ie_err(l3_process_t *pc, int ret) { switch(ret) { case 0: break; case Q931_ERROR_COMPREH: l3dss1_status_send(pc, CAUSE_MANDATORY_IE_MISS); break; case Q931_ERROR_UNKNOWN: l3dss1_status_send(pc, CAUSE_IE_NOTIMPLEMENTED); break; case Q931_ERROR_IELEN: l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); break; default: break; } } static int l3dss1_get_cid(l3_process_t *pc, struct l3_msg *l3m) { memset(pc->cid, 0, 4); /* clear cid */ if (!l3m->channel_id) { dprint(DBGM_L3, "port%d no channel id\n", pc->l2if->l2addr.dev); return -1; } if (l3m->channel_id[0] < 1) { dprint(DBGM_L3, "port%d ERROR: channel id short read\n", pc->l2if->l2addr.dev); return -2; } if (l3m->channel_id[0] > 3) { dprint(DBGM_L3, "port%d ERROR: channel id too large\n", pc->l2if->l2addr.dev); return -3; } if (l3m->channel_id[1] & 0x40) { dprint(DBGM_L3, "port%d ERROR: channel id for adjected channels not supported\n", pc->l2if->l2addr.dev); return -4; } if (l3m->channel_id[1] & 0x04) { dprint(DBGM_L3, "port%d channel id with dchannel\n", pc->l2if->l2addr.dev); goto done; } if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if (l3m->channel_id[1] & 0x20) { dprint(DBGM_L3, "port%d ERROR: channel id not for BRI interface\n", pc->l2if->l2addr.dev); return -11; } } else { /* primary rate */ if (!(l3m->channel_id[1] & 0x20)) { dprint(DBGM_L3, "port%d ERROR: channel id not for PRI interface\n", pc->l2if->l2addr.dev); return -11; } if (l3m->channel_id[0] < 3) goto done; if (l3m->channel_id[2] & 0x10) { /* map not allowed by ETSI */ dprint(DBGM_L3, "port%d ERROR: channel id uses channel map\n", pc->l2if->l2addr.dev); return -12; } } done: memcpy(pc->cid, l3m->channel_id, l3m->channel_id[0] + 1); return 0; } static int l3dss1_get_cause(l3_process_t *pc, struct l3_msg *l3m) { unsigned char l; unsigned char *p; if (l3m->cause) { p = l3m->cause; l = *p++; if (l>30) { return -30; } if (l) l--; else { return -2; } if (l && !(*p & 0x80)) { l--; p++; /* skip recommendation */ } p++; if (l) { if (!(*p & 0x80)) { return(-3); } pc->rm_cause = *p & 0x7F; } else { return -4; } } else return -1; return 0; } static void l3dss1_release_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { StopAllL3Timer(pc); if (l3m) { SendMsg(pc, l3m, 19); } else { newl3state(pc, 19); l3dss1_message(pc, MT_RELEASE); } L3AddTimer(&pc->timer1, T308, CC_T308_1); } static void l3dss1_setup_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { l3_msg_increment_refcnt(l3m); pc->t303msg = l3m; SendMsg(pc, l3m, 1); pc->n303 = N303; L3DelTimer(&pc->timer1); L3AddTimer(&pc->timer1, T303, CC_T303); } static void l3dss1_disconnect_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { unsigned char c[2]; if (pc->t303msg) { free_l3_msg(pc->t303msg); pc->t303msg = NULL; } StopAllL3Timer(pc); if (l3m) { if (!l3m->cause) { c[0] = 0x80 | CAUSE_LOC_USER; c[1] = 0x80 | CAUSE_NORMALUNSPECIFIED; add_layer3_ie(l3m, IE_CAUSE, 2, c); pc->cause = CAUSE_NORMALUNSPECIFIED; } else /* we do not handle 3a here - not used in mISDN */ pc->cause = l3m->cause[2] & 0x7f; SendMsg(pc, l3m, 11); } else { newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_NORMALUNSPECIFIED); pc->cause = CAUSE_NORMALUNSPECIFIED; } L3AddTimer(&pc->timer1, T305, CC_T305); } static void l3dss1_connect_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (!pc->cid[0]) { /* no channel was selected */ l3dss1_disconnect_req(pc, pr, NULL); if (l3m) free_l3_msg(l3m); return; } if (l3m) { SendMsg(pc, l3m, 8); } else { newl3state(pc, 8); l3dss1_message(pc, MT_CONNECT); } L3DelTimer(&pc->timer1); L3AddTimer(&pc->timer1, T313, CC_T313); } static void l3dss1_release_cmpl_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { StopAllL3Timer(pc); if (l3m) { SendMsg(pc, l3m, 0); } else { newl3state(pc, 0); l3dss1_message(pc, MT_RELEASE_COMPLETE); } release_l3_process(pc); } static void l3dss1_alert_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, 7); } else { newl3state(pc, 7); l3dss1_message(pc, MT_ALERTING); } L3DelTimer(&pc->timer1); } static void l3dss1_proceed_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, 9); } else { newl3state(pc, 9); l3dss1_message(pc, MT_CALL_PROCEEDING); } L3DelTimer(&pc->timer1); } static void l3dss1_setup_ack_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, 25); } else { newl3state(pc, 25); l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE); } L3DelTimer(&pc->timer1); L3AddTimer(&pc->timer1, T302, CC_T302); } static void l3dss1_suspend_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, 15); } else { newl3state(pc, 15); l3dss1_message(pc, MT_SUSPEND); } L3AddTimer(&pc->timer1, T319, CC_T319); } static void l3dss1_resume_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, 17); } else { newl3state(pc, 17); l3dss1_message(pc, MT_RESUME); } L3AddTimer(&pc->timer1, T318, CC_T318); } static void l3dss1_status_enq_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) free_l3_msg(l3m); l3dss1_message(pc, MT_STATUS_ENQUIRY); } static void l3dss1_information_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (pc->state == 2) { L3DelTimer(&pc->timer1); L3AddTimer(&pc->timer1, T304, CC_T304); } if (l3m) { SendMsg(pc, l3m, -1); } } static void l3dss1_notify_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, -1); } } static void l3dss1_progress_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, -1); } } static void l3dss1_facility_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, -1); } } static void l3dss1_restart_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { SendMsg(pc, l3m, -1); } } static void l3dss1_register_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { SendMsg(pc, l3m, 31); } static void l3dss1_release_cmpl(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { StopAllL3Timer(pc); newl3state(pc, 0); mISDN_l3up(pc, MT_RELEASE_COMPLETE, l3m); release_l3_process(pc); } static void l3dss1_alerting(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; if (!(ret = l3dss1_get_cid(pc, l3m))) { if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if ((0 == (pc->cid[1] & 3)) || (3 == (pc->cid[1] & 3))) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } } /* valid test for primary rate ??? */ } else if (1 == pc->state) { unsigned char cause; if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } ret = check_infoelements(pc, l3m, ie_ALERTING, MT_ALERTING); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } L3DelTimer(&pc->timer1); /* T304 */ if (pc->t303msg) { free_l3_msg(pc->t303msg); pc->t303msg = NULL; } newl3state(pc, 4); if (ret) l3dss1_std_ie_err(pc, ret); mISDN_l3up(pc, MT_ALERTING, l3m); } static void l3dss1_call_proc(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; if (!(ret = l3dss1_get_cid(pc, l3m))) { if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if ((0 == (pc->cid[1] & 3)) || (3 == (pc->cid[1] & 3))) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } } /* valid test for primary rate ??? */ } else if (1 == pc->state) { unsigned char cause; if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } /* Now we are on none mandatory IEs */ ret = check_infoelements(pc, l3m, ie_CALL_PROCEEDING, MT_CALL_PROCEEDING); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } L3DelTimer(&pc->timer1); if (pc->t303msg) { free_l3_msg(pc->t303msg); pc->t303msg = NULL; } newl3state(pc, 3); L3AddTimer(&pc->timer1, T310, CC_T310); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); mISDN_l3up(pc, MT_CALL_PROCEEDING, l3m); } static void l3dss1_connect(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; if (!(ret = l3dss1_get_cid(pc, l3m))) { if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if ((0 == (pc->cid[1] & 3)) || (3 == (pc->cid[1] & 3))) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } } /* valid test for primary rate ??? */ } else if (1 == pc->state) { unsigned char cause; if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } ret = check_infoelements(pc, l3m, ie_CONNECT, MT_CONNECT); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } L3DelTimer(&pc->timer1); /* T303 or T310 */ if (pc->t303msg) { free_l3_msg(pc->t303msg); pc->t303msg = NULL; } l3dss1_message(pc, MT_CONNECT_ACKNOWLEDGE); newl3state(pc, 10); if (ret) l3dss1_std_ie_err(pc, ret); mISDN_l3up(pc, MT_CONNECT, l3m); } static void l3dss1_connect_ack(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; ret = check_infoelements(pc, l3m, ie_CONNECT_ACKNOWLEDGE, MT_CONNECT_ACKNOWLEDGE); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } newl3state(pc, 10); L3DelTimer(&pc->timer1); if (ret) l3dss1_std_ie_err(pc, ret); mISDN_l3up(pc, MT_CONNECT_ACKNOWLEDGE, l3m); } static void l3dss1_disconnect(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; unsigned char cause = 0; StopAllL3Timer(pc); if ((ret = l3dss1_get_cause(pc, l3m))) { if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; } else if (pc->state == 7) /* Call Received*/ cause = pc->rm_cause; ret = check_infoelements(pc, l3m, ie_DISCONNECT, MT_DISCONNECT); if (Q931_ERROR_COMPREH == ret) cause = CAUSE_MANDATORY_IE_MISS; else if ((!cause) && (Q931_ERROR_UNKNOWN == ret)) cause = CAUSE_IE_NOTIMPLEMENTED; ret = pc->state; if (cause) newl3state(pc, 19); else newl3state(pc, 12); if (11 != ret) { mISDN_l3up(pc, MT_DISCONNECT, l3m); } else if (!cause) { l3dss1_release_req(pc, pr, NULL); free_l3_msg(l3m); } else free_l3_msg(l3m); if (cause) { l3dss1_message_cause(pc, MT_RELEASE, cause); L3AddTimer(&pc->timer1, T308, CC_T308_1); } } static void l3dss1_setup_ack(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; if (!(ret = l3dss1_get_cid(pc, l3m))) { if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if ((0 == (pc->cid[1] & 3)) || (3 == (pc->cid[1] & 3))) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } } /* valid test for primary rate ??? */ } else if (1 == pc->state) { unsigned char cause; if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } /* Now we are on none mandatory IEs */ ret = check_infoelements(pc, l3m, ie_SETUP_ACKNOWLEDGE, MT_SETUP_ACKNOWLEDGE); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } L3DelTimer(&pc->timer1); if (pc->t303msg) { free_l3_msg(pc->t303msg); pc->t303msg = NULL; } newl3state(pc, 2); L3AddTimer(&pc->timer1, T304, CC_T304); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); mISDN_l3up(pc, MT_SETUP_ACKNOWLEDGE, l3m); } static void l3dss1_setup(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int err = 0; /* * Bearer Capabilities */ /* only the first occurence 'll be detected ! */ if (l3m->bearer_capability) { /* * Application has to analyze and reject */ } else { /* ETS 300-104 1.3.3 */ l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS); free_l3_msg(l3m); return; } /* * Channel Identification */ if (!(err = l3dss1_get_cid(pc, l3m))) { if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if (!test_bit(MISDN_FLG_PTP, &pc->L3->ml3.options)) { if (3 == (pc->cid[1] & 3)) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } } } /* valid test for primary rate ??? */ } else { unsigned char cause; if (err == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } /* Now we are on none mandatory IEs */ err = check_infoelements(pc, l3m, ie_SETUP, MT_SETUP); if (Q931_ERROR_COMPREH == err) { l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS); free_l3_msg(l3m); return; } newl3state(pc, 6); L3DelTimer(&pc->timer1); L3AddTimer(&pc->timer1, T_CTRL, CC_TCTRL); if (err) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, err); mISDN_l3up(pc, MT_SETUP, l3m); } static void l3dss1_register(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { dprint(DBGM_L3, "port%d REGISTER\n", pc->l2if->l2addr.dev); newl3state(pc, 31); mISDN_l3up(pc, MT_REGISTER, l3m); } static void l3dss1_reset(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) free_l3_msg(l3m); release_l3_process(pc); } static void l3dss1_release(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret, cause=0; StopAllL3Timer(pc); if ((ret = l3dss1_get_cause(pc, l3m))) { if ((ret == -1) && (pc->state != 11)) cause = CAUSE_MANDATORY_IE_MISS; else if (ret != -1) cause = CAUSE_INVALID_CONTENTS; } ret = check_infoelements(pc, l3m, ie_RELEASE, MT_RELEASE); if (Q931_ERROR_COMPREH == ret) cause = CAUSE_MANDATORY_IE_MISS; else if ((Q931_ERROR_UNKNOWN == ret) && (!cause)) cause = CAUSE_IE_NOTIMPLEMENTED; if (cause) l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); else l3dss1_message(pc, MT_RELEASE_COMPLETE); mISDN_l3up(pc, MT_RELEASE, l3m); newl3state(pc, 0); release_l3_process(pc); } static void l3dss1_progress(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int err = 0; unsigned char cause = CAUSE_INVALID_CONTENTS; if (l3m->progress) { if (l3m->progress[0] != 2) { err = 1; } else if (!(l3m->progress[1] & 0x70)) { switch (l3m->progress[1]) { case 0x80: case 0x81: case 0x82: case 0x84: case 0x85: case 0x87: case 0x8a: switch (l3m->progress[2]) { case 0x81: case 0x82: case 0x83: case 0x84: case 0x88: break; default: err = 2; break; } break; default: err = 3; break; } } } else { cause = CAUSE_MANDATORY_IE_MISS; err = 4; } if (err) { l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } /* Now we are on none mandatory IEs */ err = check_infoelements(pc, l3m, ie_PROGRESS, MT_PROGRESS); if (err) l3dss1_std_ie_err(pc, err); /* * clear T310 if running (should be cleared by a Progress * Message, according to ETSI). * */ L3DelTimer(&pc->timer1); if (pc->t303msg) { free_l3_msg(pc->t303msg); pc->t303msg = NULL; } if (Q931_ERROR_COMPREH != err) { mISDN_l3up(pc, MT_PROGRESS, l3m); } else free_l3_msg(l3m); } static void l3dss1_notify(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int err = 0; unsigned char cause = CAUSE_INVALID_CONTENTS; if (l3m->notify) { if (l3m->notify[0] != 1) { err = 1; #if 0 // Why } else { switch (l3m->notify[1]) { case 0x80: case 0x81: case 0x82: break; default: err = 2; break; } #endif } } else { cause = CAUSE_MANDATORY_IE_MISS; err = 3; } if (err) { l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } /* Now we are on none mandatory IEs */ err = check_infoelements(pc, l3m, ie_NOTIFY, MT_NOTIFY); if (err) l3dss1_std_ie_err(pc, err); if (Q931_ERROR_COMPREH != err) { mISDN_l3up(pc, MT_NOTIFY, l3m); } else free_l3_msg(l3m); } static void l3dss1_status_enq(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; ret = check_infoelements(pc, l3m, ie_STATUS_ENQUIRY, MT_STATUS_ENQUIRY); l3dss1_std_ie_err(pc, ret); l3dss1_status_send(pc, CAUSE_STATUS_RESPONSE); mISDN_l3up(pc, MT_STATUS_ENQUIRY, l3m); } static void l3dss1_information(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; ret = check_infoelements(pc, l3m, ie_INFORMATION, MT_INFORMATION); if (ret) l3dss1_std_ie_err(pc, ret); if (pc->state == 25) { /* overlap receiving */ L3DelTimer(&pc->timer1); L3AddTimer(&pc->timer1, T302, CC_T302); } mISDN_l3up(pc, MT_INFORMATION, l3m); } static void l3dss1_release_ind(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int callState = -1; if (pc->state == 19) { /* ETS 300-102 5.3.5 */ newl3state(pc, 0); mISDN_l3up(pc, MT_RELEASE, l3m); release_l3_process(pc); } else { if (l3m->call_state) { if (1 == l3m->call_state[0]) callState = l3m->call_state[1]; } if (callState == 0) { /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 * set down layer 3 without sending any message */ newl3state(pc, 0); mISDN_l3up(pc, MT_RELEASE, l3m); release_l3_process(pc); } else { mISDN_l3up(pc, MT_RELEASE, l3m); } } } static void l3dss1_restart(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); release_l3_process(pc); if (l3m) free_l3_msg(l3m); } static void l3dss1_status(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret = 0; unsigned char cause = 0, callState = 0xff; if ((ret = l3dss1_get_cause(pc, l3m))) { if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; } if (l3m->call_state) { if (1 == l3m->call_state[0]) { callState = l3m->call_state[1]; if (!ie_in_set(pc, callState, l3_valid_states)) cause = CAUSE_INVALID_CONTENTS; } else cause = CAUSE_INVALID_CONTENTS; } else cause = CAUSE_MANDATORY_IE_MISS; if (!cause) { /* no error before */ ret = check_infoelements(pc, l3m, ie_STATUS, MT_STATUS); if (Q931_ERROR_COMPREH == ret) cause = CAUSE_MANDATORY_IE_MISS; else if (Q931_ERROR_UNKNOWN == ret) cause = CAUSE_IE_NOTIMPLEMENTED; } if (cause) { l3dss1_status_send(pc, cause); if (cause != CAUSE_IE_NOTIMPLEMENTED) { free_l3_msg(l3m); return; } } if (l3m->cause) cause = pc->rm_cause & 0x7f; if ((cause == CAUSE_PROTOCOL_ERROR) && (callState == 0)) { /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... * if received MT_STATUS with cause == 111 and call * state == 0, then we must set down layer 3 */ newl3state(pc, 0); mISDN_l3up(pc, MT_STATUS, l3m); release_l3_process(pc); } else mISDN_l3up(pc, MT_STATUS, l3m); } static void l3dss1_facility(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; ret = check_infoelements(pc, l3m, ie_FACILITY, MT_FACILITY); l3dss1_std_ie_err(pc, ret); if (!l3m->facility) { free_l3_msg(l3m); return; } mISDN_l3up(pc, MT_FACILITY, l3m); } static void l3dss1_suspend_ack(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; L3DelTimer(&pc->timer1); newl3state(pc, 0); /* We don't handle suspend_ack for IE errors now */ ret = check_infoelements(pc, l3m, ie_SUSPEND_ACKNOWLEDGE, MT_SUSPEND_ACKNOWLEDGE); if (ret) wprint("Got error %d from check_infoelements - ignored\n", ret); mISDN_l3up(pc, MT_SUSPEND_ACKNOWLEDGE, l3m); release_l3_process(pc); } static void l3dss1_suspend_rej(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; unsigned char cause; if ((ret = l3dss1_get_cause(pc, l3m))) { if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } ret = check_infoelements(pc, l3m, ie_SUSPEND_REJECT, MT_SUSPEND_REJECT); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } L3DelTimer(&pc->timer1); mISDN_l3up(pc, MT_SUSPEND_REJECT, l3m); newl3state(pc, 10); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_resume_ack(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; if (!(ret = l3dss1_get_cid(pc, l3m))) { if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if ((0 == (pc->cid[1] & 3)) || (3 == (pc->cid[1] & 3))) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } } /* valid test for primary rate ??? */ } else if (1 == pc->state) { unsigned char cause; if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } ret = check_infoelements(pc, l3m, ie_RESUME_ACKNOWLEDGE, MT_RESUME_ACKNOWLEDGE); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } L3DelTimer(&pc->timer1); mISDN_l3up(pc, MT_RESUME_ACKNOWLEDGE, l3m); newl3state(pc, 10); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_resume_rej(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; unsigned char cause; if ((ret = l3dss1_get_cause(pc, l3m))) { if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } ret = check_infoelements(pc, l3m, ie_RESUME_REJECT, MT_RESUME_REJECT); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } L3DelTimer(&pc->timer1); mISDN_l3up(pc, MT_RESUME_REJECT, l3m); newl3state(pc, 0); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); release_l3_process(pc); } static void l3dss1_global_restart(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { unsigned char ri; l3_process_t *up, *n; struct l3_msg *nl3m; int ret; L3DelTimer(&pc->timer1); if (l3m->restart_ind) { ri = l3m->restart_ind[1]; } else { ri = 0x86; } memset(pc->cid, 0, 4); /* clear cid */ if (!(ret = l3dss1_get_cid(pc, l3m))) { if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if (0 == (pc->cid[1] & 3)) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } } /* valid test for primary rate ??? */ } else if (ret != -1) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } newl3state(pc, 2); list_for_each_entry_safe(up, n, &pc->L3->plist, list) { if ((ri & 6) == 6) dss1man(up, MT_RESTART, NULL); else if (test_bit(FLG_BASICRATE, &pc->L3->ml3.options)) { if ((up->cid[1] & 3) == (pc->cid[1] & 3)) dss1man(up, MT_RESTART, NULL); } else { if ((up->cid[3] & 0x7f) == (pc->cid[3] & 0x7f)) dss1man(up, MT_RESTART, NULL); } } nl3m = MsgStart(pc, MT_RESTART_ACKNOWLEDGE); if (pc->cid[0]) /*copy channel ie*/ add_layer3_ie(nl3m, IE_CHANNEL_ID, pc->cid[0], pc->cid + 1); free_l3_msg(l3m); add_layer3_ie(nl3m, IE_RESTART_IND, 1, &ri); SendMsg(pc, nl3m, -1); newl3state(pc, 0); } static void l3dss1_restart_ack(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { eprint("Restart Acknowledge\n"); } static void l3dss1_dummy(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) { dprint(DBGM_L3, "Got %s (%x) dss1 TE state %d - unhandled\n", _mi_msg_type2str(l3m->type), l3m->type, pc->state); free_l3_msg(l3m); } } static void l3dss1_hold_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (!test_bit(MISDN_FLG_PTP, &pc->L3->ml3.options)) { if ((pc->state & VALID_HOLD_STATES_PTMP) == 0) { /* not a valid HOLD state for PtMP */ return; } } switch(pc->aux_state) { case AUX_IDLE: break; default: eprint("RETRIEVE_REQ in wrong aux state %d\n", pc->aux_state); case AUX_HOLD_IND: /* maybe collition, ignored */ return; } if (l3m) SendMsg(pc, l3m, -1); else l3dss1_message(pc, MT_HOLD); pc->aux_state = AUX_HOLD_REQ; L3AddTimer(&pc->timer2, THOLD, CC_THOLD); } static void l3dss1_hold_ack_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { switch(pc->aux_state) { case AUX_HOLD_IND: break; default: eprint("HOLD_ACK in wrong aux state %d\n", pc->aux_state); return; } if (l3m) SendMsg(pc, l3m, -1); else l3dss1_message(pc, MT_HOLD_ACKNOWLEDGE); pc->aux_state = AUX_CALL_HELD; } static void l3dss1_hold_rej_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { switch(pc->aux_state) { case AUX_HOLD_IND: break; default: eprint("HOLD_REJ in wrong aux state %d\n", pc->aux_state); return; } if (l3m) SendMsg(pc, l3m, -1); else l3dss1_message_cause(pc, MT_HOLD_REJECT, CAUSE_RESOURCES_UNAVAIL); // FIXME pc->aux_state = AUX_IDLE; } static void l3dss1_hold_ind(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; ret = check_infoelements(pc, l3m, ie_HOLD, MT_HOLD); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } if (test_bit(MISDN_FLG_PTP, &pc->L3->ml3.options)) { if ((pc->state & VALID_HOLD_STATES_PTP) == 0) { /* not a valid HOLD state for PtP */ l3dss1_message_cause(pc, MT_HOLD_REJECT, CAUSE_NOTCOMPAT_STATE); free_l3_msg(l3m); return; } } else { if ((pc->state & VALID_HOLD_STATES_PTMP) == 0) { /* not a valid HOLD state for PtMP */ l3dss1_message_cause(pc, MT_HOLD_REJECT, CAUSE_NOTCOMPAT_STATE); free_l3_msg(l3m); return; } } switch(pc->aux_state) { case AUX_HOLD_REQ: L3DelTimer(&pc->timer2); /* FALLTHRU */ case AUX_IDLE: mISDN_l3up(pc, MT_HOLD, l3m); pc->aux_state = AUX_HOLD_IND; break; default: l3dss1_message_cause(pc, MT_HOLD_REJECT, CAUSE_NOTCOMPAT_STATE); free_l3_msg(l3m); return; } if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_hold_rej(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; unsigned char cause; if ((ret = l3dss1_get_cause(pc, l3m))) { if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } ret = check_infoelements(pc, l3m, ie_HOLD_REJECT, MT_HOLD_REJECT); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } switch(pc->aux_state) { case AUX_HOLD_REQ: L3DelTimer(&pc->timer2); break; default: eprint("HOLD_REJ in wrong aux state %d\n", pc->aux_state); } pc->aux_state = AUX_IDLE; mISDN_l3up(pc, MT_HOLD_REJECT, l3m); } static void l3dss1_hold_ignore(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) free_l3_msg(l3m); } static void l3dss1_hold_req_ignore(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (l3m) free_l3_msg(l3m); } static void l3dss1_hold_ack(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; ret = check_infoelements(pc, l3m, ie_HOLD_ACKNOWLEDGE, MT_HOLD_ACKNOWLEDGE); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } switch(pc->aux_state) { case AUX_HOLD_REQ: L3DelTimer(&pc->timer2); mISDN_l3up(pc, MT_HOLD_ACKNOWLEDGE, l3m); pc->aux_state = AUX_CALL_HELD; break; default: eprint("HOLD_ACK in wrong aux state %d\n", pc->aux_state); free_l3_msg(l3m); } if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_retrieve_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { if (!test_bit(MISDN_FLG_PTP, &pc->L3->ml3.options)) { if ((pc->state & (VALID_HOLD_STATES_PTMP | SBIT(12))) == 0) { /* not a valid RETRIEVE state for PtMP */ if (l3m) free_l3_msg(l3m); return; } } switch(pc->aux_state) { case AUX_CALL_HELD: break; default: eprint("RETRIEVE_REQ in wrong aux state %d\n", pc->aux_state); /* FALLTHRU */ case AUX_RETRIEVE_IND: /* maybe collition, ignored */ if (l3m) free_l3_msg(l3m); return; } if (l3m) { SendMsg(pc, l3m, -1); } else { newl3state(pc, -1); l3dss1_message(pc, MT_RETRIEVE); } pc->aux_state = AUX_RETRIEVE_REQ; L3AddTimer(&pc->timer2, TRETRIEVE, CC_TRETRIEVE); } static void l3dss1_retrieve_ack_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { switch(pc->aux_state) { case AUX_RETRIEVE_IND: break; default: eprint("HOLD_REJ in wrong aux state %d\n", pc->aux_state); if (l3m) free_l3_msg(l3m); return; } if (l3m) SendMsg(pc, l3m, -1); else l3dss1_message(pc, MT_RETRIEVE_ACKNOWLEDGE); pc->aux_state = AUX_IDLE; } static void l3dss1_retrieve_rej_req(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { switch(pc->aux_state) { case AUX_RETRIEVE_IND: break; default: eprint("HOLD_REJ in wrong aux state %d\n", pc->aux_state); if (l3m) free_l3_msg(l3m); return; } if (l3m) SendMsg(pc, l3m, -1); else l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_RESOURCES_UNAVAIL); // FIXME pc->aux_state = AUX_CALL_HELD; } static void l3dss1_retrieve_ind(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; if (test_bit(MISDN_FLG_PTP, &pc->L3->ml3.options)) { if ((pc->state & (VALID_HOLD_STATES_PTP | SBIT(12))) == 0) { /* not a valid RETRIEVE state for PtP */ l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_NOTCOMPAT_STATE); free_l3_msg(l3m); return; } } else { if ((pc->state & (VALID_HOLD_STATES_PTMP | SBIT(12))) == 0) { /* not a valid RETRIEVE state for PtMP */ l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_NOTCOMPAT_STATE); free_l3_msg(l3m); return; } } ret = check_infoelements(pc, l3m, ie_RETRIEVE, MT_RETRIEVE); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } switch(pc->aux_state) { case AUX_RETRIEVE_REQ: L3DelTimer(&pc->timer2); /* FALLTHRU */ case AUX_CALL_HELD: if (!l3m->channel_id) { l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_MANDATORY_IE_MISS); free_l3_msg(l3m); return; } mISDN_l3up(pc, MT_RETRIEVE, l3m); pc->aux_state = AUX_RETRIEVE_IND; break; default: l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_NOTCOMPAT_STATE); free_l3_msg(l3m); return; } if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_retrieve_ack(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; ret = check_infoelements(pc, l3m, ie_RETRIEVE_ACKNOWLEDGE, MT_RETRIEVE_ACKNOWLEDGE); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } switch(pc->aux_state) { case AUX_RETRIEVE_REQ: L3DelTimer(&pc->timer2); if (!l3m->channel_id) { l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); free_l3_msg(l3m); return; } mISDN_l3up(pc, MT_RETRIEVE_ACKNOWLEDGE, l3m); pc->aux_state = AUX_IDLE; break; default: eprint("RETRIEVE_ACK in wrong aux state %d\n", pc->aux_state); free_l3_msg(l3m); } if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_retrieve_rej(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int ret; unsigned char cause; if ((ret = l3dss1_get_cause(pc, l3m))) { if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); free_l3_msg(l3m); return; } ret = check_infoelements(pc, l3m, ie_RETRIEVE_REJECT, MT_RETRIEVE_REJECT); if (Q931_ERROR_COMPREH == ret) { l3dss1_std_ie_err(pc, ret); free_l3_msg(l3m); return; } switch(pc->aux_state) { case AUX_RETRIEVE_REQ: L3DelTimer(&pc->timer2); pc->aux_state = AUX_CALL_HELD; break; default: eprint("RETRIEVE_REJ in wrong aux state %d\n", pc->aux_state); } pc->aux_state = AUX_IDLE; mISDN_l3up(pc, MT_RETRIEVE_REJECT, l3m); } static void l3dss1_thold(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer2); #if 0 pc->cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ #endif mISDN_l3up(pc, MT_HOLD_REJECT, NULL); pc->aux_state = AUX_IDLE; } static void l3dss1_tretrieve(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer2); #if 0 pc->cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ #endif mISDN_l3up(pc, MT_RETRIEVE_REJECT, NULL); pc->aux_state = AUX_CALL_HELD; } static void send_timeout(l3_process_t *pc, const char *nr) { struct l3_msg *l3m; unsigned char c[5]; l3m = alloc_l3_msg(); if (!l3m) { eprint("%s no memory for l3 message\n", __FUNCTION__); return; } c[0] = 0x80 | CAUSE_LOC_USER; c[1] = 0x80 | CAUSE_TIMER_EXPIRED; c[2] = nr[0]; c[3] = nr[1]; c[4] = nr[2]; add_layer3_ie(l3m, IE_CAUSE, 5, c); mISDN_l3up(pc, MT_TIMEOUT, l3m); } static void l3dss1_t302(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_INVALID_NUMBER); L3AddTimer(&pc->timer1, T305, CC_T305); send_timeout(pc, "302"); } static void l3dss1_t303(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); if (pc->n303 > 0) { pc->n303--; if (pc->t303msg) { struct l3_msg *nl3m = pc->t303msg; if (pc->n303 > 0) l3_msg_increment_refcnt(pc->t303msg); else pc->t303msg = NULL; SendMsg(pc, nl3m, -1); } L3AddTimer(&pc->timer1, T303, CC_T303); return; } if (pc->t303msg) free_l3_msg(pc->t303msg); pc->t303msg = NULL; l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, CAUSE_TIMER_EXPIRED); send_timeout(pc, "303"); release_l3_process(pc); } static void l3dss1_t304(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); L3AddTimer(&pc->timer1, T305, CC_T305); send_timeout(pc, "304"); } static void l3dss1_t305(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { int cause; L3DelTimer(&pc->timer1); if (pc->cause != NO_CAUSE) { cause = pc->cause; } else { cause = CAUSE_NORMAL_CLEARING; } newl3state(pc, 19); l3dss1_message_cause(pc, MT_RELEASE, cause); L3AddTimer(&pc->timer1, T308, CC_T308_1); } static void l3dss1_t310(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); L3AddTimer(&pc->timer1, T305, CC_T305); send_timeout(pc, "310"); } static void l3dss1_t313(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); L3AddTimer(&pc->timer1, T305, CC_T305); send_timeout(pc, "313"); } static void l3dss1_t308_1(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { newl3state(pc, 19); L3DelTimer(&pc->timer1); l3dss1_message(pc, MT_RELEASE); L3AddTimer(&pc->timer1, T308, CC_T308_2); } static void l3dss1_t308_2(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); send_timeout(pc, "308"); release_l3_process(pc); } static void l3dss1_t309(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); test_and_clear_bit(FLG_L3P_TIMER309, &pc->flags); send_timeout(pc, "309"); newl3state(pc, 0); release_l3_process(pc); } static void l3dss1_t318(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); #if 0 pc->cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ #endif mISDN_l3up(pc, MT_RESUME_REJECT, NULL); newl3state(pc, 19); l3dss1_message(pc, MT_RELEASE); L3AddTimer(&pc->timer1, T308, CC_T308_1); } static void l3dss1_t319(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { L3DelTimer(&pc->timer1); #if 0 pc->cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ #endif mISDN_l3up(pc, MT_SUSPEND_REJECT, NULL); newl3state(pc, 10); } static void l3dss1_dl_reset(l3_process_t *pc, unsigned int pr, struct l3_msg *arg) { struct l3_msg *l3m = MsgStart(pc, MT_DISCONNECT); unsigned char c[2]; if (!l3m) return; c[0] = 0x80 | CAUSE_LOC_USER; c[1] = 0x80 | CAUSE_TEMPORARY_FAILURE; add_layer3_ie(l3m, IE_CAUSE, 2, c); mIpc_debug(L3_DEB_STATE, pc, "dss1 TE reset DL - send DISCONNECT"); l3dss1_disconnect_req(pc, pr, l3m); } static void l3dss1_dl_release(l3_process_t *pc, unsigned int pr, struct l3_msg *arg) { newl3state(pc, 0); #if 0 pc->cause = 0x1b; /* Destination out of order */ pc->para.loc = 0; #endif mIpc_debug(L3_DEB_STATE, pc, "dss1 TE release DL"); release_l3_process(pc); } static void l3dss1_dl_reestablish(l3_process_t *pc, unsigned int pr, struct l3_msg *arg) { if (!test_and_set_bit(FLG_L3P_TIMER309, &pc->flags)) { L3DelTimer(&pc->timer1); L3AddTimer(&pc->timer1, T309, CC_T309); mIpc_debug(L3_DEB_STATE, pc, "dss1 TE reestablish DL request - start T309"); } else mIpc_debug(L3_DEB_STATE, pc, "dss1 TE reestablish DL request"); l3_manager(pc->l2if, DL_ESTABLISH_REQ); } static void l3dss1_dl_reest_status(l3_process_t *pc, unsigned int pr, struct l3_msg *arg) { L3DelTimer(&pc->timer1); test_and_clear_bit(FLG_L3P_TIMER309, &pc->flags); l3dss1_status_send(pc, CAUSE_NORMALUNSPECIFIED); mIpc_debug(L3_DEB_STATE, pc, "dss1 TE reestablish DL - send STATUS"); } static void l3dss1_dl_ignore(l3_process_t *pc, unsigned int pr, struct l3_msg *l3m) { mIpc_debug(L3_DEB_STATE, pc, "dss1 TE dl_ignore msg:%s", l3m ? _mi_msg_type2str(l3m->type) : "nil"); if (l3m) free_l3_msg(l3m); } /* *INDENT-OFF* */ static struct stateentry downstatelist[] = { {SBIT(0), MT_SETUP, l3dss1_setup_req}, {SBIT(0), MT_REGISTER, l3dss1_register_req}, {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(25), MT_INFORMATION, l3dss1_information_req}, {ALL_STATES, MT_NOTIFY, l3dss1_notify_req}, {SBIT(10), MT_PROGRESS, l3dss1_progress_req}, {SBIT(0), MT_RESUME, l3dss1_resume_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25), MT_DISCONNECT, l3dss1_disconnect_req}, {SBIT(6) | SBIT(11) | SBIT(12) | SBIT(31), MT_RELEASE, l3dss1_release_req}, {ALL_STATES, MT_RESTART, l3dss1_restart}, {ALL_STATES, MT_RELEASE_COMPLETE, l3dss1_release_cmpl_req}, {SBIT(6) | SBIT(25), MT_CALL_PROCEEDING, l3dss1_proceed_req}, {SBIT(6), MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack_req}, {SBIT(25), MT_SETUP_ACKNOWLEDGE, l3dss1_dummy}, {SBIT(6) | SBIT(9) | SBIT(25), MT_ALERTING, l3dss1_alert_req}, {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), MT_CONNECT, l3dss1_connect_req}, {SBIT(10), MT_SUSPEND, l3dss1_suspend_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), MT_HOLD, l3dss1_hold_req}, {ALL_STATES, MT_HOLD, l3dss1_hold_req_ignore}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), MT_HOLD_ACKNOWLEDGE, l3dss1_hold_ack_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), MT_HOLD_REJECT, l3dss1_hold_rej_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), MT_RETRIEVE, l3dss1_retrieve_req}, {ALL_STATES, MT_RETRIEVE, l3dss1_hold_req_ignore}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), MT_RETRIEVE_ACKNOWLEDGE, l3dss1_retrieve_ack_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), MT_RETRIEVE_REJECT, l3dss1_retrieve_rej_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), MT_HOLD , l3dss1_hold_req}, {ALL_STATES, MT_FACILITY, l3dss1_facility_req}, {ALL_STATES, MT_STATUS_ENQUIRY, l3dss1_status_enq_req}, }; #define DOWNSLLEN \ (sizeof(downstatelist) / sizeof(struct stateentry)) static struct stateentry datastatelist[] = { {ALL_STATES, MT_STATUS_ENQUIRY, l3dss1_status_enq}, {ALL_STATES, MT_FACILITY, l3dss1_facility}, {SBIT(19), MT_STATUS, l3dss1_release_ind}, {ALL_STATES, MT_STATUS, l3dss1_status}, {SBIT(0), MT_SETUP, l3dss1_setup}, {SBIT(0), MT_REGISTER, l3dss1_register}, {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), MT_SETUP, l3dss1_dummy}, {SBIT(1) | SBIT(2), MT_CALL_PROCEEDING, l3dss1_call_proc}, {SBIT(1), MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, {SBIT(2) | SBIT(3), MT_ALERTING, l3dss1_alerting}, {SBIT(2) | SBIT(3) | SBIT(4), MT_PROGRESS, l3dss1_progress}, {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), MT_INFORMATION, l3dss1_information}, {ALL_STATES, MT_NOTIFY, l3dss1_notify}, {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25) | SBIT(31), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25) | SBIT(31), MT_RELEASE, l3dss1_release}, {SBIT(19), MT_RELEASE, l3dss1_release_ind}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25), MT_DISCONNECT, l3dss1_disconnect}, {SBIT(19), MT_DISCONNECT, l3dss1_dummy}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), MT_CONNECT, l3dss1_connect}, {SBIT(8), MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, {SBIT(15), MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack}, {SBIT(15), MT_SUSPEND_REJECT, l3dss1_suspend_rej}, {SBIT(17), MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack}, {SBIT(17), MT_RESUME_REJECT, l3dss1_resume_rej}, {SBIT(12) | SBIT(19), MT_HOLD, l3dss1_hold_ignore}, {ALL_STATES, MT_HOLD, l3dss1_hold_ind}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), MT_HOLD_REJECT, l3dss1_hold_rej}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), MT_HOLD_ACKNOWLEDGE, l3dss1_hold_ack}, {ALL_STATES, MT_RETRIEVE, l3dss1_retrieve_ind}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), MT_RETRIEVE_REJECT, l3dss1_retrieve_rej}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), MT_RETRIEVE_ACKNOWLEDGE, l3dss1_retrieve_ack}, }; #define DATASLLEN \ (sizeof(datastatelist) / sizeof(struct stateentry)) static struct stateentry globalmes_list[] = { {ALL_STATES, MT_STATUS, l3dss1_status}, {SBIT(0), MT_RESTART, l3dss1_global_restart}, {SBIT(1), MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack}, }; #define GLOBALM_LEN \ (sizeof(globalmes_list) / sizeof(struct stateentry)) static struct stateentry manstatelist[] = { {SBIT(2), DL_ESTABLISH_IND, l3dss1_dl_reset}, {SBIT(10), DL_ESTABLISH_IND, l3dss1_dl_reest_status}, /* flapping REL/EST */ {SBIT(10), DL_ESTABLISH_CNF, l3dss1_dl_reest_status}, {SBIT(10), DL_RELEASE_IND, l3dss1_dl_reestablish}, {ALL_STATES, DL_RELEASE_IND, l3dss1_dl_release}, {ALL_STATES, DL_ESTABLISH_IND, l3dss1_dl_ignore}, {ALL_STATES, DL_ESTABLISH_CNF, l3dss1_dl_ignore}, {SBIT(25), CC_T302, l3dss1_t302}, {SBIT(1), CC_T303, l3dss1_t303}, {SBIT(2), CC_T304, l3dss1_t304}, {SBIT(3), CC_T310, l3dss1_t310}, {SBIT(8), CC_T313, l3dss1_t313}, {SBIT(11), CC_T305, l3dss1_t305}, {SBIT(15), CC_T319, l3dss1_t319}, {SBIT(17), CC_T318, l3dss1_t318}, {SBIT(19), CC_T308_1, l3dss1_t308_1}, {SBIT(19), CC_T308_2, l3dss1_t308_2}, {SBIT(10), CC_T309, l3dss1_t309}, {SBIT(6), CC_TCTRL, l3dss1_reset}, {ALL_STATES, CC_THOLD, l3dss1_thold}, {ALL_STATES, CC_TRETRIEVE, l3dss1_tretrieve}, {ALL_STATES, MT_RESTART, l3dss1_restart}, }; #define MANSLLEN \ (sizeof(manstatelist) / sizeof(struct stateentry)) /* *INDENT-ON* */ static void global_handler(layer3_t *l3, u_int mt, struct mbuffer *mb) { u_int i; l3_process_t *proc = &l3->global; proc->pid = mb->l3h.cr; /* cr flag */ for (i = 0; i < GLOBALM_LEN; i++) if ((mt == globalmes_list[i].primitive) && ((1 << proc->state) & globalmes_list[i].state)) break; if (i == GLOBALM_LEN) { mIpc_debug(L3_DEB_STATE, proc, "dss1 TE gloabal CR state %d from down %s - not handled", proc->state, _mi_msg_type2str(mt)); l3dss1_status_send(proc, CAUSE_INVALID_CALLREF); free_mbuffer(mb); } else { mIpc_debug(L3_DEB_STATE, proc, "dss1 TE gloabal CR state %d from down %s", proc->state, _mi_msg_type2str(mt)); globalmes_list[i].rout(proc, mt, &mb->l3); } } static int dss1_fromdown(layer3_t *l3, struct mbuffer *msg) { u_int i; int cause, callState, ret; l3_process_t *proc; if (msg->len < 3) { eprint("dss1up frame too short(%d)\n", msg->len); goto freemsg; } if (msg->data[0] != Q931_PD) goto freemsg; ret = parseQ931(msg); if (ret & Q931_ERROR_FATAL) { eprint("dss1up: parse IE error %x\n", ret); goto freemsg; } if (msg->l3h.crlen == 0) { /* Dummy Callref */ if (msg->l3h.type == MT_FACILITY) { l3dss1_facility(&l3->dummy, msg->h->prim, &msg->l3); return 0; } goto freemsg; } else if ((msg->l3h.cr & 0x7fff) == 0) { /* Global CallRef */ global_handler(l3, msg->l3h.type, msg); return 0; } else if (!(proc = get_l3process4cref(l3, msg->l3.pid))) { /* No transaction process exist, that means no call with * this callreference is active */ if (msg->l3h.type == MT_SETUP || msg->l3h.type == MT_REGISTER) { /* Setup creates a new l3 process */ if (msg->l3h.cr & 0x8000) { /* Setup with wrong CREF flag */ goto freemsg; } switch (msg->l3h.type) { case MT_SETUP: dprint(DBGM_L3, "port%d: MT_SETUP\n", msg->addr.dev); break; case MT_REGISTER: dprint(DBGM_L3, "port%d: MT_REGISTER\n", msg->addr.dev); break; } if (!(proc = create_new_process(l3, msg->addr.channel,msg->l3h.cr, NULL))) { /* Maybe should answer with RELEASE_COMPLETE and * CAUSE 0x2f "Resource unavailable", but this * need a new_l3_process too ... arghh */ goto freemsg; } /* register this ID in L4 */ // we will try without this for now // ret = mISDN_l3up(proc, MT_NEW_CR, NULL); #if 0 if (ret) { eprint("dss1up: cannot register ID(%x)\n", proc->id); release_l3_process(proc); goto freemsg; } #endif } else if (msg->l3h.type == MT_STATUS) { cause = 0; if (msg->l3.cause) { if (msg->l3.cause[0] >= 2) cause = msg->l3.cause[2] & 0x7f; else cause = msg->l3.cause[1] & 0x7f; } callState = 0; if (msg->l3.call_state) { callState = msg->l3.call_state[1]; } /* ETS 300-104 part 2.4.1 * if setup has not been made and a message type * MT_STATUS is received with call state == 0, * we must send nothing */ if (callState != 0) { /* ETS 300-104 part 2.4.2 * if setup has not been made and a message type * MT_STATUS is received with call state != 0, * we must send MT_RELEASE_COMPLETE cause 101 */ if ((proc = create_new_process(l3, msg->addr.channel,msg->l3h.cr, NULL))) { l3dss1_msg_without_setup(proc, CAUSE_NOTCOMPAT_STATE); } } iprint("port%d: Got status state %d cause %d\n", msg->addr.dev, callState, cause); goto freemsg; } else if (msg->l3h.type == MT_RELEASE_COMPLETE) { goto freemsg; } else { /* ETS 300-104 part 2 * if setup has not been made and a message type * (except MT_SETUP and RELEASE_COMPLETE) is received, * we must send MT_RELEASE_COMPLETE cause 81 */ dprint(DBGM_L3, "port%d: mt(%x) without callref (maybe former process)\n", msg->addr.dev, msg->l3.type); if ((proc = create_new_process(l3, msg->addr.channel,msg->l3h.cr, NULL))) { l3dss1_msg_without_setup(proc, CAUSE_INVALID_CALLREF); } goto freemsg; } } if (l3dss1_check_messagetype_validity(proc, msg->l3h.type)) { mIpc_debug(L3_DEB_STATE, proc, "dss1 TE state %d from down %s (%x) - invalid msg type", proc->state, _mi_msg_type2str(msg->l3h.type), msg->l3h.type); goto freemsg; } for (i = 0; i < DATASLLEN; i++) if ((msg->l3h.type == datastatelist[i].primitive) && ((1 << proc->state) & datastatelist[i].state)) break; if (i == DATASLLEN) { if ((MT_RELEASE_COMPLETE != msg->l3h.type) && (MT_RELEASE != msg->l3h.type)) { mIpc_debug(L3_DEB_STATE, proc, "dss1 TE state %d from down %s - STATUS not compatible state", proc->state, _mi_msg_type2str(msg->l3h.type)); l3dss1_status_send(proc, CAUSE_NOTCOMPAT_STATE); } else mIpc_debug(L3_DEB_STATE, proc, "dss1 TE state %d from down %s - ignored", proc->state, _mi_msg_type2str(msg->l3h.type)); } else { mIpc_debug(L3_DEB_STATE, proc, "dss1 TE state %d from down %s", proc->state, _mi_msg_type2str(msg->l3h.type)); datastatelist[i].rout(proc, msg->h->prim, &msg->l3); return 0; } freemsg: free_mbuffer(msg); return 0; } static int dss1_fromup(layer3_t *l3, struct l3_msg *l3m) { u_int i; l3_process_t *proc; if (l3m->pid == MISDN_PID_DUMMY) { if (l3m->type == MT_FACILITY) { l3dss1_facility_req(&l3->dummy, l3m->type, l3m); return 0; } return -EINVAL; } if (l3m->pid == MISDN_PID_GLOBAL) { if (l3m->type == MT_RESTART) { l3dss1_restart_req(&l3->global, l3m->type, l3m); return 0; } return -EINVAL; } proc = get_l3process4pid(l3, l3m->pid); if (!proc) { eprint("mISDN dss1 fromup without proc pr=%04x dinfo(%x)\n", l3m->type, l3m->pid); return -EINVAL; } for (i = 0; i < DOWNSLLEN; i++) if ((l3m->type == downstatelist[i].primitive) && ((1 << proc->state) & downstatelist[i].state)) break; if (i == DOWNSLLEN) { mIpc_debug(L3_DEB_STATE, proc, "dss1 TE state %d from up %s - not handled", proc->state, _mi_msg_type2str(l3m->type)); free_l3_msg(l3m); } else { mIpc_debug(L3_DEB_STATE, proc, "dss1 TE state %d from up %s", proc->state, _mi_msg_type2str(l3m->type)); downstatelist[i].rout(proc, l3m->type, l3m); } return 0; } static int dss1man(l3_process_t *proc, u_int pr, struct l3_msg *l3m) { u_int i; /* This handles dl_estab and dl_release messages */ if (!proc) { eprint("mISDN dss1man without proc pr=%s\n", _mi_msg_type2str(pr)); return -EINVAL; } for (i = 0; i < MANSLLEN; i++) if ((pr == manstatelist[i].primitive) && ((1 << proc->state) & manstatelist[i].state)) break; if (i == MANSLLEN) { eprint("cr %x dss1man state %d prim %s unhandled\n", proc->pid & 0x7fff, proc->state, _mi_msg_type2str(pr)); if (l3m) free_l3_msg(l3m); } else { mIpc_debug(L3_DEB_STATE, proc, "dss1 TE state %d pr %s", proc->state, _mi_msg_type2str(pr)); manstatelist[i].rout(proc, pr, l3m); } return 0; } static void dss1_user_init(layer3_t *l3) { l3->from_l2 = dss1_fromdown; l3->to_l3 = dss1_fromup; l3->p_mgr = dss1man; test_and_set_bit(FLG_USER, &l3->ml3.options); } struct l3protocol dss1user = { .name = "DSS1 User", .protocol = L3_PROTOCOL_DSS1_USER , .init = dss1_user_init };