Add Erlang M2UA decoding and encoding routines
diff --git a/src/m2ua.hrl b/src/m2ua.hrl
new file mode 100644
index 0000000..26f10fc
--- /dev/null
+++ b/src/m2ua.hrl
@@ -0,0 +1,59 @@
+
+% RFC 3331 Section 3.1.3 Message Class
+-define(M2UA_MSGC_MGMT,		0).	% Management Messages [IUA/M2UA/M3UA/SUA]
+-define(M2UA_MSGC_TRANSFER,	1).	% Transfer Messages [M3UA]
+-define(M2UA_MSGC_SSNM,		2).	% SS7 Signalling Network Management [M3UA/SUA]
+-define(M2UA_MSGC_ASPSM,	3).	% ASP State Maintenance [IUA/M2UA/M3UA/SUA]
+-define(M2UA_MSGC_ASPTM,	4).	% ASP Traffic Maintenance [IUA/M2UA/M3UA/SUA]
+-define(M2UA_MSGC_QPTM,		5).	% Q.921/Q.931 Boundary Primitives Transport [IUA]
+-define(M2UA_MSGC_MAUP,		6).	% MTP2 User Adaption [M2UA]
+-define(M2UA_MSGC_CONNLESS,	7).	% Connectionless Messages [SUA]
+-define(M2UA_MSGC_CONN,		8).	% Connection oriented messages [SUA]
+-define(M2UA_MSGC_RKM,		9).	% Routing Key Management [M3UA]
+-define(M2UA_MSGC_IIM,		10).	% Interface Identifier Management (M2UA)
+
+% RFC 3331 Section 3.1.4 Message Type
+-define(M2UA_MAUP_MSGT_RESERVED,	0).
+-define(M2UA_MAUP_MSGT_DATA,		1).
+-define(M2UA_MAUP_MSGT_EST_REQ,		2).
+-define(M2UA_MAUP_MSGT_EST_CONF,	3).
+-define(M2UA_MAUP_MSGT_REL_REQ,		4).
+-define(M2UA_MAUP_MSGT_REL_CONF,	5).
+-define(M2UA_MAUP_MSGT_REL_IND,		6).
+-define(M2UA_MAUP_MSGT_STATE_REQ,	7).
+-define(M2UA_MAUP_MSGT_STATE_CONF,	8).
+-define(M2UA_MAUP_MSGT_STATE_IND,	9).
+-define(M2UA_MAUP_MSGT_DATA_RETR_REQ,	10).
+-define(M2UA_MAUP_MSGT_DATA_RETR_CONF,	11).
+-define(M2UA_MAUP_MSGT_DATA_RETR_IND,	12).
+-define(M2UA_MAUP_MSGT_DATA_RETR_COMPL_IND,	13).
+-define(M2UA_MAUP_MSGT_CONG_IND,	14).
+-define(M2UA_MAUP_MSGT_DATA_ACK,	15).
+
+
+-define(M2UA_ASPSM_MSGT_UP,		0).
+-define(M2UA_ASPSM_MSGT_DOWN,		1).
+-define(M2UA_ASPSM_MSGT_BEAT,		2).
+-define(M2UA_ASPSM_MSGT_UP_ACK,		3).
+-define(M2UA_ASPSM_MSGT_DOWN_ACK,	5).
+-define(M2UA_ASPSM_MSGT_BEAT_ACK,	6).
+
+-define(M2UA_ASPTM_MSGT_ACTIVE,		1).
+-define(M2UA_ASPTM_MSGT_INACTIVE,	2).
+-define(M2UA_ASPTM_MSGT_ACTIVE_ACK,	3).
+-define(M2UA_ASPTM_MSGT_INACTIVE_ACK,	4).
+
+-define(M2UA_MGMT_MSGT_ERROR,		0).
+-define(M2UA_MGMT_MSGT_NOTIFY,		1).
+
+-define(M2UA_MGMT_IIM_REG_REQ,		1).
+-define(M2UA_MGMT_IIM_REG_RSP,		2).
+-define(M2UA_MGMT_IIM_DEREG_REQ,	3).
+-define(M2UA_MGMT_IIM_DEREG_RSP,	4).
+
+-record(m2ua_msg, {
+	msg_class,
+	msg_type,
+	parameters
+	}).
+
diff --git a/src/m2ua_codec.erl b/src/m2ua_codec.erl
new file mode 100644
index 0000000..262fbb1
--- /dev/null
+++ b/src/m2ua_codec.erl
@@ -0,0 +1,69 @@
+% RFC 3331 MTP2 User Adaption Layer coding / decoding
+
+% (C) 2011 by Harald Welte <laforge@gnumonks.org>
+%
+% All Rights Reserved
+%
+% This program is free software; you can redistribute it and/or modify
+% it under the terms of the GNU Affero 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 Affero General Public License
+% along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+-module(m2ua_codec).
+-author('Harald Welte <laforge@gnumonks.org>').
+-include("m2ua.hrl").
+
+-export([parse_m2ua_msg/1, encode_m2ua_msg/1]).
+
+% parse a binary chunk of options into an options proplist
+parse_m2ua_opts(<<>>, OptList) ->
+	OptList;
+parse_m2ua_opts(OptBin, OptList) ->
+	<<Tag:16/big, LengthIncHdr:16/big, Remain/binary>> = OptBin,
+	Length = LengthIncHdr - 4,
+	<<Value:Length/binary, NextOpts/binary>> = Remain,
+	NewOpt = {Tag, {Length, Value}},
+	parse_m2ua_opts(NextOpts, [NewOpt|OptList]).
+
+% parse a single M2UA message
+parse_m2ua_msgt(_, _, _, Remain) ->
+	parse_m2ua_opts(Remain, []).
+
+% parse a M2UA message binary into a record
+parse_m2ua_msg(DataBin) when is_binary(DataBin) ->
+	<<1:8, 0:8, MsgClass:8, MsgType:8, MsgLen:32/big, Remain/binary>> = DataBin,
+	Parsed = parse_m2ua_msgt(MsgClass, MsgType, MsgLen, Remain),
+	{ok, #m2ua_msg{msg_class = MsgClass, msg_type = MsgType, parameters = Parsed}}.
+
+
+
+% encode a single option
+encode_m2ua_opt({OptNum, {DataBinLen, DataBin}}) when is_integer(OptNum) ->
+	LengthIncHdr = DataBinLen + 4,
+	<<OptNum:16/big, LengthIncHdr:16/big, DataBin/binary>>.
+
+% encode a list of options
+encode_m2ua_opts([], OptEnc) ->
+	list_to_binary(OptEnc);
+encode_m2ua_opts([CurOpt|OptPropList], OptEnc) ->
+	CurOptEnc = encode_m2ua_opt(CurOpt),
+	encode_m2ua_opts(OptPropList, list_to_binary([OptEnc, CurOptEnc])).
+	
+
+% encode a particular message type
+encode_m2ua_msgt(MsgClass, MsgType, Params) ->
+	OptBin = encode_m2ua_opts(Params, <<>>),
+	MsgLenIncHdr = 4 + binary:byte_size(OptBin),
+	<<1:8, 0:8, MsgClass:8, MsgType:8, MsgLenIncHdr:32/big, OptBin/binary>>.
+
+% encode a message from record to binary
+encode_m2ua_msg(#m2ua_msg{msg_class = MsgClass, msg_type = MsgType, parameters = Params}) ->
+	encode_m2ua_msgt(MsgClass, MsgType, Params).