ac4a184768504df8ce5491892b2ca4f78acad15f
[obnox/wireshark/wip.git] / asn1 / tetra / packet-tetra-template.c
1 /* packet-tetra.c
2  * Routines for TETRA packet dissection
3  *
4  *$Id$
5  *
6  * Copyright (c) 2007 - 2011 Professional Mobile Communication Research Group,
7  *    Beijing Institute of Technology, China
8  * Copyright (c) 2011 Holger Hans Peter Freyther
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1998 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  *
28  * REF: ETSI EN 300 392-2 V3.2.1
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <glib.h>
36 #include <epan/packet.h>
37 #include <epan/conversation.h>
38 #include <epan/asn1.h>
39 #include <epan/prefs.h>
40
41 #include <stdio.h>
42 #include <string.h>
43
44 #include <epan/dissectors/packet-per.h>
45 #include "packet-tetra.h"
46
47 #define PROTO_TAG_tetra "TETRA"
48
49 /* Wireshark ID of the tetra protocol */
50 static int proto_tetra = -1;
51
52 /* These are the handles of our subdissectors */
53 static dissector_handle_t data_handle = NULL;
54
55 static dissector_handle_t tetra_handle;
56 static void dissect_tetra(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
57
58 static int global_tetra_port = 7074;
59
60 /* Whether the capture data include carrier numbers */
61 static gboolean include_carrier_number = TRUE;
62
63 /* The following hf_* variables are used to hold the Wireshark IDs of
64 * our header fields; they are filled out when we call
65 * proto_register_field_array() in proto_register_tetra()
66 */
67 /** Kts attempt at defining the protocol */
68 static gint hf_tetra = -1;
69 static gint hf_tetra_header = -1;
70 static gint hf_tetra_channels = -1;
71 static gint hf_tetra_channel1 = -1;
72 static gint hf_tetra_channel2 = -1;
73 static gint hf_tetra_channel3 = -1;
74 static gint hf_tetra_txreg = -1;
75 static gint hf_tetra_timer = -1;
76 static gint hf_tetra_pdu = -1;
77 static gint hf_tetra_rvstr = -1;
78 static gint hf_tetra_carriernumber = -1;
79 static gint hf_tetra_rxchannel1 = -1;
80 static gint hf_tetra_rxchannel2 = -1;
81 static gint hf_tetra_crc = -1;
82 static gint hf_tetra_len0 = -1;
83
84 #include "packet-tetra-hf.c"
85
86 /* Initialize the subtree pointers */
87 /* These are the ids of the subtrees that we may be creating */
88 static gint ett_tetra = -1;
89 static gint ett_tetra_header = -1;
90 static gint ett_tetra_length = -1;
91 static gint ett_tetra_txreg = -1;
92 static gint ett_tetra_text = -1;
93
94 #include "packet-tetra-ett.c"
95
96 #include "packet-tetra-fn.c"
97
98 static const value_string channeltypenames[] = {
99         { 0, "Reserved" },
100         { 1, "AACH" },
101         { 2, "SCH/F" },
102         { 3, "SCH/HD" },
103         { 4, "Unknown" },
104         { 5, "BSCH" },
105         { 6, "BNCH" },
106         { 7, "TCH/F" },
107         { 8, "TCH/H" },
108         { 9, "TCH4.8"},
109         { 10, "TCH7.2"},
110         { 11, "STCH"},
111         { 0, NULL }
112 };
113
114 static const value_string recvchanneltypenames[] = {
115         { 0, "Reserved" },
116         { 1, "AACH" },
117         { 2, "SCH/F" },
118         { 3, "SCH/HD" },
119         { 4, "Unknown" },
120         { 5, "BSCH" },
121         { 6, "BNCH" },
122         { 7, "TCH/F" },
123         { 8, "TCH/H" },
124         { 9, "TCH4.8"},
125         { 10, "TCH7.2"},
126         { 11, "STCH"},
127         { 15, "SCH/HU"},
128         { 0, NULL }
129 };
130
131 /* Get the length of received pdu */
132 static gint get_rx_pdu_length(guint32 channel_type)
133 {
134         gint len = 0;
135
136         switch(channel_type) {
137         case TETRA_CHAN_AACH:
138                 len = 14;
139                 break;
140         case TETRA_CHAN_SCH_F:
141                 len = 268;
142                 break;
143         case TETRA_CHAN_SCH_D:
144                 len = 124; ;
145                 break;
146         case TETRA_CHAN_BSCH:
147                 len = 60;
148                 break;
149         case TETRA_CHAN_BNCH:
150                 len = 124;
151                 break;
152         case TETRA_CHAN_TCH_F:
153                 len = 274;
154                 break;
155         case TETRA_CHAN_TCH_H:
156                 len = 137;
157                 break;
158         case TETRA_CHAN_TCH_2_4:
159                 len = 144;
160                 break;
161         case TETRA_CHAN_TCH_4_8:
162                 len = 288;
163                 break;
164         case TETRA_CHAN_STCH:
165                 len = 124;
166                 break;
167         case TETRA_CHAN_SCH_HU:
168                 len = 92;
169                 break;
170         default:
171                 len = 0;
172                 break;
173         }
174
175         return len;
176 }
177
178 /* Get the length of transmitted pdu */
179 static gint get_tx_pdu_length(guint32 channel_type)
180 {
181         gint len = 0;
182
183         switch(channel_type) {
184         case TETRA_CHAN_AACH:
185                 len = 14;
186                 break;
187         case TETRA_CHAN_SCH_F:
188                 len = 268;
189                 break;
190         case TETRA_CHAN_SCH_D:
191                 len = 124;
192                 break;
193         case TETRA_CHAN_BSCH:
194                 len = 60;
195                 break;
196         case TETRA_CHAN_BNCH:
197                 len = 124;
198                 break;
199         case TETRA_CHAN_TCH_F:
200                 len = 274;
201                 break;
202         case TETRA_CHAN_TCH_H:
203                 len = 137;
204                 break;
205         case TETRA_CHAN_TCH_2_4:
206                 len = 144;
207                 break;
208         case TETRA_CHAN_TCH_4_8:
209                 len = 288;
210                 break;
211         case TETRA_CHAN_STCH:
212                 len = 124;
213                 break;
214         }
215
216         return len;
217 }
218
219 void tetra_dissect_pdu(int channel_type, int dir, tvbuff_t *pdu, proto_tree *tree, packet_info *pinfo)
220 {
221         proto_item *tetra_sub_item;
222         proto_tree *tetra_sub_tree;
223         guint8 p;
224
225         tetra_sub_item = proto_tree_add_item(tree, hf_tetra_pdu,
226                                              pdu, 0, tvb_length(pdu), ENC_NA);
227
228         tetra_sub_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
229
230         switch(channel_type) {
231         case TETRA_CHAN_AACH:
232                 dissect_AACH_PDU(pdu, pinfo, tetra_sub_tree );
233                 break;
234         case TETRA_CHAN_SCH_F:
235                 p = tvb_get_guint8(pdu, 0);
236                 switch(p >> 6) {
237                 case 0:
238                         dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree );
239                         break;
240                 case 1: /* MAC-FRAG or MAC-END */
241                         if((p >> 5) == 3) {
242                                 if (dir == TETRA_DOWNLINK)
243                                         dissect_MAC_END_DOWNLINK_PDU(pdu, pinfo, tetra_sub_tree );
244                                 else
245                                         dissect_MAC_END_UPLINK_PDU(pdu, pinfo, tetra_sub_tree);
246
247                         } else
248                                 dissect_MAC_FRAG_PDU(pdu, pinfo, tetra_sub_tree );
249                         break;
250                 case 2:
251                         dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree );
252                         break;
253                 }
254                 break;
255         case TETRA_CHAN_SCH_D:
256                 p = tvb_get_guint8(pdu, 0);
257                 switch(p >> 6) {
258                 case 0:
259                         dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree );
260                         break;
261                 case 1: /* MAC-FRAG or MAC-END */
262                         if((p >> 5) == 3)
263                                 dissect_MAC_END_DOWN111_PDU(pdu, pinfo, tetra_sub_tree );
264                         else
265                                 dissect_MAC_FRAG120_PDU(pdu, pinfo, tetra_sub_tree );
266                 break;
267                 case 2:
268                         dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree );
269                         break;
270                 }
271                 break;
272         case TETRA_CHAN_SCH_HU:
273                 p = tvb_get_guint8(pdu, 0);
274                 switch(p >> 7) {
275                 case 0: /* MAC-ACCESS */
276                         dissect_MAC_ACCESS_PDU(pdu, pinfo, tetra_sub_tree);
277                         break;
278                 case 1: /* MAC-END-HU */
279                         dissect_MAC_END_HU_PDU(pdu, pinfo, tetra_sub_tree);
280                         break;
281                 }
282                 break;
283         case TETRA_CHAN_BSCH:
284                 dissect_BSCH_PDU(pdu, pinfo, tetra_sub_tree );
285                 break;
286         case TETRA_CHAN_BNCH:
287                 dissect_BNCH_PDU(pdu, pinfo, tetra_sub_tree );
288                 break;
289         case TETRA_CHAN_STCH:
290                 p = tvb_get_guint8(pdu, 0);
291                 switch(p >> 6) {
292                 case 0:
293                         dissect_MAC_RESOURCE_PDU(pdu, pinfo, tetra_sub_tree );
294                         break;
295                 case 1: /* MAC-FRAG or MAC-END */
296                         if((p >> 5) == 3) {
297                                 if (dir == TETRA_DOWNLINK)
298                                         dissect_MAC_END_DOWN111_PDU(pdu, pinfo, tetra_sub_tree );
299                                 else
300                                         dissect_MAC_END_UP114_PDU(pdu, pinfo, tetra_sub_tree);
301                         } else
302                                 dissect_MAC_FRAG120_PDU(pdu, pinfo, tetra_sub_tree );
303                         break;
304                 case 2:
305                         dissect_MAC_ACCESS_DEFINE_PDU(pdu, pinfo, tetra_sub_tree );
306                         break;
307                 }
308                 break;
309         }
310 }
311
312 static void dissect_tetra_UNITDATA_IND(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tetra_tree, int offset)
313 {
314         guint32 rxreg = 0;
315         guint32 channels = 0, i;
316         guint32 channel_type;
317         gint pdu_offset = 0;
318         proto_item *tetra_sub_item;
319         proto_tree *tetra_header_tree = NULL;
320         tvbuff_t *payload_tvb;
321
322         /* Length */
323         rxreg = tvb_get_letohl(tvb, offset);
324         tetra_sub_item = proto_tree_add_uint(tetra_tree, hf_tetra_len0, tvb, offset, 4, rxreg);
325
326         /* RvSteR */
327         offset += 4;
328         rxreg = tvb_get_letohl(tvb, offset);
329         tetra_sub_item = proto_tree_add_uint(tetra_tree, hf_tetra_rvstr, tvb, offset, 4, rxreg);
330
331         /* Logical channels */
332         channels = rxreg & 0x3;
333         tetra_sub_item = proto_tree_add_uint( tetra_tree, hf_tetra_channels, tvb, offset, 4, channels );
334         tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
335
336         pdu_offset = offset + 4;
337         for(i = 0; i < channels; i++) {
338                 gint hf_channel[] = {hf_tetra_rxchannel1, hf_tetra_rxchannel2};
339                 gint byte_len, bits_len, remaining_bits;
340
341                 /* Channel type */
342                 channel_type = (rxreg >> ((i + 1) * 4) ) & 0xf;
343                 proto_tree_add_uint( tetra_header_tree, hf_channel[i], tvb, offset, 4, channel_type);
344
345                 /* CRC */
346                 proto_tree_add_boolean( tetra_header_tree, hf_tetra_crc, tvb, offset, 4, !(rxreg >> (i + 2) & 0x01));
347
348                 /* PDU */
349                 bits_len = get_rx_pdu_length(channel_type);
350                 byte_len = bits_len >> 3;
351                 remaining_bits = bits_len % 8;
352                 if ((remaining_bits)!=0)
353                         byte_len++;
354
355                 payload_tvb = tvb_new_subset(tvb, pdu_offset, byte_len, byte_len);
356                 tetra_dissect_pdu(channel_type, TETRA_UPLINK, payload_tvb, tetra_header_tree, pinfo);
357
358                 if ((remaining_bits)!=0)
359                         byte_len--;
360                 pdu_offset += byte_len;
361         }
362 }
363
364 void dissect_tetra_UNITDATA_REQ(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tetra_tree, int offset)
365 {
366         guint32 txreg = 0;
367         guint32 channels = 0, i;
368         guint32 channel_type;
369         gint pdu_offset = 0;
370         proto_item *tetra_sub_item = NULL;
371         proto_tree *tetra_header_tree = NULL;
372         tvbuff_t *payload_tvb;
373
374         /* TxR */
375         txreg = tvb_get_letohl(tvb, offset);
376         tetra_sub_item = proto_tree_add_uint(tetra_tree, hf_tetra_txreg, tvb, offset, 4, txreg);
377
378         /* Logical channels */
379         channels = (txreg & 0x3) + 1;
380         tetra_sub_item = proto_tree_add_uint( tetra_tree, hf_tetra_channels, tvb, offset, 4, channels );
381         tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
382         txreg >>= 2;
383         /* Skip 0000B */
384         if(channels == 2)
385                 txreg >>= 4;
386
387         pdu_offset = offset + 4;
388         for(i = 0; i < channels; i++) {
389                 gint hf_channel[] = {hf_tetra_channel1, hf_tetra_channel2, hf_tetra_channel3};
390                 gint byte_len, bits_len, remaining_bits;
391
392                 channel_type = txreg & 0xf;
393                 proto_tree_add_uint( tetra_header_tree, hf_channel[i], tvb, offset, 4, channel_type);
394                 txreg >>= 4;
395                 /* PDU */
396                 bits_len = get_tx_pdu_length(channel_type);
397                 byte_len = bits_len >> 3;
398                 remaining_bits = bits_len % 8;
399                 if ((remaining_bits)!=0)
400                                 byte_len++;
401
402                 payload_tvb = tvb_new_subset(tvb, pdu_offset, byte_len, byte_len);
403                 tetra_dissect_pdu(channel_type, TETRA_DOWNLINK, payload_tvb, tetra_header_tree, pinfo);
404                 pdu_offset += byte_len;
405         }
406 }
407
408 static void
409 dissect_tetra(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
410 {
411
412         proto_item *tetra_item = NULL;
413         proto_item *tetra_sub_item = NULL;
414         proto_tree *tetra_tree = NULL;
415         proto_tree *tetra_header_tree = NULL;
416         guint16 type = 0;
417         guint16 carriernumber = -1;
418
419         col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_tetra);
420         /* Clear out stuff in the info column */
421         col_clear(pinfo->cinfo,COL_INFO);
422
423         /*
424          * This is not a good way of dissecting packets.  The tvb length should
425          * be sanity checked so we aren't going past the actual size of the buffer.
426          */
427         type = tvb_get_guint8(tvb, 0);
428
429         if(include_carrier_number) {
430                 carriernumber = tvb_get_guint8(tvb, 1);
431                 carriernumber |= 0xff00;
432         }
433
434
435         switch(type) {
436         case 1:
437                 if(include_carrier_number)
438                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d tetra-UNITDATA-REQ, Carrier: %d",
439                                         pinfo->srcport, pinfo->destport, carriernumber);
440                 else
441                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d tetra-UNITDATA-REQ",
442                                         pinfo->srcport, pinfo->destport);
443                 break;
444         case 2:
445                 if(include_carrier_number)
446                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d tetra-UNITDATA-IND, Carrier: %d",
447                                         pinfo->srcport, pinfo->destport, carriernumber);
448                 else
449                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d tetra-UNITDATA-IND",
450                                         pinfo->srcport, pinfo->destport);
451                 break;
452         case 3:
453                 if(include_carrier_number)
454                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d MAC-Timer, Carrier: %d",
455                                         pinfo->srcport, pinfo->destport, carriernumber);
456                 else
457                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d MAC-Timer",
458                                         pinfo->srcport, pinfo->destport);
459                 break;
460         case 127:
461                 if(include_carrier_number)
462                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d tetra-UNITDATA-IND Done, Carrier: %d",
463                                         pinfo->srcport, pinfo->destport, carriernumber);
464                 else
465                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d tetra-UNITDATA-IND Done",
466                                         pinfo->srcport, pinfo->destport);
467                 break;
468         case 128:
469                 if(include_carrier_number)
470                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d tetra-UNITDATA-REQ Done, Carrier: %d",
471                                         pinfo->srcport, pinfo->destport, carriernumber);
472           else
473                         col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d tetra-UNITDATA-REQ Done",
474                                         pinfo->srcport, pinfo->destport);
475                 break;
476         default:
477                 col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d Unknown command: %d",
478                                 pinfo->srcport, pinfo->destport, type);
479                 break;
480         }
481
482         if (tree) { /* we are being asked for details */
483                 guint32 offset = 0;
484                 guint32 txtimer = 0;
485                 guint32 tslot = 0;
486
487                 tetra_item = proto_tree_add_item(tree, proto_tetra, tvb, 0, -1, ENC_NA);
488                 tetra_tree = proto_item_add_subtree(tetra_item, ett_tetra);
489                 tetra_header_tree = proto_item_add_subtree(tetra_item, ett_tetra);
490
491                 offset ++;
492
493                 /* Carrier number */
494                 if(include_carrier_number) {
495                         tetra_sub_item = proto_tree_add_uint(tetra_tree, hf_tetra_carriernumber, tvb, offset, 1, carriernumber);
496                         offset ++;
497                 }
498
499                 /* Registers */
500                 tetra_sub_item = proto_tree_add_item( tetra_tree, hf_tetra_header, tvb, offset, -1, ENC_NA );
501                 tetra_header_tree = proto_item_add_subtree(tetra_sub_item, ett_tetra);
502
503                 /* Timer */
504                 txtimer = tvb_get_letohl(tvb, offset);
505                 tetra_sub_item = proto_tree_add_item(tetra_header_tree, hf_tetra_timer, tvb, offset, 4, TRUE);
506                 tslot = ((txtimer & 0x7800) >> 11);
507                 if(tslot==4)
508                         tslot = 3;
509                 if(tslot==8)
510                         tslot = 4;
511                 proto_item_append_text(tetra_sub_item, " (Multiple frame: %d, Frame: %d, Slot: %d)",
512                                                                                                         txtimer & 0x3F, (txtimer & 0x7c0) >> 6,
513                                                                                                         tslot);
514
515                 offset += 4;
516
517                 switch(type) {
518                 case 1: /* tetra-UNITDATA-REQ */
519                 case 128: /* tetra-UNITDATA-REQ Done */
520                         dissect_tetra_UNITDATA_REQ(tvb, pinfo, tetra_header_tree, offset);
521                         break;
522                 case 2: /* tetra-UNITDATA-IND */
523                 case 127: /* tetra-UNITDATA-IND Done */
524                         dissect_tetra_UNITDATA_IND(tvb, pinfo, tetra_header_tree, offset);
525                         break;
526                 case 3: /* MAC-Timer */
527                         break;
528                 default:
529                         break;
530                 }
531         }
532 }
533
534 void proto_reg_handoff_tetra(void)
535 {
536         static gboolean initialized=FALSE;
537
538         if (!initialized) {
539                 data_handle = find_dissector("data");
540                 tetra_handle = create_dissector_handle(dissect_tetra, proto_tetra);
541                 dissector_add_uint("udp.port", global_tetra_port, tetra_handle);
542         }
543
544 }
545
546
547 void proto_register_tetra (void)
548 {
549         module_t *per_module;
550
551         /*
552          * A header field is something you can search/filter on.
553          *
554          * We create a structure to register our fields. It consists of an
555          * array of hf_register_info structures, each of which are of the format
556          * {&(field id), {name, abbrev, type, display, strings, bitmask, blurb, HFILL}}.
557          */
558         static hf_register_info hf[] = {
559                 { &hf_tetra,
560                 { "Data", "tetra.data", FT_NONE, BASE_NONE, NULL, 0x0,
561                 "tetra PDU", HFILL }},
562                 { &hf_tetra_header,
563                 { "Registers", "tetra.header", FT_NONE, BASE_NONE, NULL, 0x0,
564                  "TETRA Registers", HFILL }},
565                 { &hf_tetra_channels,
566                 { "Logical Channels", "tetra.channels", FT_UINT8, BASE_DEC, NULL, 0x0,
567                 "The amount of logical channels", HFILL }},
568                 { &hf_tetra_channel1,
569                 { "Channel 1", "tetra.txchannel1", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
570                 "Logical channels type", HFILL }},
571                 { &hf_tetra_channel2,
572                 { "Channel 2", "tetra.txchannel2", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
573                 "Logical channels type", HFILL }},
574                 { &hf_tetra_channel3,
575                 { "Channel 3", "tetra.txchannel3", FT_UINT8, BASE_DEC, VALS(channeltypenames), 0x0,
576                 "Logical channels type", HFILL }},
577                 { &hf_tetra_txreg,
578                 { "TxR", "tetra.txreg", FT_UINT16, BASE_HEX, NULL, 0x0,
579                  "TX Register", HFILL }},
580                 { &hf_tetra_rvstr,
581                 { "RvSteR", "tetra.rvster", FT_UINT16, BASE_HEX, NULL, 0x0,
582                  "Receive Status Register", HFILL }},
583                 { &hf_tetra_carriernumber,
584                 { "Carrier Number", "tetra.carrier", FT_UINT16, BASE_HEX, NULL, 0x0,
585                  NULL, HFILL }},
586                 { &hf_tetra_rxchannel1,
587                 { "Channel 1", "tetra.rxchannel1", FT_UINT8, BASE_DEC, VALS(recvchanneltypenames), 0x0,
588                 "Logical channels type", HFILL }},
589                 { &hf_tetra_rxchannel2,
590                 { "Channel 2", "tetra.rxchannel2", FT_UINT8, BASE_DEC, VALS(recvchanneltypenames), 0x0,
591                 "Logical channels type", HFILL }},
592                 { &hf_tetra_timer,
593                 { "Timer", "tetra.timer", FT_UINT16, BASE_HEX, NULL, 0x0,
594                  "Timer Register", HFILL }},
595                 { &hf_tetra_crc,
596                 { "CRC", "tetra.crc", FT_BOOLEAN, BASE_DEC, NULL, 0x0,
597                  "CRC result", HFILL }},
598                 { &hf_tetra_len0,
599                 { "Length", "tetra.len0", FT_UINT16, BASE_DEC, NULL, 0x0,
600                  "Length of the PDU", HFILL }},
601                 { &hf_tetra_pdu,
602                 { "PDU", "tetra.pdu", FT_BYTES, BASE_NONE, NULL, 0x0,
603                  NULL, HFILL }} ,
604
605 #include "packet-tetra-hfarr.c"
606         };
607
608         /* List of subtrees */
609         static gint *ett[] = {
610                 &ett_tetra,
611                 &ett_tetra_header,
612                 &ett_tetra_length,
613                 &ett_tetra_txreg,
614                 &ett_tetra_text,
615 #include "packet-tetra-ettarr.c"
616         };
617
618         /* execute protocol initialization only once */
619         if (proto_tetra != -1)
620                 return;
621
622         proto_tetra = proto_register_protocol("TETRA Protocol", "tetra", "tetra");
623         proto_register_field_array (proto_tetra, hf, array_length (hf));
624         proto_register_subtree_array (ett, array_length (ett));
625         register_dissector("tetra", dissect_tetra, proto_tetra);
626
627         per_module = prefs_register_protocol(proto_tetra, NULL);
628         prefs_register_bool_preference(per_module, "include_carrier_number",
629                         "The data include carrier numbers",
630                         "Whether the captured data include carrier number",
631                         &include_carrier_number);
632 }