forked from osmocom/wireshark
![Bill Meier](/assets/img/avatar_default.png)
Change-Id: I36b2731d67f9345d2fd0c23800bba7d2be94c387 Reviewed-on: https://code.wireshark.org/review/6008 Reviewed-by: Bill Meier <wmeier@newsguy.com>
656 lines
18 KiB
C
656 lines
18 KiB
C
/* packet-tetra.c
|
|
* Routines for TETRA packet dissection
|
|
*
|
|
* Copyright (c) 2007 - 2011 Professional Mobile Communication Research Group,
|
|
* Beijing Institute of Technology, China
|
|
* Copyright (c) 2011 Holger Hans Peter Freyther
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* REF: ETSI EN 300 392-2 V3.2.1
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <epan/packet.h>
|
|
#include <epan/expert.h>
|
|
#include <epan/prefs.h>
|
|
#include <epan/oids.h>
|
|
#include <epan/conversation.h>
|
|
#include <epan/asn1.h>
|
|
|
|
#include "packet-per.h"
|
|
#include "packet-tetra.h"
|
|
|
|
#define PROTO_TAG_tetra "TETRA"
|
|
|
|
void proto_register_tetra(void);
|
|
void proto_reg_handoff_tetra(void);
|
|
|
|
/* Wireshark ID of the tetra protocol */
|
|
static int proto_tetra = -1;
|
|
|
|
/* These are the handles of our subdissectors */
|
|
static dissector_handle_t data_handle = NULL;
|
|
|
|
static dissector_handle_t tetra_handle;
|
|
static void dissect_tetra(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
|
|
|
|
static int global_tetra_port = 7074;
|
|
|
|
/* Whether the capture data include carrier numbers */
|
|
static gboolean include_carrier_number = TRUE;
|
|
|
|
/* The following hf_* variables are used to hold the Wireshark IDs of
|
|
* our header fields; they are filled out when we call
|
|
* proto_register_field_array() in proto_register_tetra()
|
|
*/
|
|
/** Kts attempt at defining the protocol */
|
|
static gint hf_tetra = -1;
|
|
static gint hf_tetra_header = -1;
|
|
static gint hf_tetra_channels = -1;
|
|
static gint hf_tetra_channel1 = -1;
|
|
static gint hf_tetra_channel2 = -1;
|
|
static gint hf_tetra_channel3 = -1;
|
|
static gint hf_tetra_txreg = -1;
|
|
static gint hf_tetra_timer = -1;
|
|
static gint hf_tetra_pdu = -1;
|
|
static gint hf_tetra_rvstr = -1;
|
|
static gint hf_tetra_carriernumber = -1;
|
|
static gint hf_tetra_rxchannel1 = -1;
|
|
static gint hf_tetra_rxchannel2 = -1;
|
|
static gint hf_tetra_rxchannel3 = -1;
|
|
static gint hf_tetra_crc = -1;
|
|
static gint hf_tetra_len0 = -1;
|
|
|
|
#include "packet-tetra-hf.c"
|
|
|
|
/* Initialize the subtree pointers */
|
|
/* These are the ids of the subtrees that we may be creating */
|
|
static gint ett_tetra = -1;
|
|
static gint ett_tetra_header = -1;
|
|
static gint ett_tetra_length = -1;
|
|
static gint ett_tetra_txreg = -1;
|
|
static gint ett_tetra_text = -1;
|
|
|
|
#include "packet-tetra-ett.c"
|
|
|
|
static expert_field ei_tetra_channels_incorrect = EI_INIT;
|
|
|
|
#include "packet-tetra-fn.c"
|
|
|
|
static const value_string channeltypenames[] = {
|
|
{ 0, "Reserved" },
|
|
{ 1, "AACH" },
|
|
{ 2, "SCH/F" },
|
|
{ 3, "SCH/HD" },
|
|
{ 4, "Unknown" },
|
|
{ 5, "BSCH" },
|
|
{ 6, "BNCH" },
|
|
{ 7, "TCH/F" },
|
|
{ 8, "TCH/H" },
|
|
{ 9, "TCH4.8"},
|
|
{ 10, "TCH7.2"},
|
|
{ 11, "STCH"},
|
|
{ 0, NULL }
|
|
};
|
|
|
|
static const value_string recvchanneltypenames[] = {
|
|
{ 0, "Reserved" },
|
|
{ 1, "AACH" },
|
|
{ 2, "SCH/F" },
|
|
{ 3, "SCH/HD" },
|
|
{ 4, "Unknown" },
|
|
{ 5, "BSCH" },
|
|
{ 6, "BNCH" },
|
|
{ 7, "TCH/F" },
|
|
{ 8, "TCH/H" },
|
|
{ 9, "TCH4.8"},
|
|
{ 10, "TCH7.2"},
|
|
{ 11, "STCH"},
|
|
{ 15, "SCH/HU"},
|
|
{ 0, NULL }
|
|
};
|
|
|
|
/* Get the length of received pdu */
|
|
static gint get_rx_pdu_length(guint32 channel_type)
|
|
{
|
|
gint len = 0;
|
|
|
|
switch(channel_type) {
|
|
case TETRA_CHAN_AACH:
|
|
len = 14;
|
|
break;
|
|
case TETRA_CHAN_SCH_F:
|
|
len = 268;
|
|
break;
|
|
case TETRA_CHAN_SCH_D:
|
|
len = 124; ;
|
|
break;
|
|
case TETRA_CHAN_BSCH:
|
|
len = 60;
|
|
break;
|
|
case TETRA_CHAN_BNCH:
|
|
len = 124;
|
|
break;
|
|
case TETRA_CHAN_TCH_F:
|
|
len = 274;
|
|
break;
|
|
case TETRA_CHAN_TCH_H:
|
|
len = 137;
|
|
break;
|
|
case TETRA_CHAN_TCH_2_4:
|
|
len = 144;
|
|
break;
|
|
case TETRA_CHAN_TCH_4_8:
|
|
len = 288;
|
|
break;
|
|
case TETRA_CHAN_STCH:
|
|
len = 124;
|
|
break;
|
|
case TETRA_CHAN_SCH_HU:
|
|
len = 92;
|
|
break;
|
|
default:
|
|
len = 0;
|
|
break;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/* Get the length of transmitted pdu */
|
|
static gint get_tx_pdu_length(guint32 channel_type)
|
|
{
|
|
gint len = 0;
|
|
|
|
switch(channel_type) {
|
|
case TETRA_CHAN_AACH:
|
|
len = 14;
|
|
break;
|
|
case TETRA_CHAN_SCH_F:
|
|
len = 268;
|
|
break;
|
|
case TETRA_CHAN_SCH_D:
|
|
len = 124;
|
|
break;
|
|
case TETRA_CHAN_BSCH:
|
|
len = 60;
|
|
break;
|
|
case TETRA_CHAN_BNCH:
|
|
len = 124;
|
|
break;
|
|
case TETRA_CHAN_TCH_F:
|
|
len = 274;
|
|
break;
|
|
case TETRA_CHAN_TCH_H:
|
|
len = 137;
|
|
break;
|
|
case TETRA_CHAN_TCH_2_4:
|
|
len = 144;
|
|
break;
|
|
case TETRA_CHAN_TCH_4_8:
|
|
len = 288;
|
|
break;
|
|
case TETRA_CHAN_STCH:
|
|
len = 124;
|
|
break;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
void tetra_dissect_pdu(int channel_type, int dir, tvbuff_t *pdu, proto_tree *tree, packet_info *pinfo)
|
|
{
|
|
proto_item *tetra_sub_item;
|
|
proto_tree *tetra_sub_tree;
|
|
guint8 p;
|
|
|
|
tetra_sub_item = proto_tree_add_item(tree, hf_tetra_pdu,
|
|
pdu, 0, tvb_captured_length(pdu), ENC_NA);
|
|
|
|
tetra_sub_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
|
|
|
|
switch(channel_type) {
|
|
case TETRA_CHAN_AACH:
|
|
dissect_AACH_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case TETRA_CHAN_SCH_F:
|
|
p = tvb_get_guint8(pdu, 0);
|
|
switch(p >> 6) {
|
|
case 0:
|
|
if (dir == TETRA_DOWNLINK)
|
|
dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
else
|
|
dissect_MAC_DATA_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case 1: /* MAC-FRAG or MAC-END */
|
|
if((p >> 5) == 3) {
|
|
if (dir == TETRA_DOWNLINK)
|
|
dissect_MAC_END_DOWNLINK_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
else
|
|
dissect_MAC_END_UPLINK_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
|
|
} else
|
|
dissect_MAC_FRAG_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case 2:
|
|
dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
}
|
|
break;
|
|
case TETRA_CHAN_SCH_D:
|
|
p = tvb_get_guint8(pdu, 0);
|
|
switch(p >> 6) {
|
|
case 0:
|
|
dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case 1: /* MAC-FRAG or MAC-END */
|
|
if((p >> 5) == 3)
|
|
dissect_MAC_END_DOWN111_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
else
|
|
dissect_MAC_FRAG120_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case 2:
|
|
dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
}
|
|
break;
|
|
case TETRA_CHAN_SCH_HU:
|
|
p = tvb_get_guint8(pdu, 0);
|
|
switch(p >> 7) {
|
|
case 0: /* MAC-ACCESS */
|
|
dissect_MAC_ACCESS_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case 1: /* MAC-END-HU */
|
|
dissect_MAC_END_HU_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
}
|
|
break;
|
|
case TETRA_CHAN_BSCH:
|
|
col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "BSCH");
|
|
dissect_BSCH_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case TETRA_CHAN_BNCH:
|
|
col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "BNCH");
|
|
dissect_BNCH_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case TETRA_CHAN_STCH:
|
|
p = tvb_get_guint8(pdu, 0);
|
|
switch(p >> 6) {
|
|
case 0:
|
|
dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case 1: /* MAC-FRAG or MAC-END */
|
|
if((p >> 5) == 3) {
|
|
if (dir == TETRA_DOWNLINK)
|
|
dissect_MAC_END_DOWN111_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
else
|
|
dissect_MAC_END_UP114_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
} else
|
|
dissect_MAC_FRAG120_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
case 2:
|
|
dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree, NULL);
|
|
break;
|
|
}
|
|
break;
|
|
case TETRA_CHAN_TCH_F:
|
|
col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Voice");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void dissect_tetra_UNITDATA_IND(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tetra_tree, int offset)
|
|
{
|
|
guint32 rxreg = 0;
|
|
guint32 channels = 0, i;
|
|
guint32 channel_type;
|
|
gint pdu_offset = 0;
|
|
proto_item *tetra_sub_item;
|
|
proto_tree *tetra_header_tree = NULL;
|
|
tvbuff_t *payload_tvb;
|
|
|
|
/* Length */
|
|
rxreg = tvb_get_letohl(tvb, offset);
|
|
proto_tree_add_uint(tetra_tree, hf_tetra_len0, tvb, offset, 4, rxreg);
|
|
|
|
/* RvSteR */
|
|
offset += 4;
|
|
rxreg = tvb_get_letohl(tvb, offset);
|
|
proto_tree_add_uint(tetra_tree, hf_tetra_rvstr, tvb, offset, 4, rxreg);
|
|
|
|
/* Logical channels */
|
|
channels = rxreg & 0x3;
|
|
tetra_sub_item = proto_tree_add_uint( tetra_tree, hf_tetra_channels, tvb, offset, 4, channels );
|
|
tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
|
|
if (channels > 3) {
|
|
expert_add_info(pinfo, tetra_sub_item, &ei_tetra_channels_incorrect);
|
|
channels = 3;
|
|
}
|
|
|
|
pdu_offset = offset + 4;
|
|
for(i = 0; i < channels; i++) {
|
|
gint byte_len, bits_len, remaining_bits;
|
|
gint hf_channel[3];
|
|
|
|
hf_channel[0] = hf_tetra_rxchannel1;
|
|
hf_channel[1] = hf_tetra_rxchannel2;
|
|
hf_channel[2] = hf_tetra_rxchannel3;
|
|
|
|
/* Channel type */
|
|
channel_type = (rxreg >> ((i + 1) * 4) ) & 0xf;
|
|
proto_tree_add_uint( tetra_header_tree, hf_channel[i], tvb, offset, 4, channel_type);
|
|
|
|
/* CRC */
|
|
proto_tree_add_boolean( tetra_header_tree, hf_tetra_crc, tvb, offset, 4, !(rxreg >> (i + 2) & 0x01));
|
|
|
|
/* PDU */
|
|
bits_len = get_rx_pdu_length(channel_type);
|
|
byte_len = bits_len >> 3;
|
|
remaining_bits = bits_len % 8;
|
|
if ((remaining_bits)!=0)
|
|
byte_len++;
|
|
|
|
payload_tvb = tvb_new_subset_length(tvb, pdu_offset, byte_len);
|
|
tetra_dissect_pdu(channel_type, TETRA_UPLINK, payload_tvb, tetra_header_tree, pinfo);
|
|
|
|
if ((remaining_bits)!=0)
|
|
byte_len--;
|
|
pdu_offset += byte_len;
|
|
}
|
|
}
|
|
|
|
static void dissect_tetra_UNITDATA_REQ(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tetra_tree, int offset)
|
|
{
|
|
guint32 txreg = 0;
|
|
guint32 channels = 0, i;
|
|
guint32 channel_type;
|
|
gint pdu_offset = 0;
|
|
proto_item *tetra_sub_item = NULL;
|
|
proto_tree *tetra_header_tree = NULL;
|
|
tvbuff_t *payload_tvb;
|
|
|
|
/* TxR */
|
|
txreg = tvb_get_letohl(tvb, offset);
|
|
proto_tree_add_uint(tetra_tree, hf_tetra_txreg, tvb, offset, 4, txreg);
|
|
|
|
/* Logical channels */
|
|
channels = (txreg & 0x3) + 1;
|
|
tetra_sub_item = proto_tree_add_uint( tetra_tree, hf_tetra_channels, tvb, offset, 4, channels );
|
|
tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
|
|
txreg >>= 2;
|
|
/* Skip 0000B */
|
|
if(channels == 2)
|
|
txreg >>= 4;
|
|
|
|
if (channels > 3) {
|
|
expert_add_info(pinfo, tetra_sub_item, &ei_tetra_channels_incorrect);
|
|
channels = 3;
|
|
}
|
|
|
|
pdu_offset = offset + 4;
|
|
for(i = 0; i < channels; i++) {
|
|
gint byte_len, bits_len, remaining_bits;
|
|
gint hf_channel[3];
|
|
|
|
hf_channel[0] = hf_tetra_channel1;
|
|
hf_channel[1] = hf_tetra_channel2;
|
|
hf_channel[2] = hf_tetra_channel3;
|
|
|
|
channel_type = txreg & 0xf;
|
|
proto_tree_add_uint( tetra_header_tree, hf_channel[i], tvb, offset, 4, channel_type);
|
|
txreg >>= 4;
|
|
/* PDU */
|
|
bits_len = get_tx_pdu_length(channel_type);
|
|
byte_len = bits_len >> 3;
|
|
remaining_bits = bits_len % 8;
|
|
if ((remaining_bits)!=0)
|
|
byte_len++;
|
|
|
|
payload_tvb = tvb_new_subset_length(tvb, pdu_offset, byte_len);
|
|
tetra_dissect_pdu(channel_type, TETRA_DOWNLINK, payload_tvb, tetra_header_tree, pinfo);
|
|
pdu_offset += byte_len;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dissect_tetra(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
|
|
proto_item *tetra_item = NULL;
|
|
proto_item *tetra_sub_item = NULL;
|
|
proto_tree *tetra_tree = NULL;
|
|
proto_tree *tetra_header_tree = NULL;
|
|
guint16 type = 0;
|
|
guint8 carriernumber = -1;
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_tetra);
|
|
/* Clear out stuff in the info column */
|
|
col_clear(pinfo->cinfo,COL_INFO);
|
|
|
|
/*
|
|
* This is not a good way of dissecting packets. The tvb length should
|
|
* be sanity checked so we aren't going past the actual size of the buffer.
|
|
*/
|
|
type = tvb_get_guint8(tvb, 0);
|
|
|
|
if(include_carrier_number) {
|
|
carriernumber = tvb_get_guint8(tvb, 1);
|
|
}
|
|
|
|
|
|
switch(type) {
|
|
case 1:
|
|
if(include_carrier_number)
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-REQ, Carrier: %d",
|
|
carriernumber);
|
|
else
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-REQ");
|
|
break;
|
|
case 2:
|
|
if(include_carrier_number)
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-IND, Carrier: %d",
|
|
carriernumber);
|
|
else
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-IND");
|
|
break;
|
|
case 3:
|
|
if(include_carrier_number)
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "MAC-Timer, Carrier: %d",
|
|
carriernumber);
|
|
else
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "MAC-Timer");
|
|
break;
|
|
case 127:
|
|
if(include_carrier_number)
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-IND Done, Carrier: %d",
|
|
carriernumber);
|
|
else
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-IND Done");
|
|
break;
|
|
case 128:
|
|
if(include_carrier_number)
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-REQ Done, Carrier: %d",
|
|
carriernumber);
|
|
else
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Tetra-UNITDATA-REQ Done");
|
|
break;
|
|
default:
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown command: %d", type);
|
|
break;
|
|
}
|
|
|
|
if (tree) { /* we are being asked for details */
|
|
guint32 offset = 0;
|
|
guint32 txtimer = 0;
|
|
guint32 tslot = 0;
|
|
|
|
tetra_item = proto_tree_add_item(tree, proto_tetra, tvb, 0, -1, ENC_NA);
|
|
tetra_tree = proto_item_add_subtree(tetra_item, ett_tetra);
|
|
|
|
offset ++;
|
|
|
|
/* Carrier number */
|
|
if(include_carrier_number) {
|
|
proto_tree_add_uint(tetra_tree, hf_tetra_carriernumber, tvb, offset, 1, carriernumber);
|
|
offset ++;
|
|
}
|
|
|
|
/* Registers */
|
|
tetra_sub_item = proto_tree_add_item( tetra_tree, hf_tetra_header, tvb, offset, -1, ENC_NA );
|
|
tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
|
|
|
|
/* Timer */
|
|
txtimer = tvb_get_letohl(tvb, offset);
|
|
tetra_sub_item = proto_tree_add_item(tetra_header_tree, hf_tetra_timer, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
tslot = ((txtimer & 0x7800) >> 11);
|
|
if(tslot==4)
|
|
tslot = 3;
|
|
if(tslot==8)
|
|
tslot = 4;
|
|
proto_item_append_text(tetra_sub_item, " (Multiple frame: %d, Frame: %d, Slot: %d)",
|
|
txtimer & 0x3F, (txtimer & 0x7c0) >> 6,
|
|
tslot);
|
|
|
|
offset += 4;
|
|
|
|
switch(type) {
|
|
case 1: /* tetra-UNITDATA-REQ */
|
|
case 128: /* tetra-UNITDATA-REQ Done */
|
|
dissect_tetra_UNITDATA_REQ(tvb, pinfo, tetra_header_tree, offset);
|
|
break;
|
|
case 2: /* tetra-UNITDATA-IND */
|
|
case 127: /* tetra-UNITDATA-IND Done */
|
|
dissect_tetra_UNITDATA_IND(tvb, pinfo, tetra_header_tree, offset);
|
|
break;
|
|
case 3: /* MAC-Timer */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void proto_reg_handoff_tetra(void)
|
|
{
|
|
static gboolean initialized=FALSE;
|
|
|
|
if (!initialized) {
|
|
data_handle = find_dissector("data");
|
|
tetra_handle = create_dissector_handle(dissect_tetra, proto_tetra);
|
|
dissector_add_uint("udp.port", global_tetra_port, tetra_handle);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void proto_register_tetra (void)
|
|
{
|
|
module_t *tetra_module;
|
|
expert_module_t* expert_tetra;
|
|
|
|
/*
|
|
* A header field is something you can search/filter on.
|
|
*
|
|
* We create a structure to register our fields. It consists of an
|
|
* array of hf_register_info structures, each of which are of the format
|
|
* {&(field id), {name, abbrev, type, display, strings, bitmask, blurb, HFILL}}.
|
|
*/
|
|
static hf_register_info hf[] = {
|
|
{ &hf_tetra,
|
|
{ "Data", "tetra.data", FT_NONE, BASE_NONE, NULL, 0x0,
|
|
"tetra PDU", HFILL }},
|
|
{ &hf_tetra_header,
|
|
{ "Registers", "tetra.header", FT_NONE, BASE_NONE, NULL, 0x0,
|
|
"TETRA Registers", HFILL }},
|
|
{ &hf_tetra_channels,
|
|
{ "Logical Channels", "tetra.channels", FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
"The amount of logical channels", HFILL }},
|
|
{ &hf_tetra_channel1,
|
|
{ "Channel 1", "tetra.txchannel1", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
|
|
"Logical channels type", HFILL }},
|
|
{ &hf_tetra_channel2,
|
|
{ "Channel 2", "tetra.txchannel2", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
|
|
"Logical channels type", HFILL }},
|
|
{ &hf_tetra_channel3,
|
|
{ "Channel 3", "tetra.txchannel3", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
|
|
"Logical channels type", HFILL }},
|
|
{ &hf_tetra_txreg,
|
|
{ "TxR", "tetra.txreg", FT_UINT16, BASE_HEX, NULL, 0x0,
|
|
"TX Register", HFILL }},
|
|
{ &hf_tetra_rvstr,
|
|
{ "RvSteR", "tetra.rvster", FT_UINT16, BASE_HEX, NULL, 0x0,
|
|
"Receive Status Register", HFILL }},
|
|
{ &hf_tetra_carriernumber,
|
|
{ "Carrier Number", "tetra.carrier", FT_UINT8, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL }},
|
|
{ &hf_tetra_rxchannel1,
|
|
{ "Channel 1", "tetra.rxchannel1", FT_UINT8, BASE_DEC, VALS(recvchanneltypenames), 0x0,
|
|
"Logical channels type", HFILL }},
|
|
{ &hf_tetra_rxchannel2,
|
|
{ "Channel 2", "tetra.rxchannel2", FT_UINT8, BASE_DEC, VALS(recvchanneltypenames), 0x0,
|
|
"Logical channels type", HFILL }},
|
|
{ &hf_tetra_rxchannel3,
|
|
{ "Channel 3", "tetra.rxchannel3", FT_UINT8, BASE_DEC, VALS(recvchanneltypenames), 0x0,
|
|
"Logical channels type", HFILL }},
|
|
{ &hf_tetra_timer,
|
|
{ "Timer", "tetra.timer", FT_UINT16, BASE_HEX, NULL, 0x0,
|
|
"Timer Register", HFILL }},
|
|
{ &hf_tetra_crc,
|
|
{ "CRC", "tetra.crc", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
|
|
"CRC result", HFILL }},
|
|
{ &hf_tetra_len0,
|
|
{ "Length", "tetra.len0", FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
"Length of the PDU", HFILL }},
|
|
{ &hf_tetra_pdu,
|
|
{ "PDU", "tetra.pdu", FT_BYTES, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL }} ,
|
|
|
|
#include "packet-tetra-hfarr.c"
|
|
};
|
|
|
|
/* List of subtrees */
|
|
static gint *ett[] = {
|
|
&ett_tetra,
|
|
&ett_tetra_header,
|
|
&ett_tetra_length,
|
|
&ett_tetra_txreg,
|
|
&ett_tetra_text,
|
|
#include "packet-tetra-ettarr.c"
|
|
};
|
|
|
|
static ei_register_info ei[] = {
|
|
{ &ei_tetra_channels_incorrect, { "tetra.channels.incorrect", PI_MALFORMED, PI_WARN, "Channel count incorrect, must be <= 3", EXPFILL }},
|
|
};
|
|
|
|
proto_tetra = proto_register_protocol("TETRA Protocol", "tetra", "tetra");
|
|
proto_register_field_array (proto_tetra, hf, array_length (hf));
|
|
proto_register_subtree_array (ett, array_length (ett));
|
|
register_dissector("tetra", dissect_tetra, proto_tetra);
|
|
expert_tetra = expert_register_protocol(proto_tetra);
|
|
expert_register_field_array(expert_tetra, ei, array_length(ei));
|
|
|
|
tetra_module = prefs_register_protocol(proto_tetra, NULL);
|
|
prefs_register_bool_preference(tetra_module, "include_carrier_number",
|
|
"The data include carrier numbers",
|
|
"Whether the captured data include carrier number",
|
|
&include_carrier_number);
|
|
}
|