Use ENC_NA as encoding for proto_tree_add_item() calls which directly reference an...
[obnox/wireshark/wip.git] / epan / dissectors / packet-btrfcomm.c
index 06d0cdaab2ba90594214f11dd7e6e81c04bdb154..af7eee3af760ba1c000806165c53fdccecf73ed0 100644 (file)
@@ -1,5 +1,10 @@
 /* packet-btrfcomm.c
  * Routines for Bluetooth RFCOMM protocol dissection
+ * and RFCOMM based profile dissection:
+ *    - Handsfree Profile (HFP)
+ *    - Dial-Up Networking (DUN) Profile
+ *    - Serial Port Profile (SPP)
+ *
  * Copyright 2002, Wolfgang Hansmann <hansmann@cs.uni-bonn.de>
  *
  * Refactored for wireshark checkin
 #include <glib.h>
 
 #include <epan/packet.h>
-#include <epan/value_string.h>
 #include <etypes.h>
 #include <epan/emem.h>
 #include <epan/expert.h>
+#include <epan/tap.h>
+#include "packet-btsdp.h"
 #include "packet-btl2cap.h"
 #include "packet-btrfcomm.h"
 
@@ -70,9 +76,15 @@ static int hf_msc_l = -1;
 
 static int hf_fcs = -1;
 
+static int hf_at_cmd = -1;
+static int hf_dun_at_cmd = -1;
+static int hf_data = -1;
+
 /* Initialize the protocol and registered fields */
 static int proto_btrfcomm = -1;
-
+static int proto_bthf = -1;
+static int proto_btdun = -1;
+static int proto_btspp = -1;
 
 /* Initialize the subtree pointers */
 static gint ett_btrfcomm = -1;
@@ -83,8 +95,15 @@ static gint ett_mcc = -1;
 static gint ett_ctrl_pn_ci = -1;
 static gint ett_ctrl_pn_v24 = -1;
 
+static gint ett_bthf = -1;
+static gint ett_btdun = -1;
+static gint ett_btspp = -1;
+
 static emem_tree_t *dlci_table;
 
+/* Initialize dissector table */
+dissector_table_t rfcomm_service_dissector_table;
+
 typedef struct _dlci_stream_t {
        int len;
        int current;
@@ -94,11 +113,12 @@ typedef struct _dlci_stream_t {
 } dlci_stream_t;
 
 typedef struct _dlci_state_t {
+       guint32 service;
        char do_credit_fc;
-       dlci_stream_t direction[2];
 } dlci_state_t;
 
-static dissector_handle_t btobex_handle;
+static dissector_handle_t data_handle;
+static dissector_handle_t ppp_handle;
 
 static const value_string vs_ctl_pn_i[] = {
        {0x0, "use UIH Frames"},
@@ -241,12 +261,12 @@ dissect_ctrl_pn(packet_info *pinfo, proto_tree *t, tvbuff_t *tvb, int offset, in
        ti = proto_tree_add_text(t, tvb, offset, 1, "I1-I4: 0x%x, C1-C4: 0x%x", flags&0xf, (flags>>4)&0xf);
        st = proto_item_add_subtree(ti, ett_ctrl_pn_ci);
 
-       proto_tree_add_item(st, hf_pn_c14, tvb, offset, 1, TRUE);
-       proto_tree_add_item(st, hf_pn_i14, tvb, offset, 1, TRUE);
+       proto_tree_add_item(st, hf_pn_c14, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(st, hf_pn_i14, tvb, offset, 1, ENC_LITTLE_ENDIAN);
        offset++;
 
        /* priority */
-       proto_tree_add_item(t, hf_priority, tvb, offset, 1, TRUE);
+       proto_tree_add_item(t, hf_priority, tvb, offset, 1, ENC_LITTLE_ENDIAN);
        offset++;
 
        /* Ack timer */
@@ -254,24 +274,29 @@ dissect_ctrl_pn(packet_info *pinfo, proto_tree *t, tvbuff_t *tvb, int offset, in
        offset++;
 
        /* max frame size */
-       proto_tree_add_item(t, hf_max_frame_size, tvb, offset, 2, TRUE);
+       proto_tree_add_item(t, hf_max_frame_size, tvb, offset, 2, ENC_LITTLE_ENDIAN);
        offset+=2;
 
        /* max retrans */
-       proto_tree_add_item(t, hf_max_retrans, tvb, offset, 1, TRUE);
+       proto_tree_add_item(t, hf_max_retrans, tvb, offset, 1, ENC_LITTLE_ENDIAN);
        offset++;
 
        /* error recovery mode */
-       proto_tree_add_item(t, hf_error_recovery_mode, tvb, offset, 1, TRUE);
+       proto_tree_add_item(t, hf_error_recovery_mode, tvb, offset, 1, ENC_LITTLE_ENDIAN);
        offset++;
 
        if(!pinfo->fd->flags.visited){
-               dlci_state=se_tree_lookup32(dlci_table, dlci);
+               guint32 token;
+
+               if( pinfo->p2p_dir == cr_flag )
+                       token = dlci | 0x01; /* local service */
+               else
+                       token = dlci;
+
+               dlci_state=se_tree_lookup32(dlci_table, token);
                if(!dlci_state){
                        dlci_state=se_alloc0(sizeof(dlci_state_t));
-                       dlci_state->direction[0].current=-1;
-                       dlci_state->direction[1].current=-1;
-                       se_tree_insert32(dlci_table, dlci, dlci_state);
+                       se_tree_insert32(dlci_table, token, dlci_state);
                }
 
                if(!cl){
@@ -308,16 +333,16 @@ dissect_ctrl_msc(proto_tree *t, tvbuff_t *tvb, int offset, int length)
                                 (status >> 6) & 1, (status >> 7) & 1);
        st = proto_item_add_subtree(it, ett_ctrl_pn_v24);
 
-       proto_tree_add_item(st, hf_msc_fc, tvb, offset, 1, TRUE);
-       proto_tree_add_item(st, hf_msc_rtc, tvb, offset, 1, TRUE);
-       proto_tree_add_item(st, hf_msc_rtr, tvb, offset, 1, TRUE);
-       proto_tree_add_item(st, hf_msc_ic, tvb, offset, 1, TRUE);
-       proto_tree_add_item(st, hf_msc_dv, tvb, offset, 1, TRUE);
+       proto_tree_add_item(st, hf_msc_fc, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(st, hf_msc_rtc, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(st, hf_msc_rtr, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(st, hf_msc_ic, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(st, hf_msc_dv, tvb, offset, 1, ENC_LITTLE_ENDIAN);
        offset++;
 
        if(length==3){
                proto_tree_add_text(t, tvb, offset, 1, "Break bits B1-B3: 0x%x", (tvb_get_guint8(tvb, offset) & 0xf) >> 1);
-               proto_tree_add_item(t, hf_msc_l, tvb, offset, 1, TRUE);
+               proto_tree_add_item(t, hf_msc_l, tvb, offset, 1, ENC_LITTLE_ENDIAN);
                offset++;
        }
 
@@ -354,8 +379,8 @@ dissect_btrfcomm_Address(tvbuff_t *tvb, int offset, proto_tree *tree, guint8 *ea
        addr_tree = proto_item_add_subtree(ti, ett_addr);
 
        proto_tree_add_uint(addr_tree, hf_dlci, tvb, offset, 1, dlci);
-       proto_tree_add_item(addr_tree, hf_cr, tvb, offset, 1, TRUE);
-       proto_tree_add_item(addr_tree, hf_ea, tvb, offset, 1, TRUE);
+       proto_tree_add_item(addr_tree, hf_cr, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(addr_tree, hf_ea, tvb, offset, 1, ENC_LITTLE_ENDIAN);
        offset++;
 
        return offset;
@@ -383,8 +408,8 @@ dissect_btrfcomm_Control(tvbuff_t *tvb, int offset, proto_tree *tree, guint8 *pf
        ti = proto_tree_add_text(tree, tvb, offset, 1, "Control: Frame type: %s (0x%x), P/F flag: %d", val_to_str(frame_type, vs_frame_type, "Unknown"), frame_type, pf_flag);
        hctl_tree = proto_item_add_subtree(ti, ett_control);
 
-       proto_tree_add_item(hctl_tree, hf_pf, tvb, offset, 1, TRUE);
-       proto_tree_add_item(hctl_tree, hf_frame_type, tvb, offset, 1, TRUE);
+       proto_tree_add_item(hctl_tree, hf_pf, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(hctl_tree, hf_frame_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
 
        offset++;
        return offset;
@@ -448,18 +473,16 @@ dissect_btrfcomm_MccType(tvbuff_t *tvb, int offset, proto_tree *tree, packet_inf
 
 
        if(mcc_type){
-               if ((check_col(pinfo->cinfo, COL_INFO))){
-                       col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(mcc_type, vs_ctl, "Unknown"));
-               }
+               col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(mcc_type, vs_ctl, "Unknown"));
        }
 
 
        ti = proto_tree_add_text(tree, tvb, start_offset, offset-start_offset, "Type: %s (0x%x), C/R flag = %d, E/A flag = %d", val_to_str(mcc_type, vs_ctl, "Unknown"), mcc_type, mcc_cr_flag, mcc_ea_flag);
        mcc_tree = proto_item_add_subtree(ti, ett_mcc);
 
-       proto_tree_add_item(mcc_tree, hf_mcc_cmd, tvb, start_offset, offset-start_offset, TRUE);
-       proto_tree_add_item(mcc_tree, hf_mcc_cr, tvb, start_offset, 1, TRUE);
-       proto_tree_add_item(mcc_tree, hf_mcc_ea, tvb, start_offset, 1, TRUE);
+       proto_tree_add_item(mcc_tree, hf_mcc_cmd, tvb, start_offset, offset-start_offset, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(mcc_tree, hf_mcc_cr, tvb, start_offset, 1, ENC_LITTLE_ENDIAN);
+       proto_tree_add_item(mcc_tree, hf_mcc_ea, tvb, start_offset, 1, ENC_LITTLE_ENDIAN);
 
        return offset;
 }
@@ -480,41 +503,58 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        guint8 dlci, cr_flag, ea_flag;
        guint8 frame_type, pf_flag;
        guint16 frame_len;
-       dlci_state_t *dlci_state;
+       dlci_state_t *dlci_state = NULL;
 
-       ti = proto_tree_add_item(tree, proto_btrfcomm, tvb, offset, -1, TRUE);
+       ti = proto_tree_add_item(tree, proto_btrfcomm, tvb, offset, -1, ENC_LITTLE_ENDIAN);
        rfcomm_tree = proto_item_add_subtree(ti, ett_btrfcomm);
 
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "RFCOMM");
-       if (check_col(pinfo->cinfo, COL_INFO)) {
-               col_set_str(pinfo->cinfo, COL_INFO, pinfo->p2p_dir == P2P_DIR_SENT ? "Sent " : "Rcvd ");
-       }
+       switch (pinfo->p2p_dir) {
 
+       case P2P_DIR_SENT:
+               col_add_str(pinfo->cinfo, COL_INFO, "Sent ");
+               break;
 
-       /* flags and dlci */
-       offset=dissect_btrfcomm_Address(tvb, offset, rfcomm_tree, &ea_flag, &cr_flag, &dlci);
+       case P2P_DIR_RECV:
+               col_add_str(pinfo->cinfo, COL_INFO, "Rcvd ");
+               break;
 
+       case P2P_DIR_UNKNOWN:
+               col_clear(pinfo->cinfo, COL_INFO);
+               break;
 
-       dlci_state=se_tree_lookup32(dlci_table, dlci);
-       if(!dlci_state){
-               dlci_state=se_alloc0(sizeof(dlci_state_t));
-               dlci_state->direction[0].current=-1;
-               dlci_state->direction[1].current=-1;
-               se_tree_insert32(dlci_table, dlci, dlci_state);
+       default:
+               col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown direction %d ",
+                   pinfo->p2p_dir);
+               break;
        }
 
+
+       /* flags and dlci */
+       offset=dissect_btrfcomm_Address(tvb, offset, rfcomm_tree, &ea_flag, &cr_flag, &dlci);
        /* pf and frame type */
        offset=dissect_btrfcomm_Control(tvb, offset, rfcomm_tree, &pf_flag, &frame_type);
+       /* payload length */
+       offset=dissect_btrfcomm_PayloadLen(tvb, offset, rfcomm_tree, &frame_len);
 
+       if (dlci && (frame_len || (frame_type == 0xef) || (frame_type == 0x2f) )) {
+               guint32 token;
 
-       if ((check_col(pinfo->cinfo, COL_INFO))){
-               col_append_fstr(pinfo->cinfo, COL_INFO, "%s DLCI=%d ", val_to_str(frame_type, vs_frame_type_short, "Unknown"), dlci);
-       }
-
+               if( pinfo->p2p_dir == cr_flag )
+                       token = dlci | 0x01; /* local service */
+               else
+                       token = dlci;
 
-       /* payload length */
-       offset=dissect_btrfcomm_PayloadLen(tvb, offset, rfcomm_tree, &frame_len);
+               dlci_state=se_tree_lookup32(dlci_table, token);
+               if(!dlci_state){
+                       dlci_state=se_alloc0(sizeof(dlci_state_t));
+                       se_tree_insert32(dlci_table, token, dlci_state);
+               }
+       }
 
+       col_append_fstr(pinfo->cinfo, COL_INFO, "%s DLCI=%d ", val_to_str(frame_type, vs_frame_type_short, "Unknown"), dlci);
+       if(dlci && (frame_type == 0x2f))
+               col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", val_to_str(dlci_state->service, vs_service_classes, "Unknown"));
 
        /* UID frame */
        if(frame_type==0xef && dlci && pf_flag) {
@@ -522,7 +562,7 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                if((dlci_state->do_credit_fc&0x03)==0x03){
 /*QQQ use tvb_length_remaining()==2 and !frame_len as heuristics to catch this as well? */
                        /* add credit based flow control byte */
-                       proto_tree_add_item(rfcomm_tree, hf_fc_credits, tvb, offset, 1, TRUE);
+                       proto_tree_add_item(rfcomm_tree, hf_fc_credits, tvb, offset, 1, ENC_LITTLE_ENDIAN);
                        offset++;
                }
        }
@@ -570,8 +610,11 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                proto_item_set_len(mcc_ti, offset-start_offset);
        }
 
-       /* dissect everything as OBEX for now */
-       if(dlci && frame_len && btobex_handle){
+
+       /* try to find a higher layer dissector that has registered to handle data
+        * for this kind of service, if none is found dissect it as raw "data"
+        */
+       if(dlci&&frame_len){
                tvbuff_t *next_tvb;
                btl2cap_data_t *l2cap_data;
                btrfcomm_data_t rfcomm_data;
@@ -583,10 +626,15 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                rfcomm_data.chandle = l2cap_data->chandle;
                rfcomm_data.cid = l2cap_data->cid;
                rfcomm_data.dlci = dlci;
-               call_dissector(btobex_handle, next_tvb, pinfo, tree);
+
+               if(!dissector_try_uint(rfcomm_service_dissector_table, dlci_state->service,
+                                       next_tvb, pinfo, tree)){
+                       /* unknown service, let the data dissector handle it */
+                       call_dissector(data_handle, next_tvb, pinfo, tree);
+               }
        }
 
-       proto_tree_add_item(rfcomm_tree, hf_fcs, tvb, fcs_offset, 1, TRUE);
+       proto_tree_add_item(rfcomm_tree, hf_fcs, tvb, fcs_offset, 1, ENC_LITTLE_ENDIAN);
 }
 
 
@@ -730,7 +778,7 @@ proto_register_btrfcomm(void)
        };
 
        /* Register the protocol name and description */
-       proto_btrfcomm = proto_register_protocol("Bluetooth RFCOMM Packet", "RFCOMM", "btrfcomm");
+       proto_btrfcomm = proto_register_protocol("Bluetooth RFCOMM Protocol", "RFCOMM", "btrfcomm");
 
        register_dissector("btrfcomm", dissect_btrfcomm, proto_btrfcomm);
 
@@ -738,18 +786,241 @@ proto_register_btrfcomm(void)
        proto_register_field_array(proto_btrfcomm, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
 
+       rfcomm_service_dissector_table = register_dissector_table("btrfcomm.service", "RFCOMM SERVICE", FT_UINT16, BASE_HEX);
+
        dlci_table=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "RFCOMM dlci table");
 }
 
+static int
+btrfcomm_sdp_tap_packet(void *arg _U_, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *arg2)
+{
+       btsdp_data_t *sdp_data = (btsdp_data_t *) arg2;
+
+       if( sdp_data->protocol == BTSDP_RFCOMM_PROTOCOL_UUID ) {
+               guint32 token;
+               dlci_state_t *dlci_state;
+
+               /* rfcomm channel * 2 = dlci */
+               token = (sdp_data->channel<<1) | (sdp_data->flags & BTSDP_LOCAL_SERVICE_FLAG_MASK);
+
+               dlci_state=se_tree_lookup32(dlci_table, token);
+               if(!dlci_state){
+                       dlci_state=se_alloc0(sizeof(dlci_state_t));
+                       se_tree_insert32(dlci_table, token, dlci_state);
+               }
+               dlci_state->service = sdp_data->service;
+       }
+       return 0;
+}
 
 void
 proto_reg_handoff_btrfcomm(void)
 {
        dissector_handle_t btrfcomm_handle;
 
-       btobex_handle = find_dissector("btobex");
+       btrfcomm_handle = find_dissector("btrfcomm");
+       dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_RFCOMM, btrfcomm_handle);
+
+       data_handle = find_dissector("data");
+
+       /* tap into the btsdp dissector to look for rfcomm channel infomation that
+          helps us determine the type of rfcomm payload, i.e. which service is
+          using the channels so we know which sub-dissector to call */
+       register_tap_listener("btsdp", NULL, NULL, TL_IS_DISSECTOR_HELPER, NULL, btrfcomm_sdp_tap_packet, NULL);
+}
+
+/* Bluetooth Handsfree (HF) profile dissection */
+static void
+dissect_bthf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       proto_item *ti;
+       proto_tree *st;
+
+       guint length = tvb_length(tvb);
+
+       col_set_str(pinfo->cinfo, COL_PROTOCOL, "HANDSFREE");
+
+       ti = proto_tree_add_item(tree, proto_bthf, tvb, 0, -1, ENC_BIG_ENDIAN);
+       st = proto_item_add_subtree(ti, ett_bthf);
+
+       col_add_fstr(pinfo->cinfo, COL_INFO, "%s \"%s\"",
+                               pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd", tvb_format_text(tvb, 0, length));
+
+       proto_tree_add_item(st, hf_at_cmd, tvb, 0, -1, ENC_LITTLE_ENDIAN);
+}
+
+void
+proto_register_bthf(void)
+{
+       static hf_register_info hf[] = {
+               {&hf_at_cmd,
+               {"AT Cmd", "bthf.atcmd",
+               FT_STRING, BASE_NONE, NULL, 0,
+               "AT Command", HFILL}
+               },
+       };
+
+       /* Setup protocol subtree array */
+       static gint *ett[] = {
+               &ett_bthf,
+       };
+
+       proto_bthf = proto_register_protocol("Bluetooth Handsfree Packet", "BTHF", "bthf");
+
+       /* Required function calls to register the header fields and subtrees used */
+       proto_register_field_array(proto_bthf, hf, array_length(hf));
+       proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_bthf(void)
+{
+       dissector_handle_t bthf_handle;
+
+       bthf_handle = create_dissector_handle(dissect_bthf, proto_bthf);
 
-    btrfcomm_handle = find_dissector("btrfcomm");
-       dissector_add("btl2cap.psm", BTL2CAP_PSM_RFCOMM, btrfcomm_handle);
+       dissector_add_uint("btrfcomm.service", BTSDP_HFP_SERVICE_UUID, bthf_handle);
+       dissector_add_uint("btrfcomm.service", BTSDP_HFP_GW_SERVICE_UUID, bthf_handle);
 }
 
+/* Bluetooth Dial-Up Networking (DUN) profile dissection */
+static void
+dissect_btdun(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       proto_item *ti;
+       proto_tree *st;
+       gboolean is_at_cmd;
+       guint i, length;
+
+       length = tvb_length(tvb);
+
+       col_set_str(pinfo->cinfo, COL_PROTOCOL, "DUN");
+
+       ti = proto_tree_add_item(tree, proto_btdun, tvb, 0, -1, ENC_BIG_ENDIAN);
+       st = proto_item_add_subtree(ti, ett_btdun);
+
+       is_at_cmd = TRUE;
+       for(i=0;i<length && is_at_cmd;i++) {
+               is_at_cmd = tvb_get_guint8(tvb, i) < 0x7d;
+       }
+
+       if( is_at_cmd) {
+               /* presumably an AT command */
+               col_add_fstr(pinfo->cinfo, COL_INFO, "%s \"%s\"",
+                       pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd", tvb_format_text(tvb, 0, length));
+
+              proto_tree_add_item(st, hf_dun_at_cmd, tvb, 0, -1, ENC_LITTLE_ENDIAN);
+       }
+       else {
+               /* ... or raw PPP */
+               if( ppp_handle )
+                       call_dissector(ppp_handle, tvb, pinfo, tree);
+               else {
+                       /* TODO: remove the above 'if' and this 'else-body' when "ppp_raw_hdlc" is available, requires that it is
+                           made non-anonymous in ppp dissector to use */
+                       col_set_str(pinfo->cinfo, COL_PROTOCOL, "PPP");
+                       col_add_fstr(pinfo->cinfo, COL_INFO, "%s <PPP frame>", pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd");
+
+                       call_dissector(data_handle, tvb, pinfo, tree);
+               }
+       }
+}
+
+void
+proto_register_btdun(void)
+{
+       static hf_register_info hf[] = {
+               {&hf_dun_at_cmd,
+               {"AT Cmd", "btdun.atcmd",
+               FT_STRING, BASE_NONE, NULL, 0,
+               "AT Command", HFILL}
+               },
+       };
+
+       /* Setup protocol subtree array */
+       static gint *ett[] = {
+               &ett_btdun,
+       };
+
+       proto_btdun = proto_register_protocol("Bluetooth DUN Packet", "BTDUN", "btdun");
+
+       /* Required function calls to register the header fields and subtrees used */
+       proto_register_field_array(proto_bthf, hf, array_length(hf));
+       proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_btdun(void)
+{
+       dissector_handle_t btdun_handle;
+
+       btdun_handle = create_dissector_handle(dissect_btdun, proto_btdun);
+
+       dissector_add_uint("btrfcomm.service", BTSDP_DUN_SERVICE_UUID, btdun_handle);
+
+       ppp_handle = find_dissector("ppp_raw_hdlc");
+}
+
+/* Bluetooth Serial Port profile (SPP) dissection */
+static void
+dissect_btspp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       proto_item *ti;
+       proto_tree *st;
+       gboolean ascii_only;
+       guint i, length = tvb_length(tvb);
+
+       col_set_str(pinfo->cinfo, COL_PROTOCOL, "SPP");
+
+       ti = proto_tree_add_item(tree, proto_btspp, tvb, 0, -1, ENC_BIG_ENDIAN);
+       st = proto_item_add_subtree(ti, ett_btspp);
+
+       length = MIN(length,60);
+       ascii_only = TRUE;
+       for(i=0;i<length && ascii_only;i++) {
+               ascii_only = tvb_get_guint8(tvb, i) < 0x80;
+       }
+
+       if(ascii_only) {
+               col_add_fstr(pinfo->cinfo, COL_INFO, "%s \"%s%s\"",
+                               pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd",
+                               tvb_format_text(tvb, 0, length),
+                               tvb_length(tvb) > length ? "...":"");
+       }
+
+       proto_tree_add_item(st, hf_data, tvb, 0, -1, ENC_NA);
+}
+
+void
+proto_register_btspp(void)
+{
+       static hf_register_info hf[] = {
+       {&hf_data,
+               {"Data", "btspp.data",
+               FT_BYTES, BASE_NONE, NULL, 0,
+               NULL, HFILL}},
+       };
+
+       /* Setup protocol subtree array */
+       static gint *ett[] = {
+               &ett_btspp,
+       };
+
+       proto_btspp = proto_register_protocol("Bluetooth SPP Packet", "BTSPP", "btspp");
+
+       /* Required function calls to register the header fields and subtrees used */
+       proto_register_field_array(proto_bthf, hf, array_length(hf));
+       proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_btspp(void)
+{
+       dissector_handle_t btspp_handle;
+
+       btspp_handle = create_dissector_handle(dissect_btspp, proto_btspp);
+
+       dissector_add_uint("btrfcomm.service", BTSDP_SPP_SERVICE_UUID, btspp_handle);
+}
+
+