aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-llrp.c
blob: e09957dfd238d760542562cae48e61cc5cbba7c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
/* packet-llrp.c
 * Routines for Low Level Reader Protocol dissection
 * Copyright 2012, Evan Huus <eapache@gmail.com>
 *
 * http://www.gs1.org/gsmp/kc/epcglobal/llrp
 *
 * $Id$
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <glib.h>

#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>

#define LLRP_PORT 5084

/* Initialize the protocol and registered fields */
static int proto_llrp         = -1;
static int hf_llrp_version    = -1;
static int hf_llrp_type       = -1;
static int hf_llrp_length     = -1;
static int hf_llrp_id         = -1;
static int hf_llrp_cur_ver    = -1;
static int hf_llrp_sup_ver    = -1;
static int hf_llrp_req_cap    = -1;
static int hf_llrp_req_conf   = -1;
static int hf_llrp_rospec     = -1;
static int hf_llrp_antenna_id = -1;
static int hf_llrp_gpi_port   = -1;
static int hf_llrp_gpo_port   = -1;
static int hf_llrp_rest_fact  = -1;
static int hf_llrp_accessspec = -1;
static int hf_llrp_vendor     = -1;
static int hf_llrp_tlv_type   = -1;
static int hf_llrp_tv_type    = -1;
static int hf_llrp_tlv_len    = -1;
static int hf_llrp_param      = -1;

/* Initialize the subtree pointers */
static gint ett_llrp = -1;
static gint ett_llrp_param = -1;

/* Message Types */
#define LLRP_TYPE_GET_READER_CAPABILITES            1
#define LLRP_TYPE_GET_READER_CONFIG                 2
#define LLRP_TYPE_SET_READER_CONFIG                 3
#define LLRP_TYPE_CLOSE_CONNECTION_RESPONSE         4
#define LLRP_TYPE_GET_READER_CAPABILITES_RESPONSE  11
#define LLRP_TYPE_GET_READER_CONFIG_RESPONSE       12
#define LLRP_TYPE_SET_READER_CONFIG_RESPONSE       13
#define LLRP_TYPE_CLOSE_CONNECTION                 14
#define LLRP_TYPE_ADD_ROSPEC                       20
#define LLRP_TYPE_DELETE_ROSPEC                    21
#define LLRP_TYPE_START_ROSPEC                     22
#define LLRP_TYPE_STOP_ROSPEC                      23
#define LLRP_TYPE_ENABLE_ROSPEC                    24
#define LLRP_TYPE_DISABLE_ROSPEC                   25
#define LLRP_TYPE_GET_ROSPECS                      26
#define LLRP_TYPE_ADD_ROSPEC_RESPONSE              30
#define LLRP_TYPE_DELETE_ROSPEC_RESPONSE           31
#define LLRP_TYPE_START_ROSPEC_RESPONSE            32
#define LLRP_TYPE_STOP_ROSPEC_RESPONSE             33
#define LLRP_TYPE_ENABLE_ROSPEC_RESPONSE           34
#define LLRP_TYPE_DISABLE_ROSPEC_RESPONSE          35
#define LLRP_TYPE_GET_ROSPECS_RESPONSE             36
#define LLRP_TYPE_ADD_ACCESSSPEC                   40
#define LLRP_TYPE_DELETE_ACCESSSPEC                41
#define LLRP_TYPE_ENABLE_ACCESSSPEC                42
#define LLRP_TYPE_DISABLE_ACCESSSPEC               43
#define LLRP_TYPE_GET_ACCESSSPECS                  44
#define LLRP_TYPE_CLIENT_REQUEST_OP                45
#define LLRP_TYPE_GET_SUPPORTED_VERSION            46
#define LLRP_TYPE_SET_PROTOCOL_VERSION             47
#define LLRP_TYPE_ADD_ACCESSSPEC_RESPONSE          50
#define LLRP_TYPE_DELETE_ACCESSSPEC_RESPONSE       51
#define LLRP_TYPE_ENABLE_ACCESSSPEC_RESPONSE       52
#define LLRP_TYPE_DISABLE_ACCESSSPEC_RESPONSE      53
#define LLRP_TYPE_GET_ACCESSSPECS_RESPONSE         54
#define LLRP_TYPE_CLIENT_RESQUEST_OP_RESPONSE      55
#define LLRP_TYPE_GET_SUPPORTED_VERSION_RESPONSE   56
#define LLRP_TYPE_SET_PROTOCOL_VERSION_RESPONSE    57
#define LLRP_TYPE_GET_REPORT                       60
#define LLRP_TYPE_RO_ACCESS_REPORT                 61
#define LLRP_TYPE_KEEPALIVE                        62
#define LLRP_TYPE_READER_EVENT_NOTIFICATION        63
#define LLRP_TYPE_ENABLE_EVENTS_AND_REPORTS        64
#define LLRP_TYPE_KEEPALIVE_ACK                    72
#define LLRP_TYPE_ERROR_MESSAGE                   100
#define LLRP_TYPE_CUSTOM_MESSAGE                 1023

static const value_string message_types[] = {
    { LLRP_TYPE_GET_READER_CAPABILITES,          "Get Reader Capabilites"          },
    { LLRP_TYPE_GET_READER_CONFIG,               "Get Reader Config"               },
    { LLRP_TYPE_SET_READER_CONFIG,               "Set Reader Config"               },
    { LLRP_TYPE_CLOSE_CONNECTION_RESPONSE,       "Close Connection Response"       },
    { LLRP_TYPE_GET_READER_CAPABILITES_RESPONSE, "Get Reader Capabilites Response" },
    { LLRP_TYPE_GET_READER_CONFIG_RESPONSE,      "Get Reader Config Response"      },
    { LLRP_TYPE_SET_READER_CONFIG_RESPONSE,      "Set Reader Config Response"      },
    { LLRP_TYPE_CLOSE_CONNECTION,                "Close Connection"                },
    { LLRP_TYPE_ADD_ROSPEC,                      "Add ROSpec"                      },
    { LLRP_TYPE_DELETE_ROSPEC,                   "Delete ROSpec"                   },
    { LLRP_TYPE_START_ROSPEC,                    "Start ROSpec"                    },
    { LLRP_TYPE_STOP_ROSPEC,                     "Stop ROSpec"                     },
    { LLRP_TYPE_ENABLE_ROSPEC,                   "Enable ROSpec"                   },
    { LLRP_TYPE_DISABLE_ROSPEC,                  "Disable ROSpec"                  },
    { LLRP_TYPE_GET_ROSPECS,                     "Get ROSpecs"                     },
    { LLRP_TYPE_ADD_ROSPEC_RESPONSE,             "Add ROSpec Response"             },
    { LLRP_TYPE_DELETE_ROSPEC_RESPONSE,          "Delete ROSpec Response"          },
    { LLRP_TYPE_START_ROSPEC_RESPONSE,           "Start ROSpec Response"           },
    { LLRP_TYPE_STOP_ROSPEC_RESPONSE,            "Stop ROSpec Response"            },
    { LLRP_TYPE_ENABLE_ROSPEC_RESPONSE,          "Enable ROSpec Response"          },
    { LLRP_TYPE_DISABLE_ROSPEC_RESPONSE,         "Disable ROSpec Response"         },
    { LLRP_TYPE_GET_ROSPECS_RESPONSE,            "Get ROSpecs Response"            },
    { LLRP_TYPE_ADD_ACCESSSPEC,                  "Add AccessSpec"                  },
    { LLRP_TYPE_DELETE_ACCESSSPEC,               "Delete AccessSpec"               },
    { LLRP_TYPE_ENABLE_ACCESSSPEC,               "Enable AccessSpec"               },
    { LLRP_TYPE_DISABLE_ACCESSSPEC,              "Disable AccessSpec"              },
    { LLRP_TYPE_GET_ACCESSSPECS,                 "Get AccessSpecs"                 },
    { LLRP_TYPE_CLIENT_REQUEST_OP,               "Client Request OP"               },
    { LLRP_TYPE_GET_SUPPORTED_VERSION,           "Get Supported Version"           },
    { LLRP_TYPE_SET_PROTOCOL_VERSION,            "Set Protocol Version"            },
    { LLRP_TYPE_ADD_ACCESSSPEC_RESPONSE,         "Add AccessSpec Response"         },
    { LLRP_TYPE_DELETE_ACCESSSPEC_RESPONSE,      "Delete AccessSpec Response"      },
    { LLRP_TYPE_ENABLE_ACCESSSPEC_RESPONSE,      "Enable AccessSpec Response"      },
    { LLRP_TYPE_DISABLE_ACCESSSPEC_RESPONSE,     "Disable AccessSpec Response"     },
    { LLRP_TYPE_GET_ACCESSSPECS_RESPONSE,        "Get AccessSpecs Response"        },
    { LLRP_TYPE_CLIENT_RESQUEST_OP_RESPONSE,     "Client Resquest OP Response"     },
    { LLRP_TYPE_GET_SUPPORTED_VERSION_RESPONSE,  "Get Supported Version Response"  },
    { LLRP_TYPE_SET_PROTOCOL_VERSION_RESPONSE,   "Set Protocol Version Response"   },
    { LLRP_TYPE_GET_REPORT,                      "Get Report"                      },
    { LLRP_TYPE_RO_ACCESS_REPORT,                "RO Access Report"                },
    { LLRP_TYPE_KEEPALIVE,                       "Keepalive"                       },
    { LLRP_TYPE_READER_EVENT_NOTIFICATION,       "Reader Event Notification"       },
    { LLRP_TYPE_ENABLE_EVENTS_AND_REPORTS,       "Enable Events And Reports"       },
    { LLRP_TYPE_KEEPALIVE_ACK,                   "Keepalive Ack"                   },
    { LLRP_TYPE_ERROR_MESSAGE,                   "Error Message"                   },
    { LLRP_TYPE_CUSTOM_MESSAGE,                  "Custom Message"                  },
    { 0,                                          NULL                             }
};

/* Versions */
#define LLRP_VERS_1_0_1 0x01
#define LLRP_VERS_1_1   0x02

static const value_string llrp_versions[] = {
    { LLRP_VERS_1_0_1, "1.0.1" },
    { LLRP_VERS_1_1,   "1.1"   },
    { 0,                NULL   }
};

/* Capabilities */
#define LLRP_CAP_ALL            0
#define LLRP_CAP_GENERAL_DEVICE 1
#define LLRP_CAP_LLRP           2
#define LLRP_CAP_REGULATORY     3
#define LLRP_CAP_AIR_PROTOCOL   4

static const value_string capabilities_request[] = {
    { LLRP_CAP_ALL,            "All"                            },
    { LLRP_CAP_GENERAL_DEVICE, "General Device Capabilities"    },
    { LLRP_CAP_LLRP,           "LLRP Capabilites"               },
    { LLRP_CAP_REGULATORY,     "Regulatory Capabilities"        },
    { LLRP_CAP_AIR_PROTOCOL,   "Air Protocol LLRP Capabilities" },
    { 0,                        NULL                            }
};

/* Configurations */
#define LLRP_CONF_ALL                             0
#define LLRP_CONF_IDENTIFICATION                  1
#define LLRP_CONF_ANTENNA_PROPERTIES              2
#define LLRP_CONF_ANTENNA_CONFIGURATION           3
#define LLRP_CONF_RO_REPORT_SPEC                  4
#define LLRP_CONF_READER_EVENT_NOTIFICATION_SPEC  5
#define LLRP_CONF_ACCESS_REPORT_SPEC              6
#define LLRP_CONF_LLRP_CONFIGURATION_STATE        7
#define LLRP_CONF_KEEPALIVE_SPEC                  8
#define LLRP_CONF_GPI_PORT_CURRENT_STATE          9
#define LLRP_CONF_GPO_WRITE_DATA                 10
#define LLRP_CONF_EVENTS_AND_REPORTS             11

static const value_string config_request[] = {
    { LLRP_CONF_ALL,                            "All"                            },
    { LLRP_CONF_IDENTIFICATION,                 "Identification"                 },
    { LLRP_CONF_ANTENNA_PROPERTIES,             "Antenna Properties"             },
    { LLRP_CONF_ANTENNA_CONFIGURATION,          "Antenna Configuration"          },
    { LLRP_CONF_RO_REPORT_SPEC,                 "RO Report Spec"                 },
    { LLRP_CONF_READER_EVENT_NOTIFICATION_SPEC, "Reader Event Notification Spec" },
    { LLRP_CONF_ACCESS_REPORT_SPEC,             "Access Report Spec"             },
    { LLRP_CONF_LLRP_CONFIGURATION_STATE,       "LLRP Configuration State"       },
    { LLRP_CONF_KEEPALIVE_SPEC,                 "Keepalive Spec"                 },
    { LLRP_CONF_GPI_PORT_CURRENT_STATE,         "GPI Port Current State"         },
    { LLRP_CONF_GPO_WRITE_DATA,                 "GPO Write Data"                 },
    { LLRP_CONF_EVENTS_AND_REPORTS,             "Events and Reports"             },
    { 0,                                         NULL                            }
};
   
/* TLV Parameter Types */
#define LLRP_TLV_UTC_TIMESTAMP           128
#define LLRP_TLV_UPTIME                  129
#define LLRP_TLV_GENERAL_DEVICE_CAP      137
#define LLRP_TLV_RECEIVE_SENSE_ENTRY     139
#define LLRP_TLV_ANTENNA_AIR_PROTO       140
#define LLRP_TLV_GPIO_CAPABILITIES       141
#define LLRP_TLV_LLRP_CAPABILITIES       142
#define LLRP_TLV_REGU_CAPABILITIES       143
#define LLRP_TLV_UHF_CAPABILITIES        144
#define LLRP_TLV_XMIT_POWER_LEVEL_ENTRY  145
#define LLRP_TLV_FREQ_INFORMATION        146
#define LLRP_TLV_FREQ_HOP_TABLE          147
#define LLRP_TLV_FIXED_FREQ_TABLE        148
#define LLRP_TLV_ANTENNA_RCV_SENSE_RANGE 149
#define LLRP_TLV_RO_SPEC                 177
#define LLRP_TLV_RO_BOUND_SPEC           178
#define LLRP_TLV_RO_SPEC_START_TRIGGER   179
#define LLRP_TLV_PER_TRIGGER_VAL         180
#define LLRP_TLV_GPI_TRIGGER_VAL         181
#define LLRP_TLV_RO_SPEC_STOP_TRIGGER    182
#define LLRP_TLV_AI_SPEC                 183
#define LLRP_TLV_AI_SPEC_STOP            184
#define LLRP_TLV_TAG_OBSERV_TRIGGER      185
#define LLRP_TLV_INVENTORY_PARAM_SPEC    186
#define LLRP_TLV_RF_SURVEY_SPEC          187
#define LLRP_TLV_RF_SURVEY_SPEC_STOP_TR  188
#define LLRP_TLV_ACCESS_SPEC             207
#define LLRP_TLV_ACCESS_SPEC_STOP_TRIG   208
#define LLRP_TLV_ACCESS_COMMAND          209
#define LLRP_TLV_CLIENT_REQ_OP_SPEC      210
#define LLRP_TLV_CLIENT_REQ_RESPONSE     211
#define LLRP_TLV_LLRP_CONF_STATE_VAL     217
#define LLRP_TLV_IDENT                   218
#define LLRP_TLV_GPO_WRITE_DATA          219
#define LLRP_TLV_KEEPALIVE_SPEC          220
#define LLRP_TLV_ANTENNA_PROPS           221
#define LLRP_TLV_ANTENNA_CONF            222
#define LLRP_TLV_RF_RECEIVER             223
#define LLRP_TLV_RF_TRANSMITTER          224
#define LLRP_TLV_GPI_PORT_CURRENT_STATE  225
#define LLRP_TLV_EVENTS_AND_REPORTS      226
#define LLRP_TLV_RO_REPORT_SPEC          237
#define LLRP_TLV_TAG_REPORT_CONTENT_SEL  238
#define LLRP_TLV_ACCESS_REPORT_SPEC      239
#define LLRP_TLV_TAG_REPORT_DATA         240
#define LLRP_TLV_EPC_DATA                241
#define LLRP_TLV_RF_SURVEY_REPORT_DATA   242
#define LLRP_TLV_FREQ_RSSI_LEVEL_ENTRY   243
#define LLRP_TLV_READER_EVENT_NOTI_SPEC  244
#define LLRP_TLV_EVENT_NOTIF_STATE       245
#define LLRP_TLV_READER_EVENT_NOTI_DATA  246
#define LLRP_TLV_HOPPING_EVENT           247
#define LLRP_TLV_GPI_EVENT               248
#define LLRP_TLV_RO_SPEC_EVENT           249
#define LLRP_TLV_REPORT_BUF_LEVEL_WARN   250
#define LLRP_TLV_REPORT_BUF_OVERFLOW_ERR 251
#define LLRP_TLV_READER_EXCEPTION_EVENT  252
#define LLRP_TLV_RF_SURVEY_EVENT         253
#define LLRP_TLV_AI_SPEC_EVENT           254
#define LLRP_TLV_ANTENNA_EVENT           255
#define LLRP_TLV_CONN_ATTEMPT_EVENT      256
#define LLRP_TLV_CONN_CLOSE_EVENT        257
#define LLRP_TLV_LLRP_STATUS             287
#define LLRP_TLV_FIELD_ERROR             288
#define LLRP_TLV_PARAM_ERROR             289
#define LLRP_TLV_C1G2_LLRP_CAP           327
#define LLRP_TLV_C1G2_UHF_RF_MD_TBL      328
#define LLRP_TLV_C1G2_UHF_RF_MD_TBL_ENT  329
#define LLRP_TLV_C1G2_INVENTORY_COMMAND  330
#define LLRP_TLV_C1G2_FILTER             331
#define LLRP_TLV_C1G2_TAG_INV_MASK       332
#define LLRP_TLV_C1G2_TAG_INV_AWARE_FLTR 333
#define LLRP_TLV_C1G2_TAG_INV_UNAWR_FLTR 334
#define LLRP_TLV_C1G2_RF_CONTROL         335
#define LLRP_TLV_C1G2_SINGULATION_CTRL   336
#define LLRP_TLV_C1G2_TAG_INV_AWARE_SING 337
#define LLRP_TLV_C1G2_TAG_SPEC           338
#define LLRP_TLV_C1G2_TARGET_TAG         339
#define LLRP_TLV_C1G2_READ               341
#define LLRP_TLV_C1G2_WRITE              342
#define LLRP_TLV_C1G2_KILL               343
#define LLRP_TLV_C1G2_LOCK               344
#define LLRP_TLV_C1G2_LOCK_PAYLOAD       345
#define LLRP_TLV_C1G2_BLK_ERASE          346
#define LLRP_TLV_C1G2_BLK_WRITE          347
#define LLRP_TLV_C1G2_EPC_MEMORY_SLCTOR  348
#define LLRP_TLV_C1G2_READ_OP_SPEC_RES   349
#define LLRP_TLV_C1G2_WRT_OP_SPEC_RES    350
#define LLRP_TLV_C1G2_KILL_OP_SPEC_RES   351
#define LLRP_TLV_C1G2_LOCK_OP_SPEC_RES   352
#define LLRP_TLV_C1G2_BLK_ERS_OP_SPC_RES 353
#define LLRP_TLV_C1G2_BLK_WRT_OP_SPC_RES 354
#define LLRP_TLV_LOOP_SPEC               355
#define LLRP_TLV_MAX_RECEIVE_SENSE       363
#define LLRP_TLV_RF_SURVEY_FREQ_CAP      365

static const value_string tlv_type[] = {
    { LLRP_TLV_UTC_TIMESTAMP,           "UTC Timestamp"                                  },
    { LLRP_TLV_UPTIME,                  "Uptime"                                         },
    { LLRP_TLV_GENERAL_DEVICE_CAP,      "General Device Capabilities"                    },
    { LLRP_TLV_RECEIVE_SENSE_ENTRY,     "Receive Sensitivity Entry"                      },
    { LLRP_TLV_ANTENNA_AIR_PROTO,       "Antenna Air Protocol"                           },
    { LLRP_TLV_GPIO_CAPABILITIES,       "GPIO Capabilities"                              },
    { LLRP_TLV_LLRP_CAPABILITIES,       "LLRP Capabilities"                              },
    { LLRP_TLV_REGU_CAPABILITIES,       "REGU Capabilities"                              },
    { LLRP_TLV_UHF_CAPABILITIES,        "UHF Capabilities"                               },
    { LLRP_TLV_XMIT_POWER_LEVEL_ENTRY,  "Transmit Power Level Entry"                     },
    { LLRP_TLV_FREQ_INFORMATION,        "Frequency Information"                          },
    { LLRP_TLV_FREQ_HOP_TABLE,          "Frequenct Hop Table"                            },
    { LLRP_TLV_FIXED_FREQ_TABLE,        "Fixed Frequency Table"                          },
    { LLRP_TLV_ANTENNA_RCV_SENSE_RANGE, "Antenna RCV Sensitivity Range"                  },
    { LLRP_TLV_RO_SPEC,                 "RO Spec"                                        },
    { LLRP_TLV_RO_BOUND_SPEC,           "RO Bound Spec"                                  },
    { LLRP_TLV_RO_SPEC_START_TRIGGER,   "RO Spec Start Trigger"                          },
    { LLRP_TLV_PER_TRIGGER_VAL,         "PER Trigger Value"                              },
    { LLRP_TLV_GPI_TRIGGER_VAL,         "GPI Trigger Value"                              },
    { LLRP_TLV_RO_SPEC_STOP_TRIGGER,    "RO Spec Stop Trigger"                           },
    { LLRP_TLV_AI_SPEC,                 "AI Spec"                                        },
    { LLRP_TLV_AI_SPEC_STOP,            "AI Spec Stop"                                   },
    { LLRP_TLV_TAG_OBSERV_TRIGGER,      "Tag Observation Trigger"                        },
    { LLRP_TLV_INVENTORY_PARAM_SPEC,    "Inventory Parameter Spec ID"                    },
    { LLRP_TLV_RF_SURVEY_SPEC,          "RF Survey Spec"                                 },
    { LLRP_TLV_RF_SURVEY_SPEC_STOP_TR,  "RF Survey Spec Stop Trigger"                    },
    { LLRP_TLV_ACCESS_SPEC,             "Access Spec"                                    },
    { LLRP_TLV_ACCESS_SPEC_STOP_TRIG,   "Access Spec Stop Trigger"                       },
    { LLRP_TLV_ACCESS_COMMAND,          "Access Command"                                 },
    { LLRP_TLV_CLIENT_REQ_OP_SPEC,      "Client Request Op Spec"                         },
    { LLRP_TLV_CLIENT_REQ_RESPONSE,     "Client Request Response"                        },
    { LLRP_TLV_LLRP_CONF_STATE_VAL,     "LLRP Configuration State Value"                 },
    { LLRP_TLV_IDENT,                   "Identification"                                 },
    { LLRP_TLV_GPO_WRITE_DATA,          "GPO Write Data"                                 },
    { LLRP_TLV_KEEPALIVE_SPEC,          "Keepalive Spec"                                 },
    { LLRP_TLV_ANTENNA_PROPS,           "Antenna Properties"                             },
    { LLRP_TLV_ANTENNA_CONF,            "Antenna Configuration"                          },
    { LLRP_TLV_RF_RECEIVER,             "RF Receiver"                                    },
    { LLRP_TLV_RF_TRANSMITTER,          "RF Transmitter"                                 },
    { LLRP_TLV_GPI_PORT_CURRENT_STATE,  "GPI Port Current State"                         },
    { LLRP_TLV_EVENTS_AND_REPORTS,      "Events And Reports"                             },
    { LLRP_TLV_RO_REPORT_SPEC,          "RO Report Spec"                                 },
    { LLRP_TLV_TAG_REPORT_CONTENT_SEL,  "Tag Report Content Selector"                    },
    { LLRP_TLV_ACCESS_REPORT_SPEC,      "Access Report Spec"                             },
    { LLRP_TLV_TAG_REPORT_DATA,         "Tag Report Data"                                },
    { LLRP_TLV_EPC_DATA,                "EPC Data"                                       },
    { LLRP_TLV_RF_SURVEY_REPORT_DATA,   "RF Survey Report Data"                          },
    { LLRP_TLV_FREQ_RSSI_LEVEL_ENTRY,   "Frequency RSSI Level Entry"                     },
    { LLRP_TLV_READER_EVENT_NOTI_SPEC,  "Reader Event Notification Spec"                 },
    { LLRP_TLV_EVENT_NOTIF_STATE,       "Event Notification State"                       },
    { LLRP_TLV_READER_EVENT_NOTI_DATA,  "Reader Event Notification Data"                 },
    { LLRP_TLV_HOPPING_EVENT,           "Hopping Event"                                  },
    { LLRP_TLV_GPI_EVENT,               "GPI Event"                                      },
    { LLRP_TLV_RO_SPEC_EVENT,           "RO Spec Event"                                  },
    { LLRP_TLV_REPORT_BUF_LEVEL_WARN,   "Report Buffer Level Warning Event"              },
    { LLRP_TLV_REPORT_BUF_OVERFLOW_ERR, "Report Buffer Overflow Error Event"             },
    { LLRP_TLV_READER_EXCEPTION_EVENT,  "Reader Exception Event"                         },
    { LLRP_TLV_RF_SURVEY_EVENT,         "RF Survey Event"                                },
    { LLRP_TLV_AI_SPEC_EVENT,           "AI Spec Event"                                  },
    { LLRP_TLV_ANTENNA_EVENT,           "ANTENNA Event"                                  },
    { LLRP_TLV_CONN_ATTEMPT_EVENT,      "CONN Attempt Event"                             },
    { LLRP_TLV_CONN_CLOSE_EVENT,        "CONN Close Event"                               },
    { LLRP_TLV_LLRP_STATUS,             "LLRP Status"                                    },
    { LLRP_TLV_FIELD_ERROR,             "Field Error"                                    },
    { LLRP_TLV_PARAM_ERROR,             "Param Error"                                    },
    { LLRP_TLV_C1G2_LLRP_CAP,           "C1G2 LLRP Capabilities"                         },
    { LLRP_TLV_C1G2_UHF_RF_MD_TBL,      "C1G2 UHF RF Mode Table"                         },
    { LLRP_TLV_C1G2_UHF_RF_MD_TBL_ENT,  "C1G2 UHF RF Mode Table Entry"                   },
    { LLRP_TLV_C1G2_INVENTORY_COMMAND,  "C1G2 Inventory Command"                         },
    { LLRP_TLV_C1G2_FILTER,             "C1G2 Filter"                                    },
    { LLRP_TLV_C1G2_TAG_INV_MASK,       "C1G2 Tag Inventory Mask"                        },
    { LLRP_TLV_C1G2_TAG_INV_AWARE_FLTR, "C1G2 Tag Inventory State-Aware Filtre Action"   },
    { LLRP_TLV_C1G2_TAG_INV_UNAWR_FLTR, "C1G2 Tag Inventory State-Unaware Filter Action" },
    { LLRP_TLV_C1G2_RF_CONTROL,         "C1G2 RF Control"                                },
    { LLRP_TLV_C1G2_SINGULATION_CTRL,   "C1G2 Singulation Control"                       },
    { LLRP_TLV_C1G2_TAG_INV_AWARE_SING, "C1G2 Tag Inventory State-Aware Singulation"     },
    { LLRP_TLV_C1G2_TAG_SPEC,           "C1G2 Tag Spec"                                  },
    { LLRP_TLV_C1G2_TARGET_TAG,         "C1G2 Target Tag"                                },
    { LLRP_TLV_C1G2_READ,               "C1G2 Read"                                      },
    { LLRP_TLV_C1G2_WRITE,              "C1G2 Write"                                     },
    { LLRP_TLV_C1G2_KILL,               "C1G2 Kill"                                      },
    { LLRP_TLV_C1G2_LOCK,               "C1G2 Lock"                                      },
    { LLRP_TLV_C1G2_LOCK_PAYLOAD,       "C1G2 Lock Payload"                              },
    { LLRP_TLV_C1G2_BLK_ERASE,          "C1G2 Block Erase"                               },
    { LLRP_TLV_C1G2_BLK_WRITE,          "C1G2 Block Write"                               },
    { LLRP_TLV_C1G2_EPC_MEMORY_SLCTOR,  "C1G2 EPC Memory Selector"                       },
    { LLRP_TLV_C1G2_READ_OP_SPEC_RES,   "C1G2 Read Op Spec Result"                       },
    { LLRP_TLV_C1G2_WRT_OP_SPEC_RES,    "C1G2 Write Op Spec Result"                      },
    { LLRP_TLV_C1G2_KILL_OP_SPEC_RES,   "C1G2 Kill Op Spec Result"                       },
    { LLRP_TLV_C1G2_LOCK_OP_SPEC_RES,   "C1G2 Lock Op Spec Result"                       },
    { LLRP_TLV_C1G2_BLK_ERS_OP_SPC_RES, "C1G2 Block Erase Op Spec Result"                },
    { LLRP_TLV_C1G2_BLK_WRT_OP_SPC_RES, "C1G2 Block Write Op Spec Result"                },
    { LLRP_TLV_LOOP_SPEC,               "Loop Spec"                                      },
    { LLRP_TLV_MAX_RECEIVE_SENSE,       "Maximum Receive Sensitivity"                    },
    { LLRP_TLV_RF_SURVEY_FREQ_CAP,      "RF Survey Frequency Capabilities"               },
    { 0,                                 NULL                                            }
};

/* TV Parameter Types */
#define LLRP_TV_ANTENNA_ID               1
#define LLRP_TV_FIRST_SEEN_TIME_UTC      2
#define LLRP_TV_FIRST_SEEN_TIME_UPTIME   3
#define LLRP_TV_LAST_SEEN_TIME_UTC       4
#define LLRP_TV_LAST_SEEN_TIME_UPTIME    5
#define LLRP_TV_PEAK_RSSI                6
#define LLRP_TV_CHANNEL_INDEX            7
#define LLRP_TV_TAG_SEEN_COUNT           8
#define LLRP_TV_RO_SPEC_ID               9
#define LLRP_TV_INVENTORY_PARAM_SPEC_ID 10
#define LLRP_TV_C1G2_CRC                11
#define LLRP_TV_C1G2_PC                 12
#define LLRP_TV_EPC96                   13
#define LLRP_TV_SPEC_INDEX              14
#define LLRP_TV_CLIENT_REQ_OP_SPEC_RES  15
#define LLRP_TV_ACCESS_SPEC_ID          16
#define LLRP_TV_OP_SPEC_ID              17
#define LLRP_TV_C1G2_SINGULATION_DET    18
#define LLRP_TV_C1G2_XPC_W1             19
#define LLRP_TV_C1G2_XPC_W2             20

/* Since TV's don't have a length field,
 * use these values instead */
#define LLRP_TV_LEN_ANTENNA_ID               2
#define LLRP_TV_LEN_FIRST_SEEN_TIME_UTC      8
#define LLRP_TV_LEN_FIRST_SEEN_TIME_UPTIME   8
#define LLRP_TV_LEN_LAST_SEEN_TIME_UTC       8
#define LLRP_TV_LEN_LAST_SEEN_TIME_UPTIME    8
#define LLRP_TV_LEN_PEAK_RSSI                1
#define LLRP_TV_LEN_CHANNEL_INDEX            2
#define LLRP_TV_LEN_TAG_SEEN_COUNT           2
#define LLRP_TV_LEN_RO_SPEC_ID               4
#define LLRP_TV_LEN_INVENTORY_PARAM_SPEC_ID  2
#define LLRP_TV_LEN_C1G2_CRC                 2
#define LLRP_TV_LEN_C1G2_PC                  2
#define LLRP_TV_LEN_EPC96                    2
#define LLRP_TV_LEN_SPEC_INDEX               2
#define LLRP_TV_LEN_CLIENT_REQ_OP_SPEC_RES   2
#define LLRP_TV_LEN_ACCESS_SPEC_ID           4
#define LLRP_TV_LEN_OP_SPEC_ID               2
#define LLRP_TV_LEN_C1G2_SINGULATION_DET     4
#define LLRP_TV_LEN_C1G2_XPC_W1              2
#define LLRP_TV_LEN_C1G2_XPC_W2              2

static const value_string tv_type[] = {
    { LLRP_TV_ANTENNA_ID,              "Antenna ID"                    },
    { LLRP_TV_FIRST_SEEN_TIME_UTC,     "First Seen Timestamp UTC"      },
    { LLRP_TV_FIRST_SEEN_TIME_UPTIME,  "First Seen Timestamp Uptime"   },
    { LLRP_TV_LAST_SEEN_TIME_UTC,      "Last Seen Timestamp UTC"       },
    { LLRP_TV_LAST_SEEN_TIME_UPTIME,   "Last Seen Timestamp Uptime"    },
    { LLRP_TV_PEAK_RSSI,               "Peak RSSI"                     },
    { LLRP_TV_CHANNEL_INDEX,           "Channel Index"                 },
    { LLRP_TV_TAG_SEEN_COUNT,          "Tag Seen Count"                },
    { LLRP_TV_RO_SPEC_ID,              "RO Spec ID"                    },
    { LLRP_TV_INVENTORY_PARAM_SPEC_ID, "Inventory Parameter Spec ID"   },
    { LLRP_TV_C1G2_CRC,                "C1G2 CRC"                      },
    { LLRP_TV_C1G2_PC,                 "C1G2 PC"                       },
    { LLRP_TV_EPC96,                   "EPC-96"                        },
    { LLRP_TV_SPEC_INDEX,              "Spec Index"                    },
    { LLRP_TV_CLIENT_REQ_OP_SPEC_RES,  "Client Request Op Spec Result" },
    { LLRP_TV_ACCESS_SPEC_ID,          "Access Spec ID"                },
    { LLRP_TV_OP_SPEC_ID,              "Op Spec ID"                    },
    { LLRP_TV_C1G2_SINGULATION_DET,    "C1G2 Singulation Details"      },
    { LLRP_TV_C1G2_XPC_W1,             "C1G2 XPC W1"                   },
    { LLRP_TV_C1G2_XPC_W2,             "C1G2 XPC W2"                   },
    { 0,                                NULL                           }
};

/* Misc */
#define LLRP_ROSPEC_ALL      0
#define LLRP_ANTENNA_ALL     0
#define LLRP_GPI_PORT_ALL    0
#define LLRP_GPO_PORT_ALL    0
#define LLRP_ACCESSSPEC_ALL  0
#define LLRP_TLV_LEN_MIN     4
#define LLRP_LEN_MIN        10

static void
dissect_llrp_parameters(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        guint offset)
{
    guint8 has_length;
    guint16 len, type;
    guint real_len;
    proto_item *ti;
    proto_tree *param_tree;

    while (tvb_reported_length_remaining(tvb, offset) > 0)
    {
        has_length = !(tvb_get_guint8(tvb, offset) & 0x80);

        if (has_length)
        {
            len = tvb_get_ntohs(tvb, offset + 2);

            if (len < LLRP_TLV_LEN_MIN)
                real_len = LLRP_TLV_LEN_MIN;
            else if (len > tvb_reported_length_remaining(tvb, offset))
                real_len = tvb_reported_length_remaining(tvb, offset);
            else
                real_len = len;

            ti = proto_tree_add_none_format(tree, hf_llrp_param, tvb,
                    offset, real_len, "TLV Parameter");
            param_tree = proto_item_add_subtree(ti, ett_llrp_param);

            proto_tree_add_item(param_tree, hf_llrp_tlv_type, tvb,
                    offset, 2, ENC_BIG_ENDIAN);
            offset += 2;

            ti = proto_tree_add_item(param_tree, hf_llrp_tlv_len, tvb,
                    offset, 2, ENC_BIG_ENDIAN);
            if (len != real_len)
                expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR,
                        "Invalid length field: claimed %u, should be %u.",
                        len, real_len);
            offset += 2;

            /* TODO: Decode actual TLV parameter fields */

            /* The len field includes the 4-byte parameter header that we've
             * already accounted for in offset */
            offset += real_len - 4;
        }
        else
        {
            type = tvb_get_guint8(tvb, offset) & 0x7F;

            /* TODO: Decode actual TV fields rather than just jumping
             * ahead the correct length */
            switch (type)
            {
                case LLRP_TV_ANTENNA_ID:
                    real_len = LLRP_TV_LEN_ANTENNA_ID; break;
                case LLRP_TV_FIRST_SEEN_TIME_UTC:
                    real_len = LLRP_TV_LEN_FIRST_SEEN_TIME_UTC; break;
                case LLRP_TV_FIRST_SEEN_TIME_UPTIME:
                    real_len = LLRP_TV_LEN_FIRST_SEEN_TIME_UPTIME; break;
                case LLRP_TV_LAST_SEEN_TIME_UTC:
                    real_len = LLRP_TV_LEN_LAST_SEEN_TIME_UTC; break;
                case LLRP_TV_LAST_SEEN_TIME_UPTIME:
                    real_len = LLRP_TV_LEN_LAST_SEEN_TIME_UPTIME; break;
                case LLRP_TV_PEAK_RSSI:
                    real_len = LLRP_TV_LEN_PEAK_RSSI; break;
                case LLRP_TV_CHANNEL_INDEX:
                    real_len = LLRP_TV_LEN_CHANNEL_INDEX; break;
                case LLRP_TV_TAG_SEEN_COUNT:
                    real_len = LLRP_TV_LEN_TAG_SEEN_COUNT; break;
                case LLRP_TV_RO_SPEC_ID:
                    real_len = LLRP_TV_LEN_RO_SPEC_ID; break;
                case LLRP_TV_INVENTORY_PARAM_SPEC_ID:
                    real_len = LLRP_TV_LEN_INVENTORY_PARAM_SPEC_ID; break;
                case LLRP_TV_C1G2_CRC:
                    real_len = LLRP_TV_LEN_C1G2_CRC; break;
                case LLRP_TV_C1G2_PC:
                    real_len = LLRP_TV_LEN_C1G2_PC; break;
                case LLRP_TV_EPC96:
                    real_len = LLRP_TV_LEN_EPC96; break;
                case LLRP_TV_SPEC_INDEX:
                    real_len = LLRP_TV_LEN_SPEC_INDEX; break;
                case LLRP_TV_CLIENT_REQ_OP_SPEC_RES:
                    real_len = LLRP_TV_LEN_CLIENT_REQ_OP_SPEC_RES; break;
                case LLRP_TV_ACCESS_SPEC_ID:
                    real_len = LLRP_TV_LEN_ACCESS_SPEC_ID; break;
                case LLRP_TV_OP_SPEC_ID:
                    real_len = LLRP_TV_LEN_OP_SPEC_ID; break;
                case LLRP_TV_C1G2_SINGULATION_DET:
                    real_len = LLRP_TV_LEN_C1G2_SINGULATION_DET; break;
                case LLRP_TV_C1G2_XPC_W1:
                    real_len = LLRP_TV_LEN_C1G2_XPC_W1; break;
                case LLRP_TV_C1G2_XPC_W2:
                    real_len = LLRP_TV_LEN_C1G2_XPC_W2; break;
                default:
                    /* ???
                     * No need to mark it, since the hf_llrp_tv_type field
                     * will already show up as 'unknown'. */
                    real_len = 0;
                    break;
            };

            ti = proto_tree_add_none_format(tree, hf_llrp_param, tvb,
                    offset, real_len + 1, "TV Parameter");
            param_tree = proto_item_add_subtree(ti, ett_llrp_param);

            proto_tree_add_item(param_tree, hf_llrp_tv_type, tvb,
                    offset, 1, ENC_NA);
            offset++;

            /* Unlike for TLV's, real_len for TV's doesn't include the standard
             * header length, so just add it straight to the offset. */
            offset += real_len;
        }
    }
}

static void
dissect_llrp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        guint16 type, guint offset)
{
    guint8  requested_data;
    guint16 antenna_id, gpi_port, gpo_port;
    guint32 spec_id;
    proto_item *ti;

    switch (type)
    {
        /* Simple cases just have normal TLV or TV parameters */
        case LLRP_TYPE_CLOSE_CONNECTION_RESPONSE:
        case LLRP_TYPE_GET_READER_CAPABILITES_RESPONSE:
        case LLRP_TYPE_ADD_ROSPEC:
        case LLRP_TYPE_ADD_ROSPEC_RESPONSE:
        case LLRP_TYPE_DELETE_ROSPEC_RESPONSE:
        case LLRP_TYPE_START_ROSPEC_RESPONSE:
        case LLRP_TYPE_STOP_ROSPEC_RESPONSE:
        case LLRP_TYPE_ENABLE_ROSPEC_RESPONSE:
        case LLRP_TYPE_DISABLE_ROSPEC_RESPONSE:
        case LLRP_TYPE_GET_ROSPECS_RESPONSE:
        case LLRP_TYPE_ADD_ACCESSSPEC:
        case LLRP_TYPE_ADD_ACCESSSPEC_RESPONSE:
        case LLRP_TYPE_DELETE_ACCESSSPEC_RESPONSE:
        case LLRP_TYPE_ENABLE_ACCESSSPEC_RESPONSE:
        case LLRP_TYPE_DISABLE_ACCESSSPEC_RESPONSE:
        case LLRP_TYPE_GET_ACCESSSPECS:
        case LLRP_TYPE_CLIENT_REQUEST_OP:
        case LLRP_TYPE_CLIENT_RESQUEST_OP_RESPONSE:
        case LLRP_TYPE_RO_ACCESS_REPORT:
        case LLRP_TYPE_READER_EVENT_NOTIFICATION:
        case LLRP_TYPE_ERROR_MESSAGE:
        case LLRP_TYPE_GET_READER_CONFIG_RESPONSE:
        case LLRP_TYPE_SET_READER_CONFIG_RESPONSE:
        case LLRP_TYPE_SET_PROTOCOL_VERSION_RESPONSE:
        case LLRP_TYPE_GET_ACCESSSPECS_RESPONSE:
        case LLRP_TYPE_GET_REPORT:
        case LLRP_TYPE_ENABLE_EVENTS_AND_REPORTS:
            dissect_llrp_parameters(tvb, pinfo, tree, offset);
            break;
        /* Some just have an ROSpec ID */
        case LLRP_TYPE_START_ROSPEC:
        case LLRP_TYPE_STOP_ROSPEC:
        case LLRP_TYPE_ENABLE_ROSPEC:
        case LLRP_TYPE_DISABLE_ROSPEC:
        case LLRP_TYPE_DELETE_ROSPEC:
            spec_id = tvb_get_ntohl(tvb, offset);
            if (spec_id == LLRP_ROSPEC_ALL)
                proto_tree_add_uint_format(tree, hf_llrp_rospec, tvb,
                        offset, 4, spec_id, "All ROSpecs (%u)", spec_id);
            else
                proto_tree_add_item(tree, hf_llrp_rospec, tvb,
                        offset, 4, ENC_BIG_ENDIAN);
            break;
        /* Some just have an AccessSpec ID */
        case LLRP_TYPE_ENABLE_ACCESSSPEC:
        case LLRP_TYPE_DELETE_ACCESSSPEC:
        case LLRP_TYPE_DISABLE_ACCESSSPEC:
            spec_id = tvb_get_ntohl(tvb, offset);
            if (spec_id == LLRP_ACCESSSPEC_ALL)
                proto_tree_add_uint_format(tree, hf_llrp_accessspec, tvb,
                        offset, 4, spec_id, "All Access Specs (%u)", spec_id);
            else
                proto_tree_add_item(tree, hf_llrp_accessspec, tvb,
                        offset, 4, ENC_BIG_ENDIAN);
            proto_tree_add_item(tree, hf_llrp_accessspec, tvb, offset, 4, ENC_BIG_ENDIAN);
            break;
        case LLRP_TYPE_GET_READER_CAPABILITES:
            proto_tree_add_item(tree, hf_llrp_req_cap, tvb, offset, 1, ENC_NA);
            offset++;
            dissect_llrp_parameters(tvb, pinfo, tree, offset);
            break;
        /* GET_READER_CONFIG is complicated */
        case LLRP_TYPE_GET_READER_CONFIG:
            requested_data = tvb_get_guint8(tvb, offset + 2);
            switch (requested_data)
            {
                case LLRP_CONF_ALL:
                    antenna_id = tvb_get_ntohs(tvb, offset);
                    if (antenna_id == LLRP_ANTENNA_ALL)
                        proto_tree_add_uint_format(tree, hf_llrp_antenna_id, tvb,
                                offset, 2, antenna_id, "All Antennas (%u)", antenna_id);
                    else
                        proto_tree_add_item(tree, hf_llrp_antenna_id, tvb,
                                offset, 2, ENC_BIG_ENDIAN);
                    offset += 2;
                    proto_tree_add_item(tree, hf_llrp_req_conf, tvb,
                            offset, 1, ENC_NA);
                    offset++;
                    gpi_port = tvb_get_ntohs(tvb, offset);
                    if (gpi_port == LLRP_GPI_PORT_ALL)
                        proto_tree_add_uint_format(tree, hf_llrp_gpi_port, tvb,
                                offset, 2, gpi_port, "All GPI Ports (%u)", gpi_port);
                    else
                        proto_tree_add_item(tree, hf_llrp_gpi_port, tvb,
                                offset, 2, ENC_BIG_ENDIAN);
                    offset += 2;
                    gpo_port = tvb_get_ntohs(tvb, offset);
                    if (gpo_port == LLRP_GPO_PORT_ALL)
                        proto_tree_add_uint_format(tree, hf_llrp_gpo_port, tvb,
                                offset, 2, gpo_port, "All GPO Ports (%u)", gpo_port);
                    else
                        proto_tree_add_item(tree, hf_llrp_gpo_port, tvb,
                                offset, 2, ENC_BIG_ENDIAN);
                    offset += 2;
                    break;
                case LLRP_CONF_ANTENNA_PROPERTIES:
                case LLRP_CONF_ANTENNA_CONFIGURATION:
                    antenna_id = tvb_get_ntohs(tvb, offset);
                    if (antenna_id == LLRP_ANTENNA_ALL)
                        proto_tree_add_uint_format(tree, hf_llrp_antenna_id, tvb,
                                offset, 2, antenna_id, "All Antennas (%u)", antenna_id);
                    else
                        proto_tree_add_item(tree, hf_llrp_antenna_id, tvb,
                                offset, 2, ENC_BIG_ENDIAN);
                    offset += 2;
                    proto_tree_add_item(tree, hf_llrp_req_conf, tvb,
                            offset, 1, ENC_NA);
                    offset++;
                    offset += 4; /* Skip both GPI and GPO ports */
                    break;
                case LLRP_CONF_IDENTIFICATION:
                case LLRP_CONF_RO_REPORT_SPEC:
                case LLRP_CONF_READER_EVENT_NOTIFICATION_SPEC:
                case LLRP_CONF_ACCESS_REPORT_SPEC:
                case LLRP_CONF_LLRP_CONFIGURATION_STATE:
                case LLRP_CONF_KEEPALIVE_SPEC:
                case LLRP_CONF_EVENTS_AND_REPORTS:
                    offset += 2; /* Skip antenna ID */
                    proto_tree_add_item(tree, hf_llrp_req_conf, tvb,
                            offset, 1, ENC_NA);
                    offset++;
                    offset += 4; /* Skip both GPI and GPO ports */
                    break;
                case LLRP_CONF_GPI_PORT_CURRENT_STATE:
                    offset += 2; /* Skip antenna ID */
                    proto_tree_add_item(tree, hf_llrp_req_conf, tvb,
                            offset, 1, ENC_NA);
                    offset++;
                    gpi_port = tvb_get_ntohs(tvb, offset);
                    if (gpi_port == LLRP_GPI_PORT_ALL)
                        proto_tree_add_uint_format(tree, hf_llrp_gpi_port, tvb,
                                offset, 2, gpi_port, "All GPI Ports (%u)", gpi_port);
                    else
                        proto_tree_add_item(tree, hf_llrp_gpi_port, tvb,
                                offset, 2, ENC_BIG_ENDIAN);
                    offset += 2;
                    offset += 2; /* Skip GPO Port */
                    break;
                case LLRP_CONF_GPO_WRITE_DATA:
                    offset += 2; /* Skip antenna ID */
                    proto_tree_add_item(tree, hf_llrp_req_conf, tvb,
                            offset, 1, ENC_NA);
                    offset++;
                    offset += 2; /* Skip GPI Port */
                    gpo_port = tvb_get_ntohs(tvb, offset);
                    if (gpo_port == LLRP_GPO_PORT_ALL)
                        proto_tree_add_uint_format(tree, hf_llrp_gpo_port, tvb,
                                offset, 2, gpo_port, "All GPO Ports (%u)", gpo_port);
                    else
                        proto_tree_add_item(tree, hf_llrp_gpo_port, tvb,
                                offset, 2, ENC_BIG_ENDIAN);
                    offset += 2;
                    break;
                default:
                    offset += 2; /* Skip antenna ID */
                    ti = proto_tree_add_item(tree, hf_llrp_req_conf, tvb,
                            offset, 1, ENC_NA);
                    expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR,
                            "Unrecognized configuration request: %u",
                            requested_data);
                    offset++;
                    offset += 4; /* Skip both GPI and GPO ports */
                    break;
            };
            dissect_llrp_parameters(tvb, pinfo, tree, offset);
            break;
        /* END GET_READER_CONFIG */
        /* Misc */
        case LLRP_TYPE_SET_READER_CONFIG:
            proto_tree_add_item(tree, hf_llrp_rest_fact, tvb, offset, 1, ENC_NA);
            offset++;
            dissect_llrp_parameters(tvb, pinfo, tree, offset);
            break;
        case LLRP_TYPE_SET_PROTOCOL_VERSION:
            proto_tree_add_item(tree, hf_llrp_version, tvb, offset, 1, ENC_NA);
            break;
        case LLRP_TYPE_GET_SUPPORTED_VERSION_RESPONSE:
            proto_tree_add_item(tree, hf_llrp_cur_ver, tvb, offset, 1, ENC_NA);
            offset++;
            proto_tree_add_item(tree, hf_llrp_sup_ver, tvb, offset, 1, ENC_NA);
            offset++;
            dissect_llrp_parameters(tvb, pinfo, tree, offset);
            break;
        case LLRP_TYPE_CUSTOM_MESSAGE:
            proto_tree_add_item(tree, hf_llrp_vendor, tvb, offset, 4, ENC_BIG_ENDIAN);
            break;
        /* Some have no extra data expected */
        case LLRP_TYPE_KEEPALIVE:
        case LLRP_TYPE_KEEPALIVE_ACK:
        case LLRP_TYPE_CLOSE_CONNECTION:
        case LLRP_TYPE_GET_ROSPECS:
        case LLRP_TYPE_GET_SUPPORTED_VERSION:
            break;
        default:
            /* We shouldn't be called if we don't already recognize the value */
            DISSECTOR_ASSERT_NOT_REACHED();
    };
}

/* Code to actually dissect the packets */
static int
dissect_llrp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_item *ti;
    proto_tree *llrp_tree;
    guint16 type;
    guint32 len;
    guint offset = 0;

    /* Check that there's enough data */
    if (tvb_reported_length(tvb) < LLRP_LEN_MIN)
        return 0;

    /* Make entries in Protocol column and Info column on summary display */
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLRP");

    col_set_str(pinfo->cinfo, COL_INFO, "LLRP Message");

    type = tvb_get_ntohs(tvb, offset) & 0x03FF;

    col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)",
                    val_to_str(type, message_types, "Unknown Type: %d"));

    ti = proto_tree_add_item(tree, proto_llrp, tvb, offset, -1, ENC_NA);
    llrp_tree = proto_item_add_subtree(ti, ett_llrp);

    proto_tree_add_item(llrp_tree, hf_llrp_version, tvb, offset, 1, ENC_NA);
    proto_tree_add_item(llrp_tree, hf_llrp_type, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;

    ti = proto_tree_add_item(llrp_tree, hf_llrp_length, tvb, offset, 4, ENC_BIG_ENDIAN);
    len = tvb_get_ntohl(tvb, offset);
    if (len > tvb_reported_length(tvb))
    {
        expert_add_info_format(pinfo, ti, PI_MALFORMED, PI_ERROR,
                               "Incorrect length field: claimed %u, but only have %u.",
                               len, tvb_reported_length(tvb));
    }
    offset += 4;

    proto_tree_add_item(llrp_tree, hf_llrp_id, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;

    if (match_strval(type, message_types))
        dissect_llrp_message(tvb, pinfo, llrp_tree, type, offset);

    return tvb_length(tvb);
}

void
proto_register_llrp(void)
{
    /* Setup list of header fields  See Section 1.6.1 for details*/
    static hf_register_info hf[] = {
        { &hf_llrp_version,
        { "Version", "llrp.version", FT_UINT8, BASE_DEC, VALS(llrp_versions), 0x1C,
          NULL, HFILL }},

        { &hf_llrp_type,
        { "Type", "llrp.type", FT_UINT16, BASE_DEC, VALS(message_types), 0x03FF,
          NULL, HFILL }},

        { &hf_llrp_length,
        { "Length", "llrp.length", FT_UINT32, BASE_DEC, NULL, 0,
          NULL, HFILL }},

        { &hf_llrp_id,
        { "ID", "llrp.id", FT_UINT32, BASE_DEC, NULL, 0,
          NULL, HFILL }},

        { &hf_llrp_cur_ver,
        { "Current Version", "llrp.cur_ver", FT_UINT8, BASE_DEC, VALS(llrp_versions), 0,
          NULL, HFILL }},

        { &hf_llrp_sup_ver,
        { "Supported Version", "llrp.sup_ver", FT_UINT8, BASE_DEC, VALS(llrp_versions), 0,
          "The max supported protocol version.", HFILL }},

        { &hf_llrp_req_cap,
        { "Requested Capabilities", "llrp.req_cap", FT_UINT8, BASE_DEC, VALS(capabilities_request), 0,
          NULL, HFILL }},

        { &hf_llrp_req_conf,
        { "Requested Configuration", "llrp.req_conf", FT_UINT8, BASE_DEC, VALS(config_request), 0,
          NULL, HFILL }},

        { &hf_llrp_rospec,
        { "ROSpec ID", "llrp.rospec", FT_UINT32, BASE_DEC, NULL, 0,
          NULL, HFILL }},

        { &hf_llrp_antenna_id,
        { "Antenna ID", "llrp.antenna_id", FT_UINT16, BASE_DEC, NULL, 0,
          NULL, HFILL }},

        { &hf_llrp_gpi_port,
        { "GPI Port Number", "llrp.gpi_port", FT_UINT16, BASE_DEC, NULL, 0,
          NULL, HFILL }},

        { &hf_llrp_gpo_port,
        { "GPO Port Number", "llrp.gpo_port", FT_UINT16, BASE_DEC, NULL, 0,
          NULL, HFILL }},

        { &hf_llrp_rest_fact,
        { "Restore Factory Settings", "llrp.rest_fact", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x80,
          NULL, HFILL }},

        { &hf_llrp_accessspec,
        { "Access Spec ID", "llrp.accessspec", FT_UINT32, BASE_DEC, NULL, 0,
          NULL, HFILL }},

        { &hf_llrp_vendor,
        { "Vendor ID", "llrp.vendor", FT_UINT32, BASE_DEC, NULL, 0,
          NULL, HFILL }},

        { &hf_llrp_tlv_type,
        { "Type", "llrp.tlv_type", FT_UINT16, BASE_DEC, VALS(tlv_type), 0x03FF,
          "The type of TLV.", HFILL }},

        { &hf_llrp_tv_type,
        { "Type", "llrp.tv_type", FT_UINT8, BASE_DEC, VALS(tv_type), 0x7F,
          "The type of TV.", HFILL }},

        { &hf_llrp_tlv_len,
        { "Length", "llrp.tlv_len", FT_UINT16, BASE_DEC, NULL, 0,
          "The length of this TLV.", HFILL }},

        { &hf_llrp_param,
        { "Parameter", "llrp.param", FT_NONE, BASE_NONE, NULL, 0,
          NULL, HFILL }}
    };

    /* Setup protocol subtree array */
    static gint *ett[] = {
        &ett_llrp,
        &ett_llrp_param
    };

    /* Register the protocol name and description */
    proto_llrp = proto_register_protocol("Low Level Reader Protocol",
            "LLRP", "llrp");

    /* Required function calls to register the header fields and subtrees used */
    proto_register_field_array(proto_llrp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_llrp(void)
{
    dissector_handle_t llrp_handle;

    llrp_handle = new_create_dissector_handle(dissect_llrp, proto_llrp);
    dissector_add_uint("tcp.port", LLRP_PORT, llrp_handle);
}


/*
 * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=4 expandtab:
 * :indentSize=4:tabSize=4:noTabs=true:
 */