Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 1 | % ITU-T Q.76x ISUPcoding / decoding |
| 2 | |
| 3 | % (C) 2011 by Harald Welte <laforge@gnumonks.org> |
| 4 | % |
| 5 | % All Rights Reserved |
| 6 | % |
| 7 | % This program is free software; you can redistribute it and/or modify |
| 8 | % it under the terms of the GNU Affero General Public License as |
| 9 | % published by the Free Software Foundation; either version 3 of the |
| 10 | % License, or (at your option) any later version. |
| 11 | % |
| 12 | % This program is distributed in the hope that it will be useful, |
| 13 | % but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | % GNU General Public License for more details. |
| 16 | % |
| 17 | % You should have received a copy of the GNU Affero General Public License |
| 18 | % along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 19 | |
| 20 | -module(isup_codec). |
| 21 | -author('Harald Welte <laforge@gnumonks.org>'). |
| 22 | -include("isup.hrl"). |
| 23 | |
| 24 | -export([parse_isup_msg/1, encode_isup_msg/1]). |
| 25 | |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 26 | -compile(export_all). |
| 27 | |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 28 | parse_isup_party(<<>>, OddEven, DigitList) -> |
| 29 | % in case of odd number of digits, we need to cut the last |
| 30 | case OddEven of |
| 31 | 1 -> |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 32 | lists:sublist(DigitList, length(DigitList)-1); |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 33 | 0 -> |
| 34 | DigitList |
| 35 | end; |
| 36 | parse_isup_party(BcdBin, OddEven, DigitList) -> |
| 37 | <<Second:4, First:4, Remain/binary>> = BcdBin, |
| 38 | NewDigits = [First, Second], |
| 39 | parse_isup_party(Remain, OddEven, DigitList ++ NewDigits). |
| 40 | |
| 41 | parse_isup_party(BinBcd, OddEven) when is_binary(BinBcd) -> |
| 42 | parse_isup_party(BinBcd, OddEven, []). |
| 43 | |
| 44 | |
| 45 | % parse a single option |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 46 | parse_isup_opt(OptType = ?ISUP_PAR_CALLED_P_NUM, _OptLen, Content) -> |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 47 | % C.3.7 Called Party Number |
| 48 | <<OddEven:1, Nature:7, Inn:1, NumPlan:3, 0:4, Remain/binary>> = Content, |
| 49 | PhoneNum = parse_isup_party(Remain, OddEven), |
| 50 | {OptType, #party_number{nature_of_addr_ind = Nature, |
| 51 | internal_net_num = Inn, |
| 52 | numbering_plan = NumPlan, |
| 53 | phone_number = PhoneNum}}; |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 54 | parse_isup_opt(OptType = ?ISUP_PAR_CALLING_P_NUM, _OptLen, Content) -> |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 55 | % C.3.8 Calling Party Number |
| 56 | <<OddEven:1, Nature:7, Ni:1, NumPlan:3, PresRestr:2, Screen:2, Remain/binary>> = Content, |
| 57 | PhoneNum = parse_isup_party(Remain, OddEven), |
| 58 | {OptType, #party_number{nature_of_addr_ind = Nature, |
| 59 | number_incompl_ind = Ni, |
| 60 | numbering_plan = NumPlan, |
| 61 | present_restrict = PresRestr, |
| 62 | screening_ind = Screen, |
| 63 | phone_number = PhoneNum}}; |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 64 | parse_isup_opt(OptType = ?ISUP_PAR_CONNECTED_NUM, _OptLen, Content) -> |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 65 | % C.3.14 Connected Number |
| 66 | <<OddEven:1, Nature:7, 0:1, NumPlan:3, PresRestr:2, Screen:2, Remain/binary>> = Content, |
| 67 | PhoneNum = parse_isup_party(Remain, OddEven), |
| 68 | {OptType, #party_number{nature_of_addr_ind = Nature, |
| 69 | numbering_plan = NumPlan, |
| 70 | present_restrict = PresRestr, |
| 71 | screening_ind = Screen, |
| 72 | phone_number = PhoneNum}}; |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 73 | parse_isup_opt(OptType = ?ISUP_PAR_SUBSEQ_NUM, _OptLen, Content) -> |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 74 | % C.3.32 Subsequent Number |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 75 | <<OddEven:1, 0:7, Remain/binary>> = Content, |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 76 | PhoneNum = parse_isup_party(Remain, OddEven), |
| 77 | {OptType, #party_number{phone_number = PhoneNum}}; |
| 78 | parse_isup_opt(OptType, OptLen, Content) -> |
| 79 | {OptType, {OptLen, Content}}. |
| 80 | |
| 81 | % parse a Binary into a list of options |
| 82 | parse_isup_opts(<<>>, OptList) -> |
| 83 | % empty list |
| 84 | OptList; |
| 85 | parse_isup_opts(<<0>>, OptList) -> |
| 86 | % end of options |
| 87 | OptList; |
| 88 | parse_isup_opts(OptBin, OptList) when is_binary(OptBin) -> |
| 89 | <<OptType:8, OptLen:8, Content:OptLen/binary, Remain/binary>> = OptBin, |
| 90 | NewOpt = parse_isup_opt(OptType, OptLen, Content), |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 91 | parse_isup_opts(Remain, OptList ++ [NewOpt]). |
| 92 | parse_isup_opts(OptBin) -> |
| 93 | parse_isup_opts(OptBin, []). |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 94 | |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 95 | % References to 'Tabe C-xxx' are to Annex C of Q.767 |
| 96 | |
| 97 | % Default case: no fixed and no variable parts, only options |
| 98 | % ANM, RLC, FOT |
| 99 | parse_isup_msgt(M, Bin) when |
| 100 | M == ?ISUP_MSGT_ANM; |
| 101 | M == ?ISUP_MSGT_RLC; |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 102 | M == ?ISUP_MSGT_FOT -> |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 103 | parse_isup_opts(Bin); |
| 104 | % Table C-5 Address complete |
| 105 | parse_isup_msgt(?ISUP_MSGT_ACM, Bin) -> |
| 106 | <<BackCallInd:16, Remain/binary>> = Bin, |
| 107 | BciOpt = {backward_call_ind, BackCallInd}, |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 108 | Opts = parse_isup_opts(Remain), |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 109 | [BciOpt|Opts]; |
| 110 | % Table C-7 Call progress |
| 111 | parse_isup_msgt(?ISUP_MSGT_CPG, Bin) -> |
| 112 | <<EventInf:8, Remain/binary>> = Bin, |
| 113 | BciOpt = {event_info, EventInf}, |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 114 | Opts = parse_isup_opts(Remain), |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 115 | [BciOpt|Opts]; |
| 116 | % Table C-9 Circuit group reset acknowledgement |
| 117 | parse_isup_msgt(?ISUP_MSGT_GRA, Bin) -> |
| 118 | % V: Range and status |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 119 | <<PtrVar:8, Remain/binary>> = Bin, |
| 120 | RangStsLen = binary:at(Remain, PtrVar), |
| 121 | RangeStatus = binary:part(Remain, PtrVar+1, RangStsLen), |
| 122 | RangeStsTuple = {?ISUP_PAR_RANGE_AND_STATUS, {RangStsLen, RangeStatus}}, |
| 123 | [RangeStsTuple]; |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 124 | % Table C-11 Connect |
| 125 | parse_isup_msgt(?ISUP_MSGT_CON, Bin) -> |
| 126 | <<BackCallInd:16, Remain/binary>> = Bin, |
| 127 | BciOpt = {backward_call_ind, BackCallInd}, |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 128 | Opts = parse_isup_opts(Remain), |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 129 | [BciOpt|Opts]; |
| 130 | % Table C-12 Continuity |
| 131 | parse_isup_msgt(?ISUP_MSGT_COT, Bin) -> |
| 132 | <<ContInd:8>> = Bin, |
| 133 | [{continuity_ind, ContInd}]; |
| 134 | % Table C-16 Initial address |
| 135 | parse_isup_msgt(?ISUP_MSGT_IAM, Bin) -> |
| 136 | <<CINat:8, FwCallInd:16/big, CallingCat:8, TransmReq:8, VarAndOpt/binary>> = Bin, |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 137 | %<<CINat:8, FwCallInd:16/big, CallingCat:8, TransmReq:8, PtrVar:8, PtrOpt:8, VarAndOpt/binary>> = Bin, |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 138 | FixedOpts = [{conn_ind_nature, CINat}, {fw_call_ind, FwCallInd}, {calling_cat, CallingCat}, |
| 139 | {transm_medium_req, TransmReq}], |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 140 | <<PtrVar:8, PtrOpt:8, _/binary>> = VarAndOpt, |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 141 | % V: Called Party Number |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 142 | CalledPartyLen = binary:at(VarAndOpt, PtrVar), |
| 143 | CalledParty = binary:part(VarAndOpt, PtrVar+1, CalledPartyLen), |
| 144 | VarOpts = [parse_isup_opt(?ISUP_PAR_CALLED_P_NUM, CalledPartyLen, CalledParty)], |
| 145 | % Optional part |
| 146 | Remain = binary:part(VarAndOpt, 1 + PtrOpt, byte_size(VarAndOpt)-(1+PtrOpt)), |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 147 | Opts = parse_isup_opts(Remain), |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 148 | FixedOpts ++ VarOpts ++ Opts; |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 149 | % Table C-17 Release |
| 150 | parse_isup_msgt(?ISUP_MSGT_REL, Bin) -> |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 151 | <<PtrVar:8, PtrOpt:8, VarAndOpt/binary>> = Bin, |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 152 | % V: Cause indicators |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 153 | CauseIndLen = binary:at(VarAndOpt, PtrVar), |
| 154 | CauseInd = binary:part(VarAndOpt, PtrVar+1, CauseIndLen), |
| 155 | VarOpts = {?ISUP_PAR_CAUSE_IND, {CauseIndLen, CauseInd}}, |
| 156 | Remain = binary:part(VarAndOpt, 1 + PtrOpt, byte_size(VarAndOpt)-(1+PtrOpt)), |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 157 | Opts = parse_isup_opts(Remain), |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 158 | VarOpts ++ Opts; |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 159 | % Table C-19 Subsequent address |
| 160 | parse_isup_msgt(?ISUP_MSGT_SAM, Bin) -> |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 161 | <<PtrVar:8, PtrOpt:8, VarAndOpt/binary>> = Bin, |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 162 | % V: Subsequent number |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 163 | SubseqNumLen = binary:at(VarAndOpt, PtrVar), |
| 164 | SubsetNum = binary:part(VarAndOpt, PtrVar+1, SubseqNumLen), |
| 165 | VarOpts = [{?ISUP_PAR_SUBSEQ_NUM, {SubseqNumLen, SubsetNum}}], |
| 166 | Remain = binary:part(VarAndOpt, 1 + PtrOpt, byte_size(VarAndOpt)-(1+PtrOpt)), |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 167 | Opts = parse_isup_opts(Remain), |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 168 | VarOpts ++ Opts; |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 169 | % Table C-21 Suspend, Resume |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 170 | parse_isup_msgt(Msgt, Bin) when Msgt == ?ISUP_MSGT_RES; Msgt == ?ISUP_MSGT_SUS -> |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 171 | <<SuspResInd:8, Remain/binary>> = Bin, |
| 172 | FixedOpts = [{susp_res_ind, SuspResInd}], |
| 173 | Opts = parse_isup_opts(Remain), |
| 174 | [FixedOpts|Opts]; |
| 175 | % Table C-23 |
| 176 | parse_isup_msgt(M, <<>>) when |
| 177 | M == ?ISUP_MSGT_BLO; |
| 178 | M == ?ISUP_MSGT_BLA; |
| 179 | M == ?ISUP_MSGT_CCR; |
| 180 | M == ?ISUP_MSGT_RSC; |
| 181 | M == ?ISUP_MSGT_UBL; |
| 182 | M == ?ISUP_MSGT_UBA -> |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 183 | []; |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 184 | % Table C-25 |
| 185 | parse_isup_msgt(M, Bin) when |
| 186 | M == ?ISUP_MSGT_CGB; |
| 187 | M == ?ISUP_MSGT_CGBA; |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 188 | M == ?ISUP_MSGT_CGU; |
| 189 | M == ?ISUP_MSGT_CGUA -> |
| 190 | <<CGMsgt:8, PtrVar:8, VarBin/binary>> = Bin, |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 191 | FixedOpts = [{cg_supv_msgt, CGMsgt}], |
| 192 | % V: Range and status |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 193 | RangStsLen = binary:at(VarBin, PtrVar), |
| 194 | RangeStatus = binary:part(VarBin, PtrVar+1, RangStsLen), |
| 195 | VarOpts = [{?ISUP_PAR_RANGE_AND_STATUS, {RangStsLen, RangeStatus}}], |
| 196 | FixedOpts ++ VarOpts; |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 197 | % Table C-26 Circuit group reset |
| 198 | parse_isup_msgt(?ISUP_MSGT_GRS, Bin) -> |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 199 | <<PtrVar:8, VarBin/binary>> = Bin, |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 200 | % V: Range without status |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 201 | RangeLen = binary:at(VarBin, PtrVar), |
| 202 | Range = binary:part(VarBin, PtrVar+1, RangeLen), |
| 203 | [{?ISUP_PAR_RANGE_AND_STATUS, {RangeLen, Range}}]. |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 204 | |
| 205 | |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 206 | parse_isup_msg(DataBin) when is_binary(DataBin) -> |
| 207 | <<Cic:12/little, 0:4, MsgType:8, Remain/binary>> = DataBin, |
Harald Welte | 50a44c2 | 2011-01-15 21:39:20 +0100 | [diff] [blame] | 208 | Opts = parse_isup_msgt(MsgType, Remain), |
| 209 | #isup_msg{cic = Cic, msg_type = MsgType, parameters = Opts}. |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 210 | |
| 211 | |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 212 | % encode a phone number from a list of digits into the BCD binary sequence |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 213 | encode_isup_party(BcdList) -> |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 214 | encode_isup_party(BcdList, <<>>, length(BcdList)). |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 215 | encode_isup_party([], Bin, NumDigits) -> |
| 216 | case NumDigits rem 2 of |
| 217 | 1 -> |
| 218 | {Bin, 1}; |
| 219 | 0 -> |
| 220 | {Bin, 0} |
| 221 | end; |
| 222 | encode_isup_party([First,Second|BcdList], Bin, NumDigits) -> |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 223 | encode_isup_party(BcdList, <<Bin/binary, Second:4, First:4>>, NumDigits). |
| 224 | |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 225 | % encode a single option |
| 226 | encode_isup_opt(?ISUP_PAR_CALLED_P_NUM, |
| 227 | #party_number{nature_of_addr_ind = Nature, |
| 228 | internal_net_num = Inn, |
| 229 | numbering_plan = NumPlan, |
| 230 | phone_number= PhoneNum}) -> |
| 231 | % C.3.7 Called Party Number |
| 232 | {PhoneBin, OddEven} = encode_isup_party(PhoneNum), |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 233 | <<OddEven:1, Nature:7, Inn:1, NumPlan:3, 0:4, PhoneBin/binary>>; |
Harald Welte | de30a87 | 2011-01-16 17:12:56 +0100 | [diff] [blame] | 234 | encode_isup_opt(?ISUP_PAR_CALLING_P_NUM, |
| 235 | #party_number{nature_of_addr_ind = Nature, |
| 236 | number_incompl_ind = Ni, |
| 237 | numbering_plan = NumPlan, |
| 238 | present_restrict = PresRestr, |
| 239 | screening_ind = Screen, |
| 240 | phone_number= PhoneNum}) -> |
| 241 | % C.3.8 Calling Party Number |
| 242 | {PhoneBin, OddEven} = encode_isup_party(PhoneNum), |
| 243 | <<OddEven:1, Nature:7, Ni:1, NumPlan:3, PresRestr:2, Screen:2, PhoneBin/binary>>; |
| 244 | encode_isup_opt(?ISUP_PAR_CONNECTED_NUM, |
| 245 | #party_number{nature_of_addr_ind = Nature, |
| 246 | numbering_plan = NumPlan, |
| 247 | present_restrict = PresRestr, |
| 248 | screening_ind = Screen, |
| 249 | phone_number = PhoneNum}) -> |
| 250 | % C.3.14 Connected Number |
| 251 | {PhoneBin, OddEven} = encode_isup_party(PhoneNum), |
| 252 | <<OddEven:1, Nature:7, 0:1, NumPlan:3, PresRestr:2, Screen:2, PhoneBin/binary>>; |
| 253 | encode_isup_opt(OptNum, {OptLen, Binary}) when is_binary(Binary) -> |
| 254 | Binary. |
| 255 | |
Harald Welte | 01f8ea3 | 2011-01-17 21:30:42 +0100 | [diff] [blame] | 256 | encode_isup_msg(#isup_msg{}) -> |
| 257 | foo. |