Revert "Fixup: tvb_* -> tvb_captured"
[metze/wireshark/wip.git] / epan / dissectors / packet-bthsp.c
1 /* packet-bthsp.c
2  * Routines for Bluetooth Headset Profile (HSP)
3  *
4  * Copyright 2013, Michal Labedzki for Tieto Corporation
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <ctype.h>
28
29 #include <epan/packet.h>
30 #include <epan/prefs.h>
31 #include <epan/expert.h>
32 #include <epan/wmem/wmem.h>
33
34 #include "packet-btrfcomm.h"
35 #include "packet-btsdp.h"
36
37 static int proto_bthsp = -1;
38
39 static int hf_command                                                      = -1;
40 static int hf_parameters                                                   = -1;
41 static int hf_command_in                                                   = -1;
42 static int hf_unsolicited                                                  = -1;
43 static int hf_role                                                         = -1;
44 static int hf_at_cmd                                                       = -1;
45 static int hf_at_cmd_type                                                  = -1;
46 static int hf_at_command_line_prefix                                       = -1;
47 static int hf_at_ignored                                                   = -1;
48 static int hf_parameter                                                    = -1;
49 static int hf_unknown_parameter                                            = -1;
50 static int hf_data                                                         = -1;
51 static int hf_fragment                                                     = -1;
52 static int hf_fragmented                                                   = -1;
53 static int hf_vgs                                                          = -1;
54 static int hf_vgm                                                          = -1;
55 static int hf_ckpd                                                         = -1;
56
57 static expert_field ei_non_mandatory_command                          = EI_INIT;
58 static expert_field ei_invalid_usage                                  = EI_INIT;
59 static expert_field ei_unknown_parameter                              = EI_INIT;
60 static expert_field ei_vgm_gain                                       = EI_INIT;
61 static expert_field ei_vgs_gain                                       = EI_INIT;
62 static expert_field ei_ckpd                                           = EI_INIT;
63
64 static gint ett_bthsp            = -1;
65 static gint ett_bthsp_command    = -1;
66 static gint ett_bthsp_parameters = -1;
67
68 static dissector_handle_t bthsp_handle;
69
70 static wmem_tree_t *fragments = NULL;
71
72 #define ROLE_UNKNOWN  0
73 #define ROLE_AG       1
74 #define ROLE_HS       2
75
76 #define TYPE_UNKNOWN       0x0000
77 #define TYPE_RESPONSE_ACK  0x0d0a
78 #define TYPE_RESPONSE      0x003a
79 #define TYPE_ACTION        0x003d
80 #define TYPE_ACTION_SIMPLY 0x000d
81 #define TYPE_READ          0x003f
82 #define TYPE_TEST          0x3d3f
83
84 static gint hsp_role = ROLE_UNKNOWN;
85
86 enum reassemble_state_t {
87     REASSEMBLE_FRAGMENT,
88     REASSEMBLE_PARTIALLY,
89     REASSEMBLE_DONE
90 };
91
92 typedef struct _fragment_t {
93     guint32                  interface_id;
94     guint32                  adapter_id;
95     guint32                  chandle;
96     guint32                  dlci;
97     guint32                  role;
98
99     guint                    index;
100     guint                    length;
101     guint8                  *data;
102     struct _fragment_t      *previous_fragment;
103
104     guint                    reassemble_start_offset;
105     guint                    reassemble_end_offset;
106     enum reassemble_state_t  reassemble_state;
107 } fragment_t;
108
109 typedef struct _at_cmd_t {
110     const guint8 *name;
111     const guint8 *long_name;
112
113     gboolean (*check_command)(gint role, guint16 type);
114     gboolean (*dissect_parameter)(tvbuff_t *tvb, packet_info *pinfo,
115             proto_tree *tree, gint offset, gint role, guint16 type,
116             guint8 *parameter_stream, guint parameter_number,
117             gint parameter_length, void **data);
118 } at_cmd_t;
119
120 static const value_string role_vals[] = {
121     { ROLE_UNKNOWN,   "Unknown" },
122     { ROLE_AG,        "AG - Audio Gate" },
123     { ROLE_HS,        "HS - Headset" },
124     { 0, NULL }
125 };
126
127 static const value_string at_cmd_type_vals[] = {
128     { 0x0d,   "Action Command" },
129     { 0x3a,   "Response" },
130     { 0x3d,   "Action Command" },
131     { 0x3f,   "Read Command" },
132     { 0x0d0a, "Response" },
133     { 0x3d3f, "Test Command" },
134     { 0, NULL }
135 };
136
137 static const enum_val_t pref_hsp_role[] = {
138     { "off",     "Off",                    ROLE_UNKNOWN },
139     { "ag",      "Sent is AG, Rcvd is HS", ROLE_AG },
140     { "hs",      "Sent is HS, Rcvd is AG", ROLE_HS },
141     { NULL, NULL, 0 }
142 };
143
144 void proto_register_bthsp(void);
145 void proto_reg_handoff_bthsp(void);
146
147 static guint32 get_uint_parameter(guint8 *parameter_stream, gint parameter_length)
148 {
149     guint32      value;
150     guint8      *val;
151
152     val = (guint8 *) wmem_alloc(wmem_packet_scope(), parameter_length + 1);
153     memcpy(val, parameter_stream, parameter_length);
154     val[parameter_length] = '\0';
155     value = (guint32) g_ascii_strtoull(val, NULL, 10);
156
157     return value;
158 }
159
160 static gboolean check_vgs(gint role, guint16 type) {
161     if (role == ROLE_HS && type == TYPE_ACTION) return TRUE;
162     if (role == ROLE_AG && type == TYPE_RESPONSE) return TRUE;
163
164     return FALSE;
165 }
166
167 static gboolean check_vgm(gint role, guint16 type) {
168     if (role == ROLE_HS && type == TYPE_ACTION) return TRUE;
169     if (role == ROLE_AG && type == TYPE_RESPONSE) return TRUE;
170
171     return FALSE;
172 }
173
174 static gboolean check_ckpd(gint role, guint16 type) {
175     if (role == ROLE_HS && type == TYPE_ACTION) return TRUE;
176
177     return FALSE;
178 }
179
180 static gboolean check_only_ag_role(gint role, guint16 type) {
181     if (role == ROLE_AG && type == TYPE_RESPONSE_ACK) return TRUE;
182
183     return FALSE;
184 }
185
186 static gint
187 dissect_vgs_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
188         gint offset, gint role, guint16 type, guint8 *parameter_stream,
189         guint parameter_number, gint parameter_length, void **data _U_)
190 {
191     proto_item  *pitem;
192     guint32      value;
193
194     if (!check_vgs(role, type)) return FALSE;
195
196     if (parameter_number > 0) return FALSE;
197
198     value = get_uint_parameter(parameter_stream, parameter_length);
199
200     pitem = proto_tree_add_uint(tree, hf_vgs, tvb, offset, parameter_length, value);
201     proto_item_append_text(pitem, "/15");
202
203     if (value > 15) {
204         expert_add_info(pinfo, pitem, &ei_vgs_gain);
205     }
206
207     return TRUE;
208 }
209
210 static gint
211 dissect_vgm_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
212         gint offset, gint role, guint16 type, guint8 *parameter_stream,
213         guint parameter_number, gint parameter_length, void **data _U_)
214 {
215     proto_item  *pitem;
216     guint32      value;
217
218     if (!check_vgm(role, type)) return FALSE;
219
220     if (parameter_number > 0) return FALSE;
221
222     value = get_uint_parameter(parameter_stream, parameter_length);
223
224     pitem = proto_tree_add_uint(tree, hf_vgm, tvb, offset, parameter_length, value);
225     proto_item_append_text(pitem, "/15");
226
227     if (value > 15) {
228         expert_add_info(pinfo, pitem, &ei_vgm_gain);
229     }
230
231     return TRUE;
232 }
233
234 static gint
235 dissect_ckpd_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
236         gint offset, gint role, guint16 type, guint8 *parameter_stream,
237         guint parameter_number, gint parameter_length, void **data _U_)
238 {
239     proto_item  *pitem;
240     guint32      value;
241
242     if (!check_ckpd(role, type)) return FALSE;
243
244
245     if (parameter_number > 0) return FALSE;
246
247     value = get_uint_parameter(parameter_stream, parameter_length);
248
249     pitem = proto_tree_add_uint(tree, hf_ckpd, tvb, offset, parameter_length, value);
250
251     if (value != 200) {
252         expert_add_info(pinfo, pitem, &ei_ckpd);
253     }
254
255     return TRUE;
256 }
257
258 static gint
259 dissect_no_parameter(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_,
260         gint offset _U_, gint role _U_, guint16 type _U_, guint8 *parameter_stream _U_,
261         guint parameter_number _U_, gint parameter_length _U_, void **data _U_)
262 {
263     return FALSE;
264 }
265
266 static const at_cmd_t at_cmds[] = {
267     { "+VGS",       "Gain of Speaker",                          check_vgs,  dissect_vgs_parameter  },
268     { "+VGM",       "Gain of Microphone",                       check_vgm,  dissect_vgm_parameter  },
269     { "+CKPD",      "Control Keypad",                           check_ckpd, dissect_ckpd_parameter },
270     { "ERROR",      "ERROR",                                    check_only_ag_role, dissect_no_parameter },
271     { "RING",       "Incomming Call Indication",                check_only_ag_role, dissect_no_parameter },
272     { "OK",         "OK",                                       check_only_ag_role, dissect_no_parameter },
273     { NULL, NULL, NULL, NULL }
274 };
275
276
277 static gint
278 dissect_at_command(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
279         gint offset, guint32 role, gint command_number)
280 {
281     proto_item      *pitem;
282     proto_tree      *command_item;
283     proto_item      *command_tree;
284     proto_tree      *parameters_item = NULL;
285     proto_item      *parameters_tree = NULL;
286     guint8          *col_str = NULL;
287     guint8          *at_stream;
288     guint8          *at_command = NULL;
289     gint             i_char = 0;
290     guint            i_char_fix = 0;
291     gint             length;
292     const at_cmd_t  *i_at_cmd;
293     gint             parameter_length;
294     guint            parameter_number = 0;
295     gint             first_parameter_offset = offset;
296     gint             last_parameter_offset  = offset;
297     guint16          type = TYPE_UNKNOWN;
298     guint32          brackets;
299     gboolean         quotation;
300     gboolean         next;
301     void            *data;
302
303     length = tvb_length_remaining(tvb, offset);
304     if (length <= 0)
305         return tvb_length(tvb);
306
307     if (!command_number) {
308         proto_tree_add_item(tree, hf_data, tvb, offset, length, ENC_NA | ENC_ASCII);
309         col_str = (guint8 *) wmem_alloc(wmem_packet_scope(), length + 1);
310         tvb_memcpy(tvb, col_str, offset, length);
311         col_str[length] = '\0';
312     }
313
314     at_stream = (guint8 *) wmem_alloc(wmem_packet_scope(), length + 1);
315     tvb_memcpy(tvb, at_stream, offset, length);
316     at_stream[length] = '\0';
317     while (at_stream[i_char]) {
318         at_stream[i_char] = toupper(at_stream[i_char]);
319         if (!command_number) {
320             col_str[i_char] = toupper(col_str[i_char]);
321             if (!g_ascii_isgraph(col_str[i_char])) col_str[i_char] = ' ';
322         }
323         i_char += 1;
324     }
325
326     command_item = proto_tree_add_none_format(tree, hf_command, tvb,
327             offset, 0, "Command %u", command_number);
328     command_tree = proto_item_add_subtree(command_item, ett_bthsp_command);
329
330     if (!command_number) col_append_fstr(pinfo->cinfo, COL_INFO, "%s", col_str);
331
332     if (role == ROLE_HS) {
333         if (command_number) {
334             at_command = at_stream;
335             i_char = 0;
336         } else {
337             at_command = g_strstr_len(at_stream, length, "AT");
338
339             if (at_command) {
340                 i_char = (guint) (at_command - at_stream);
341
342                 if (i_char) {
343                     proto_tree_add_item(command_tree, hf_at_ignored, tvb, offset,
344                         i_char, ENC_NA | ENC_ASCII);
345                     offset += i_char;
346                 }
347
348                 proto_tree_add_item(command_tree, hf_at_command_line_prefix,
349                         tvb, offset, 2, ENC_NA | ENC_ASCII);
350                 offset += 2;
351                 i_char += 2;
352                 at_command = at_stream;
353
354                 at_command += i_char;
355                 length -= i_char;
356                 i_char_fix += i_char;
357                 i_char = 0;
358             }
359         }
360     } else {
361         at_command = at_stream;
362         i_char = 0;
363         while (i_char <= length &&
364                 (at_command[i_char] == '\r' || at_command[i_char] == '\n' ||
365                 at_command[i_char] == ' ' || at_command[i_char] == '\t')) {
366             /* ignore white characters */
367             i_char += 1;
368         }
369
370         offset += i_char;
371         at_command += i_char;
372         length -= i_char;
373         i_char_fix += i_char;
374         i_char = 0;
375     }
376
377     if (at_command) {
378
379         while (i_char < length &&
380                         (at_command[i_char] != '\r' && at_command[i_char] != '=' &&
381                         at_command[i_char] != ';' && at_command[i_char] != '?' &&
382                         at_command[i_char] != ':')) {
383             i_char += 1;
384         }
385
386         i_at_cmd = at_cmds;
387         if (at_command[0] == '\r') {
388             pitem = proto_tree_add_item(command_tree, hf_at_cmd, tvb, offset - 2,
389                     2, ENC_NA | ENC_ASCII);
390             i_at_cmd = NULL;
391         } else {
392             pitem = NULL;
393             while (i_at_cmd->name) {
394                 if (g_str_has_prefix(&at_command[0], i_at_cmd->name)) {
395                     pitem = proto_tree_add_item(command_tree, hf_at_cmd, tvb, offset,
396                             (gint) strlen(i_at_cmd->name), ENC_NA | ENC_ASCII);
397                     proto_item_append_text(pitem, " (%s)", i_at_cmd->long_name);
398                     break;
399                 }
400                 i_at_cmd += 1;
401             }
402
403             if (!pitem) {
404                 pitem = proto_tree_add_item(command_tree, hf_at_cmd, tvb, offset,
405                         i_char, ENC_NA | ENC_ASCII);
406             }
407         }
408
409
410         if (i_at_cmd && i_at_cmd->name == NULL) {
411             char *name;
412
413             name = (char *) wmem_alloc(wmem_packet_scope(), i_char + 2);
414             g_strlcpy(name, at_command, i_char + 1);
415             name[i_char + 1] = '\0';
416             proto_item_append_text(command_item, ": %s (Unknown)", name);
417             proto_item_append_text(pitem, " (Unknown - Non-Standard HSP Command)");
418             expert_add_info(pinfo, pitem, &ei_non_mandatory_command);
419         } else if (i_at_cmd == NULL) {
420             proto_item_append_text(command_item, ": AT");
421         } else {
422             proto_item_append_text(command_item, ": %s", i_at_cmd->name);
423         }
424
425         offset += i_char;
426
427         if (i_at_cmd && g_strcmp0(i_at_cmd->name, "D")) {
428             if (length >= 2 && at_command[i_char] == '=' && at_command[i_char + 1] == '?') {
429                 type = at_command[i_char] << 8 | at_command[i_char + 1];
430                 proto_tree_add_uint(command_tree, hf_at_cmd_type, tvb, offset, 2, type);
431                 offset += 2;
432                 i_char += 2;
433             } else if (role == ROLE_AG && length >= 2 && at_command[i_char] == '\r' && at_command[i_char + 1] == '\n') {
434                 type = at_command[i_char] << 8 | at_command[i_char + 1];
435                 proto_tree_add_uint(command_tree, hf_at_cmd_type, tvb, offset, 2, type);
436                 offset += 2;
437                 i_char += 2;
438             } else if (length >= 1 && (at_command[i_char] == '=' ||
439                         at_command[i_char] == '\r' ||
440                         at_command[i_char] == ':' ||
441                         at_command[i_char] == '?')) {
442                 type = at_command[i_char];
443                 proto_tree_add_uint(command_tree, hf_at_cmd_type, tvb, offset, 1, type);
444                 offset += 1;
445                 i_char += 1;
446             }
447         }
448
449         if (i_at_cmd && i_at_cmd->check_command && !i_at_cmd->check_command(role, type)) {
450             expert_add_info(pinfo, command_item, &ei_invalid_usage);
451         }
452
453         parameters_item = proto_tree_add_none_format(command_tree, hf_parameters, tvb,
454                 offset, 0, "Parameters");
455         parameters_tree = proto_item_add_subtree(parameters_item, ett_bthsp_parameters);
456
457         data = NULL;
458
459         while (i_char < length) {
460
461             while (at_command[i_char] == ' ' || at_command[i_char]  == '\t') {
462                 offset += 1;
463                 i_char += 1;
464             }
465
466             parameter_length = 0;
467             brackets = 0;
468             quotation = FALSE;
469             next = FALSE;
470
471             if (at_command[i_char + parameter_length] != '\r') {
472                 while (i_char + parameter_length < length &&
473                         at_command[i_char + parameter_length] != '\r') {
474
475                     if (at_command[i_char + parameter_length] == ';') {
476                         next = TRUE;
477                         break;
478                     }
479
480                     if (at_command[i_char + parameter_length] == '"') {
481                         quotation = quotation ? FALSE : TRUE;
482                     }
483
484                     if (quotation == TRUE) {
485                         parameter_length += 1;
486                         continue;
487                     }
488
489                     if (at_command[i_char + parameter_length] == '(') {
490                         brackets += 1;
491                     }
492                     if (at_command[i_char + parameter_length] == ')') {
493                         brackets -= 1;
494                     }
495
496                     if (brackets == 0 && at_command[i_char + parameter_length] == ',') {
497                         break;
498                     }
499
500                     parameter_length += 1;
501                 }
502
503 /* TODO: Save bthsp.at_cmd, bthsp.at_cmd.type, frame_time  and frame_num here in
504
505                 if (role == ROLE_HS && pinfo->fd->flags.visited == 0) {
506
507     at_cmd_db = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
508
509     interface_id
510     adapter_id
511     chandle
512     dlci
513
514     frame_number
515 -------------------
516     at_command
517     at_type
518     frame_num
519     frame_time
520     status
521     first_response_in (if 0 - no response)
522
523
524             k_interface_id = interface_id;
525             k_adapter_id   = adapter_id;
526             k_chandle      = chandle;
527             k_dlci         = dlci;
528             k_frame_number = pinfo->fd->num;
529
530
531             key[0].length = 1;
532             key[0].key = &k_interface_id;
533             key[1].length = 1;
534             key[1].key = &k_adapter_id;
535             key[2].length = 1;
536             key[2].key = &k_chandle;
537             key[3].length = 1;
538             key[3].key = &k_dlci;
539             key[4].length = 1;
540             key[4].key = &k_frame_number;
541             key[5].length = 0;
542             key[5].key = NULL;
543
544             cmd = wmem_new(wmem_file_scope(), at_cmd_entry_t);
545             cmd->interface_id = interface_id;
546             cmd->adapter_id   = adapter_id;
547             cmd->chandle      = chandle;
548             cmd->dlci         = dlci;
549
550             cmd->frame_number = pinfo->fd->num;
551             cmd->status = STATUS_NO_RESPONSE;
552             cmd->time = pinfo->fd->abs_ts;
553             cmd->at_command
554             cmd->at_type
555             cmd->first_response_in = 0;
556
557             wmem_tree_insert32_array(at_cmd_db, key, cmd);
558     }
559
560 */
561
562                 first_parameter_offset = offset;
563                 if (type == TYPE_ACTION || type == TYPE_RESPONSE) {
564                     if (i_at_cmd && (i_at_cmd->dissect_parameter != NULL &&
565                             !i_at_cmd->dissect_parameter(tvb, pinfo, parameters_tree, offset, role,
566                             type, &at_command[i_char], parameter_number, parameter_length, &data) )) {
567                         pitem = proto_tree_add_item(parameters_tree,
568                                 hf_unknown_parameter, tvb, offset,
569                                 parameter_length, ENC_NA | ENC_ASCII);
570                         expert_add_info(pinfo, pitem, &ei_unknown_parameter);
571                     } else if (i_at_cmd && i_at_cmd->dissect_parameter == NULL) {
572                         proto_tree_add_item(parameters_tree, hf_parameter, tvb, offset,
573                                 parameter_length, ENC_NA | ENC_ASCII);
574                     }
575                 }
576             }
577
578             if (type != TYPE_ACTION_SIMPLY && type != TYPE_RESPONSE_ACK && type != TYPE_TEST && type != TYPE_READ)
579                 parameter_number += 1;
580             i_char += parameter_length;
581             offset += parameter_length;
582             last_parameter_offset = offset;
583
584             if (role == ROLE_AG &&
585                     i_char + 1 <= length &&
586                     at_command[i_char] == '\r' &&
587                     at_command[i_char + 1] == '\n') {
588                 offset += 2;
589                 i_char += 2;
590                 break;
591             } else if (at_command[i_char] == ',' ||
592                         at_command[i_char] == '\r' ||
593                         at_command[i_char] == ';') {
594                     i_char += 1;
595                     offset += 1;
596             }
597
598             if (next) break;
599         }
600
601         i_char += i_char_fix;
602         proto_item_set_len(command_item, i_char);
603     } else {
604         length = tvb_length_remaining(tvb, offset);
605         if (length < 0)
606             length = 0;
607         proto_item_set_len(command_item, length);
608         offset += length;
609     }
610
611     if (parameter_number > 0 && last_parameter_offset - first_parameter_offset > 0)
612         proto_item_set_len(parameters_item, last_parameter_offset - first_parameter_offset);
613     else
614         proto_item_append_text(parameters_item, ": No");
615
616     if (role == ROLE_AG) {
617         guint command_frame_number = 0;
618
619         if (command_frame_number) {
620             pitem = proto_tree_add_uint(command_tree, hf_command_in, tvb, offset,
621                     0, command_frame_number);
622             PROTO_ITEM_SET_GENERATED(pitem);
623         } else {
624             pitem = proto_tree_add_item(command_tree, hf_unsolicited, tvb, offset, 0, ENC_NA);
625             PROTO_ITEM_SET_GENERATED(pitem);
626         }
627     }
628
629     return offset;
630 }
631
632 static gint
633 dissect_bthsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
634 {
635     proto_item       *main_item;
636     proto_tree       *main_tree;
637     proto_item       *pitem;
638     gint              offset = 0;
639     guint32           role = ROLE_UNKNOWN;
640     wmem_tree_key_t   key[10];
641     guint32           k_interface_id;
642     guint32           k_adapter_id;
643     guint32           k_chandle;
644     guint32           k_dlci;
645     guint32           k_role;
646     guint32           k_frame_number;
647     guint32           interface_id;
648     guint32           adapter_id;
649     guint32           chandle;
650     guint32           dlci;
651     fragment_t       *fragment;
652     fragment_t       *previous_fragment;
653     fragment_t       *i_fragment;
654     btrfcomm_data_t  *rfcomm_data;
655     guint8           *at_stream;
656     gint              length;
657     gint              command_number;
658     gint              i_length;
659     tvbuff_t         *reassembled_tvb = NULL;
660     guint             reassemble_start_offset = 0;
661     guint             reassemble_end_offset   = 0;
662
663     /* Reject the packet if data is NULL */
664     if (data == NULL)
665         return 0;
666     rfcomm_data = (btrfcomm_data_t *) data;
667
668     main_item = proto_tree_add_item(tree, proto_bthsp, tvb, 0, -1, ENC_NA);
669     main_tree = proto_item_add_subtree(main_item, ett_bthsp);
670
671     col_set_str(pinfo->cinfo, COL_PROTOCOL, "HSP");
672
673     switch (pinfo->p2p_dir) {
674         case P2P_DIR_SENT:
675             col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
676             break;
677         case P2P_DIR_RECV:
678             col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
679             break;
680         default:
681             col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown direction %d ", pinfo->p2p_dir);
682             break;
683     }
684
685     interface_id = rfcomm_data->interface_id;
686     adapter_id   = rfcomm_data->adapter_id;
687     chandle      = rfcomm_data->chandle;
688     dlci         = rfcomm_data->dlci;
689
690     if ((hsp_role == ROLE_AG && pinfo->p2p_dir == P2P_DIR_SENT) ||
691             (hsp_role == ROLE_HS && pinfo->p2p_dir == P2P_DIR_RECV)) {
692         role = ROLE_AG;
693     } else if (hsp_role != ROLE_UNKNOWN) {
694         role = ROLE_HS;
695     }
696
697     if (role == ROLE_UNKNOWN) {
698         guint32          k_sdp_psm;
699         guint32          k_direction;
700         guint32          k_bd_addr_oui;
701         guint32          k_bd_addr_id;
702         guint32          k_service_type;
703         guint32          k_service_channel;
704         service_info_t  *service_info;
705
706         k_interface_id    = rfcomm_data->interface_id;
707         k_adapter_id      = rfcomm_data->adapter_id;
708         k_sdp_psm         = SDP_PSM_DEFAULT;
709         k_direction       = (rfcomm_data->is_local_psm) ? P2P_DIR_SENT : P2P_DIR_RECV;
710         if (k_direction == P2P_DIR_RECV) {
711             k_bd_addr_oui     = rfcomm_data->remote_bd_addr_oui;
712             k_bd_addr_id      = rfcomm_data->remote_bd_addr_id;
713         } else {
714             k_bd_addr_oui     = 0;
715             k_bd_addr_id      = 0;
716         }
717         k_service_type    = BTSDP_RFCOMM_PROTOCOL_UUID;
718         k_service_channel = rfcomm_data->dlci >> 1;
719         k_frame_number    = pinfo->fd->num;
720
721         key[0].length = 1;
722         key[0].key = &k_interface_id;
723         key[1].length = 1;
724         key[1].key = &k_adapter_id;
725         key[2].length = 1;
726         key[2].key = &k_sdp_psm;
727         key[3].length = 1;
728         key[3].key = &k_direction;
729         key[4].length = 1;
730         key[4].key = &k_bd_addr_oui;
731         key[5].length = 1;
732         key[5].key = &k_bd_addr_id;
733         key[6].length = 1;
734         key[6].key = &k_service_type;
735         key[7].length = 1;
736         key[7].key = &k_service_channel;
737         key[8].length = 1;
738         key[8].key = &k_frame_number;
739         key[9].length = 0;
740         key[9].key = NULL;
741
742         service_info = btsdp_get_service_info(key);
743         if (service_info && service_info->interface_id == rfcomm_data->interface_id &&
744                 service_info->adapter_id == rfcomm_data->adapter_id &&
745                 service_info->sdp_psm == SDP_PSM_DEFAULT &&
746                 ((service_info->direction == P2P_DIR_RECV &&
747                 service_info->bd_addr_oui == rfcomm_data->remote_bd_addr_oui &&
748                 service_info->bd_addr_id == rfcomm_data->remote_bd_addr_id) ||
749                 (service_info->direction != P2P_DIR_RECV &&
750                 service_info->bd_addr_oui == 0 &&
751                 service_info->bd_addr_id == 0)) &&
752                 service_info->type == BTSDP_RFCOMM_PROTOCOL_UUID &&
753                 service_info->channel == (rfcomm_data->dlci >> 1)) {
754             if ((service_info->uuid.bt_uuid == BTSDP_HSP_GW_SERVICE_UUID && service_info->direction == P2P_DIR_RECV && pinfo->p2p_dir == P2P_DIR_SENT) ||
755                 (service_info->uuid.bt_uuid == BTSDP_HSP_GW_SERVICE_UUID && service_info->direction == P2P_DIR_SENT && pinfo->p2p_dir == P2P_DIR_RECV) ||
756                 ((service_info->uuid.bt_uuid == BTSDP_HSP_SERVICE_UUID || service_info->uuid.bt_uuid == BTSDP_HSP_HS_SERVICE_UUID) && service_info->direction == P2P_DIR_RECV && pinfo->p2p_dir == P2P_DIR_RECV) ||
757                 ((service_info->uuid.bt_uuid == BTSDP_HSP_SERVICE_UUID || service_info->uuid.bt_uuid == BTSDP_HSP_HS_SERVICE_UUID) && service_info->direction == P2P_DIR_SENT && pinfo->p2p_dir == P2P_DIR_SENT)) {
758                 role = ROLE_HS;
759             } else {
760                 role = ROLE_AG;
761             }
762         }
763     }
764
765     pitem = proto_tree_add_uint(main_tree, hf_role, tvb, 0, 0, role);
766     PROTO_ITEM_SET_GENERATED(pitem);
767
768     if (role == ROLE_UNKNOWN) {
769         col_append_fstr(pinfo->cinfo, COL_INFO, "Data: %s",
770                 tvb_format_text(tvb, 0, tvb_length(tvb)));
771         proto_tree_add_item(main_tree, hf_data, tvb, 0, -1, ENC_NA | ENC_ASCII);
772         return tvb_length(tvb);
773     }
774
775     /* save fragments */
776     if (!pinfo->fd->flags.visited) {
777         k_interface_id = interface_id;
778         k_adapter_id   = adapter_id;
779         k_chandle      = chandle;
780         k_dlci         = dlci;
781         k_role         = role;
782         k_frame_number = pinfo->fd->num - 1;
783
784         key[0].length = 1;
785         key[0].key = &k_interface_id;
786         key[1].length = 1;
787         key[1].key = &k_adapter_id;
788         key[2].length = 1;
789         key[2].key = &k_chandle;
790         key[3].length = 1;
791         key[3].key = &k_dlci;
792         key[4].length = 1;
793         key[4].key = &k_role;
794         key[5].length = 1;
795         key[5].key = &k_frame_number;
796         key[6].length = 0;
797         key[6].key = NULL;
798
799         previous_fragment = (fragment_t *) wmem_tree_lookup32_array_le(fragments, key);
800         if (!(previous_fragment && previous_fragment->interface_id == interface_id &&
801                 previous_fragment->adapter_id == adapter_id &&
802                 previous_fragment->chandle == chandle &&
803                 previous_fragment->dlci == dlci &&
804                 previous_fragment->role == role &&
805                 previous_fragment->reassemble_state != REASSEMBLE_DONE)) {
806             previous_fragment = NULL;
807         }
808
809         k_interface_id = interface_id;
810         k_adapter_id   = adapter_id;
811         k_chandle      = chandle;
812         k_dlci         = dlci;
813         k_role         = role;
814         k_frame_number = pinfo->fd->num;
815
816         key[0].length = 1;
817         key[0].key = &k_interface_id;
818         key[1].length = 1;
819         key[1].key = &k_adapter_id;
820         key[2].length = 1;
821         key[2].key = &k_chandle;
822         key[3].length = 1;
823         key[3].key = &k_dlci;
824         key[4].length = 1;
825         key[4].key = &k_role;
826         key[5].length = 1;
827         key[5].key = &k_frame_number;
828         key[6].length = 0;
829         key[6].key = NULL;
830
831         fragment = wmem_new(wmem_file_scope(), fragment_t);
832         fragment->interface_id      = interface_id;
833         fragment->adapter_id        = adapter_id;
834         fragment->chandle           = chandle;
835         fragment->dlci              = dlci;
836         fragment->role              = role;
837         fragment->index             = previous_fragment ? previous_fragment->index + previous_fragment->length : 0;
838         fragment->reassemble_state  = REASSEMBLE_FRAGMENT;
839         fragment->length            = tvb_length(tvb);
840         fragment->data              = (guint8 *) wmem_alloc(wmem_file_scope(), fragment->length);
841         fragment->previous_fragment = previous_fragment;
842         tvb_memcpy(tvb, fragment->data, offset, fragment->length);
843
844         wmem_tree_insert32_array(fragments, key, fragment);
845
846         /* Detect reassemble end character: \r for HS or \n for AG */
847         length = tvb_length(tvb);
848         at_stream = tvb_get_string_enc(wmem_packet_scope(), tvb, 0, length, ENC_ASCII);
849
850         reassemble_start_offset = 0;
851
852         for (i_length = 0; i_length < length; i_length += 1) {
853             if (!((role == ROLE_HS && at_stream[i_length] == '\r') ||
854                     (role == ROLE_AG && at_stream[i_length] == '\n'))) {
855                 continue;
856             }
857
858             if (role == ROLE_HS && at_stream[i_length] == '\r') {
859                 reassemble_start_offset = i_length + 1;
860                 if (reassemble_end_offset == 0) reassemble_end_offset = i_length + 1;
861             }
862
863             if (role == ROLE_AG && at_stream[i_length] == '\n') {
864                 reassemble_start_offset = i_length + 1;
865             }
866
867             k_interface_id = interface_id;
868             k_adapter_id   = adapter_id;
869             k_chandle      = chandle;
870             k_dlci         = dlci;
871             k_role         = role;
872             k_frame_number = pinfo->fd->num;
873
874             key[0].length = 1;
875             key[0].key = &k_interface_id;
876             key[1].length = 1;
877             key[1].key = &k_adapter_id;
878             key[2].length = 1;
879             key[2].key = &k_chandle;
880             key[3].length = 1;
881             key[3].key = &k_dlci;
882             key[4].length = 1;
883             key[4].key = &k_role;
884             key[5].length = 1;
885             key[5].key = &k_frame_number;
886             key[6].length = 0;
887             key[6].key = NULL;
888
889             fragment = (fragment_t *) wmem_tree_lookup32_array_le(fragments, key);
890             if (fragment && fragment->interface_id == interface_id &&
891                     fragment->adapter_id == adapter_id &&
892                     fragment->chandle == chandle &&
893                     fragment->dlci == dlci &&
894                     fragment->role == role) {
895                 i_fragment = fragment;
896                 while (i_fragment && i_fragment->index > 0) {
897                     i_fragment = i_fragment->previous_fragment;
898                 }
899
900                 if (i_length + 1 == length &&
901                         role == ROLE_HS &&
902                         at_stream[i_length] == '\r') {
903                     fragment->reassemble_state = REASSEMBLE_DONE;
904                 } else if (i_length + 1 == length &&
905                         role == ROLE_AG &&
906                         i_length >= 4 &&
907                         at_stream[i_length] == '\n' &&
908                         at_stream[i_length - 1] == '\r' &&
909                         at_stream[0] == '\r' &&
910                         at_stream[1] == '\n') {
911                     fragment->reassemble_state = REASSEMBLE_DONE;
912                 } else if (i_length + 1 == length &&
913                         role == ROLE_AG &&
914                         i_length >= 2 &&
915                         at_stream[i_length] == '\n' &&
916                         at_stream[i_length - 1] == '\r' &&
917                         i_fragment &&
918                         i_fragment->reassemble_state == REASSEMBLE_FRAGMENT &&
919                         i_fragment->length >= 2 &&
920                         i_fragment->data[0] == '\r' &&
921                         i_fragment->data[1] == '\n') {
922                     fragment->reassemble_state = REASSEMBLE_DONE;
923                 } else if (role == ROLE_HS) {
924                     fragment->reassemble_state = REASSEMBLE_PARTIALLY;
925                 }
926                 fragment->reassemble_start_offset = reassemble_start_offset;
927                 fragment->reassemble_end_offset = reassemble_end_offset;
928             }
929         }
930     }
931
932     /* recover reassembled payload */
933     k_interface_id = interface_id;
934     k_adapter_id   = adapter_id;
935     k_chandle      = chandle;
936     k_dlci         = dlci;
937     k_role         = role;
938     k_frame_number = pinfo->fd->num;
939
940     key[0].length = 1;
941     key[0].key = &k_interface_id;
942     key[1].length = 1;
943     key[1].key = &k_adapter_id;
944     key[2].length = 1;
945     key[2].key = &k_chandle;
946     key[3].length = 1;
947     key[3].key = &k_dlci;
948     key[4].length = 1;
949     key[4].key = &k_role;
950     key[5].length = 1;
951     key[5].key = &k_frame_number;
952     key[6].length = 0;
953     key[6].key = NULL;
954
955     fragment = (fragment_t *) wmem_tree_lookup32_array_le(fragments, key);
956     if (fragment && fragment->interface_id == interface_id &&
957             fragment->adapter_id == adapter_id &&
958             fragment->chandle == chandle &&
959             fragment->dlci == dlci &&
960             fragment->role == role &&
961             fragment->reassemble_state != REASSEMBLE_FRAGMENT) {
962         guint8    *at_data;
963         guint      i_data_offset;
964
965         i_data_offset = fragment->index + fragment->length;
966         at_data = (guint8 *) wmem_alloc(pinfo->pool, fragment->index + fragment->length);
967
968         i_fragment = fragment;
969
970         if (i_fragment && i_fragment->reassemble_state == REASSEMBLE_PARTIALLY) {
971                 i_data_offset -= i_fragment->reassemble_end_offset;
972                 memcpy(at_data + i_data_offset, i_fragment->data, i_fragment->reassemble_end_offset);
973
974             i_fragment = i_fragment->previous_fragment;
975         }
976
977         if (i_fragment) {
978             while (i_fragment && i_fragment->index > 0) {
979                 i_data_offset -= i_fragment->length;
980                 memcpy(at_data + i_data_offset, i_fragment->data, i_fragment->length);
981                 i_fragment = i_fragment->previous_fragment;
982             }
983
984             if (i_fragment && i_fragment->reassemble_state == REASSEMBLE_PARTIALLY) {
985                 i_data_offset -= (i_fragment->length - i_fragment->reassemble_start_offset);
986                 memcpy(at_data + i_data_offset, i_fragment->data + i_fragment->reassemble_start_offset,
987                         i_fragment->length - i_fragment->reassemble_start_offset);
988             } else if (i_fragment) {
989                 i_data_offset -= i_fragment->length;
990                 memcpy(at_data + i_data_offset, i_fragment->data, i_fragment->length);
991             }
992         }
993
994         if (fragment->index > 0 && fragment->length > 0) {
995             proto_tree_add_item(main_tree, hf_fragment, tvb, offset,
996                                 tvb_length_remaining(tvb, offset), ENC_ASCII | ENC_NA);
997             reassembled_tvb = tvb_new_child_real_data(tvb, at_data,
998                     fragment->index + fragment->length, fragment->index + fragment->length);
999             add_new_data_source(pinfo, reassembled_tvb, "Reassembled HSP");
1000         }
1001
1002         command_number = 0;
1003         if (reassembled_tvb) {
1004             guint reassembled_offset = 0;
1005
1006             while (tvb_length(reassembled_tvb) > reassembled_offset) {
1007                 reassembled_offset = dissect_at_command(reassembled_tvb,
1008                         pinfo, main_tree, reassembled_offset, role, command_number);
1009                 command_number += 1;
1010             }
1011         } else {
1012             while (tvb_length(tvb) > (guint) offset) {
1013                 offset = dissect_at_command(tvb, pinfo, main_tree, offset, role, command_number);
1014                 command_number += 1;
1015             }
1016         }
1017     } else {
1018         col_append_fstr(pinfo->cinfo, COL_INFO, "Fragment: %s",
1019                 tvb_format_text_wsp(tvb, offset, tvb_length_remaining(tvb, offset)));
1020         pitem = proto_tree_add_item(main_tree, hf_fragmented, tvb, 0, 0, ENC_NA);
1021         PROTO_ITEM_SET_GENERATED(pitem);
1022         proto_tree_add_item(main_tree, hf_fragment, tvb, offset,
1023                 tvb_length_remaining(tvb, offset), ENC_ASCII | ENC_NA);
1024     }
1025
1026     return offset;
1027 }
1028
1029 void
1030 proto_register_bthsp(void)
1031 {
1032     module_t         *module;
1033     expert_module_t  *expert_bthsp;
1034
1035     static hf_register_info hf[] = {
1036         { &hf_command,
1037            { "Command",                          "bthsp.command",
1038            FT_NONE, BASE_NONE, NULL, 0,
1039            NULL, HFILL}
1040         },
1041         { &hf_parameters,
1042            { "Parameters",                       "bthsp.parameters",
1043            FT_NONE, BASE_NONE, NULL, 0,
1044            NULL, HFILL}
1045         },
1046         { &hf_command_in,
1047            { "Command frame number in",          "bthsp.command_in",
1048            FT_FRAMENUM, BASE_NONE, NULL, 0,
1049            NULL, HFILL}
1050         },
1051         { &hf_unsolicited,
1052            { "Unsolicited",                      "bthsp.unsolicited",
1053            FT_NONE, BASE_NONE, NULL, 0,
1054            NULL, HFILL}
1055         },
1056         { &hf_data,
1057            { "AT Stream",                        "bthsp.data",
1058            FT_STRING, BASE_NONE, NULL, 0,
1059            NULL, HFILL}
1060         },
1061         { &hf_fragment,
1062            { "Fragment",                         "bthsp.fragment",
1063            FT_STRING, BASE_NONE, NULL, 0,
1064            NULL, HFILL}
1065         },
1066         { &hf_fragmented,
1067            { "Fragmented",                       "bthsp.fragmented",
1068            FT_NONE, BASE_NONE, NULL, 0,
1069            NULL, HFILL}
1070         },
1071         { &hf_at_ignored,
1072            { "Ignored",                          "bthsp.ignored",
1073            FT_BYTES, BASE_NONE, NULL, 0,
1074            NULL, HFILL}
1075         },
1076         { &hf_at_cmd,
1077            { "Command",                          "bthsp.at_cmd",
1078            FT_STRING, BASE_NONE, NULL, 0,
1079            NULL, HFILL}
1080         },
1081         { &hf_at_cmd_type,
1082            { "Type",                             "bthsp.at_cmd.type",
1083            FT_UINT16, BASE_HEX, VALS(at_cmd_type_vals), 0,
1084            NULL, HFILL}
1085         },
1086         { &hf_at_command_line_prefix,
1087            { "Command Line Prefix",              "bthsp.command_line_prefix",
1088            FT_STRING, BASE_NONE, NULL, 0,
1089            NULL, HFILL}
1090         },
1091         { &hf_parameter,
1092            { "Parameter",                        "bthsp.parameter",
1093            FT_STRING, BASE_NONE, NULL, 0,
1094            NULL, HFILL}
1095         },
1096         { &hf_unknown_parameter,
1097            { "Unknown Parameter",                "bthsp.unknown_parameter",
1098            FT_STRING, BASE_NONE, NULL, 0,
1099            NULL, HFILL}
1100         },
1101         { &hf_role,
1102            { "Role",                             "bthsp.role",
1103            FT_UINT8, BASE_DEC, VALS(role_vals), 0,
1104            NULL, HFILL}
1105         },
1106         { &hf_vgs,
1107            { "Gain",                             "bthsp.vgs",
1108            FT_UINT8, BASE_DEC, NULL, 0,
1109            NULL, HFILL}
1110         },
1111         { &hf_vgm,
1112            { "Gain",                             "bthsp.vgm",
1113            FT_UINT8, BASE_DEC, NULL, 0,
1114            NULL, HFILL}
1115         },
1116         { &hf_ckpd,
1117            { "Key",                             "bthsp.ckpd",
1118            FT_UINT8, BASE_DEC, NULL, 0,
1119            NULL, HFILL}
1120         }
1121     };
1122
1123     static ei_register_info ei[] = {
1124         { &ei_non_mandatory_command, { "bthsp.expert.non_mandatory_command", PI_PROTOCOL, PI_NOTE, "Non-mandatory command in HSP", EXPFILL }},
1125         { &ei_invalid_usage,         { "bthsp.expert.invalid_usage", PI_PROTOCOL, PI_WARN, "Non mandatory type or command in this role", EXPFILL }},
1126         { &ei_unknown_parameter,     { "bthsp.expert.unknown_parameter", PI_PROTOCOL, PI_WARN, "Unknown parameter", EXPFILL }},
1127         { &ei_vgm_gain,              { "bthsp.expert.vgm", PI_PROTOCOL, PI_WARN, "Gain of microphone exceeds range 0-15", EXPFILL }},
1128         { &ei_vgs_gain,              { "bthsp.expert.vgs", PI_PROTOCOL, PI_WARN, "Gain of speaker exceeds range 0-15", EXPFILL }},
1129         { &ei_ckpd,              { "bthsp.expert.ckpd", PI_PROTOCOL, PI_WARN, "Only key 200 is covered in HSP", EXPFILL }}    };
1130
1131     static gint *ett[] = {
1132         &ett_bthsp,
1133         &ett_bthsp_command,
1134         &ett_bthsp_parameters
1135     };
1136
1137     fragments = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
1138
1139     proto_bthsp = proto_register_protocol("Bluetooth HSP Profile", "BT HSP", "bthsp");
1140     bthsp_handle = new_register_dissector("bthsp", dissect_bthsp, proto_bthsp);
1141
1142     proto_register_field_array(proto_bthsp, hf, array_length(hf));
1143     proto_register_subtree_array(ett, array_length(ett));
1144
1145     module = prefs_register_protocol(proto_bthsp, NULL);
1146     prefs_register_static_text_preference(module, "hsp.version",
1147             "Bluetooth Profile HSP version: 1.2",
1148             "Version of profile supported by this dissector.");
1149
1150     prefs_register_enum_preference(module, "hsp.hsp_role",
1151             "Force treat packets as AG or HS role",
1152             "Force treat packets as AG or HS role",
1153             &hsp_role, pref_hsp_role, TRUE);
1154
1155     expert_bthsp = expert_register_protocol(proto_bthsp);
1156     expert_register_field_array(expert_bthsp, ei, array_length(ei));
1157 }
1158
1159 void
1160 proto_reg_handoff_bthsp(void)
1161 {
1162     dissector_add_uint("btrfcomm.service", BTSDP_HSP_SERVICE_UUID, bthsp_handle);
1163     dissector_add_uint("btrfcomm.service", BTSDP_HSP_HS_SERVICE_UUID, bthsp_handle);
1164     dissector_add_uint("btrfcomm.service", BTSDP_HSP_GW_SERVICE_UUID, bthsp_handle);
1165     dissector_add_handle("btrfcomm.channel", bthsp_handle);
1166 }
1167
1168 /*
1169  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1170  *
1171  * Local variables:
1172  * c-basic-offset: 4
1173  * tab-width: 8
1174  * indent-tabs-mode: nil
1175  * End:
1176  *
1177  * vi: set shiftwidth=4 tabstop=8 expandtab:
1178  * :indentSize=4:tabSize=8:noTabs=true:
1179  */