| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com> |
| * Copyright (C) 2003-2011 Marcel Holtmann <marcel@holtmann.org> |
| * |
| * |
| * 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 St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <inttypes.h> |
| |
| #include "parser.h" |
| #include "sdp.h" |
| #include "l2cap.h" |
| #include "lib/hci.h" |
| #include "lib/a2mp.h" |
| #include "lib/amp.h" |
| |
| typedef struct { |
| uint16_t handle; |
| struct frame frm; |
| } handle_info; |
| #define HANDLE_TABLE_SIZE 10 |
| |
| static handle_info handle_table[HANDLE_TABLE_SIZE]; |
| |
| typedef struct { |
| uint16_t handle; |
| uint16_t cid; |
| uint16_t psm; |
| uint16_t num; |
| uint8_t mode; |
| uint8_t ext_ctrl; |
| } cid_info; |
| #define CID_TABLE_SIZE 20 |
| |
| static cid_info cid_table[2][CID_TABLE_SIZE]; |
| |
| #define SCID cid_table[0] |
| #define DCID cid_table[1] |
| |
| /* Can we move this to l2cap.h? */ |
| struct features { |
| char *name; |
| int flag; |
| }; |
| |
| static struct features l2cap_features[] = { |
| { "Flow control mode", L2CAP_FEAT_FLOWCTL }, |
| { "Retransmission mode", L2CAP_FEAT_RETRANS }, |
| { "Bi-directional QoS", L2CAP_FEAT_BIDIR_QOS }, |
| { "Enhanced Retransmission mode", L2CAP_FEAT_ERTM }, |
| { "Streaming mode", L2CAP_FEAT_STREAMING }, |
| { "FCS Option", L2CAP_FEAT_FCS }, |
| { "Extended Flow Specification", L2CAP_FEAT_EXT_FLOW }, |
| { "Fixed Channels", L2CAP_FEAT_FIXED_CHAN }, |
| { "Extended Window Size", L2CAP_FEAT_EXT_WINDOW }, |
| { "Unicast Connectless Data Reception", L2CAP_FEAT_UCD }, |
| { 0 } |
| }; |
| |
| static struct features l2cap_fix_chan[] = { |
| { "L2CAP Signalling Channel", L2CAP_FC_L2CAP }, |
| { "L2CAP Connless", L2CAP_FC_CONNLESS }, |
| { "AMP Manager Protocol", L2CAP_FC_A2MP }, |
| { 0 } |
| }; |
| |
| static struct frame *add_handle(uint16_t handle) |
| { |
| register handle_info *t = handle_table; |
| register int i; |
| |
| for (i = 0; i < HANDLE_TABLE_SIZE; i++) |
| if (!t[i].handle) { |
| t[i].handle = handle; |
| return &t[i].frm; |
| } |
| return NULL; |
| } |
| |
| static struct frame *get_frame(uint16_t handle) |
| { |
| register handle_info *t = handle_table; |
| register int i; |
| |
| for (i = 0; i < HANDLE_TABLE_SIZE; i++) |
| if (t[i].handle == handle) |
| return &t[i].frm; |
| |
| return add_handle(handle); |
| } |
| |
| static void add_cid(int in, uint16_t handle, uint16_t cid, uint16_t psm) |
| { |
| register cid_info *table = cid_table[in]; |
| register int i, pos = -1; |
| uint16_t num = 1; |
| |
| for (i = 0; i < CID_TABLE_SIZE; i++) { |
| if ((pos < 0 && !table[i].cid) || table[i].cid == cid) |
| pos = i; |
| if (table[i].psm == psm) |
| num++; |
| } |
| |
| if (pos >= 0) { |
| table[pos].handle = handle; |
| table[pos].cid = cid; |
| table[pos].psm = psm; |
| table[pos].num = num; |
| table[pos].mode = 0; |
| } |
| } |
| |
| static void del_cid(int in, uint16_t dcid, uint16_t scid) |
| { |
| register int t, i; |
| uint16_t cid[2]; |
| |
| if (!in) { |
| cid[0] = dcid; |
| cid[1] = scid; |
| } else { |
| cid[0] = scid; |
| cid[1] = dcid; |
| } |
| |
| for (t = 0; t < 2; t++) { |
| for (i = 0; i < CID_TABLE_SIZE; i++) |
| if (cid_table[t][i].cid == cid[t]) { |
| cid_table[t][i].handle = 0; |
| cid_table[t][i].cid = 0; |
| cid_table[t][i].psm = 0; |
| cid_table[t][i].num = 0; |
| cid_table[t][i].mode = 0; |
| break; |
| } |
| } |
| } |
| |
| static void del_handle(uint16_t handle) |
| { |
| register int t, i; |
| |
| for (t = 0; t < 2; t++) { |
| for (i = 0; i < CID_TABLE_SIZE; i++) |
| if (cid_table[t][i].handle == handle) { |
| cid_table[t][i].handle = 0; |
| cid_table[t][i].cid = 0; |
| cid_table[t][i].psm = 0; |
| cid_table[t][i].num = 0; |
| cid_table[t][i].mode = 0; |
| break; |
| } |
| } |
| } |
| static uint16_t get_psm(int in, uint16_t handle, uint16_t cid) |
| { |
| register cid_info *table = cid_table[in]; |
| register int i; |
| |
| for (i = 0; i < CID_TABLE_SIZE; i++) |
| if (table[i].handle == handle && table[i].cid == cid) |
| return table[i].psm; |
| return parser.defpsm; |
| } |
| |
| static uint16_t get_num(int in, uint16_t handle, uint16_t cid) |
| { |
| register cid_info *table = cid_table[in]; |
| register int i; |
| |
| for (i = 0; i < CID_TABLE_SIZE; i++) |
| if (table[i].handle == handle && table[i].cid == cid) |
| return table[i].num; |
| return 0; |
| } |
| |
| static void set_mode(int in, uint16_t handle, uint16_t cid, uint8_t mode) |
| { |
| register cid_info *table = cid_table[in]; |
| register int i; |
| |
| for (i = 0; i < CID_TABLE_SIZE; i++) |
| if (table[i].handle == handle && table[i].cid == cid) |
| table[i].mode = mode; |
| } |
| |
| static uint8_t get_mode(int in, uint16_t handle, uint16_t cid) |
| { |
| register cid_info *table = cid_table[in]; |
| register int i; |
| |
| for (i = 0; i < CID_TABLE_SIZE; i++) |
| if (table[i].handle == handle && table[i].cid == cid) |
| return table[i].mode; |
| return 0; |
| } |
| |
| static void set_ext_ctrl(int in, uint16_t handle, uint16_t cid, |
| uint8_t ext_ctrl) |
| { |
| register cid_info *table = cid_table[in]; |
| register int i; |
| |
| for (i = 0; i < CID_TABLE_SIZE; i++) |
| if (table[i].handle == handle && table[i].cid == cid) |
| table[i].ext_ctrl = ext_ctrl; |
| } |
| |
| static uint8_t get_ext_ctrl(int in, uint16_t handle, uint16_t cid) |
| { |
| register cid_info *table = cid_table[in]; |
| register int i; |
| |
| for (i = 0; i < CID_TABLE_SIZE; i++) |
| if (table[i].handle == handle && table[i].cid == cid) |
| return table[i].ext_ctrl; |
| return 0; |
| } |
| |
| static uint32_t get_val(uint8_t *ptr, uint8_t len) |
| { |
| switch (len) { |
| case 1: |
| return *ptr; |
| case 2: |
| return get_le16(ptr); |
| case 4: |
| return get_le32(ptr); |
| } |
| return 0; |
| } |
| |
| static char *reason2str(uint16_t reason) |
| { |
| switch (reason) { |
| case 0x0000: |
| return "Command not understood"; |
| case 0x0001: |
| return "Signalling MTU exceeded"; |
| case 0x0002: |
| return "Invalid CID in request"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *a2mpreason2str(uint16_t reason) |
| { |
| switch (reason) { |
| case A2MP_COMMAND_NOT_RECOGNIZED: |
| return "Command not recognized"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *connresult2str(uint16_t result) |
| { |
| switch (result) { |
| case 0x0000: |
| return "Connection successful"; |
| case 0x0001: |
| return "Connection pending"; |
| case 0x0002: |
| return "Connection refused - PSM not supported"; |
| case 0x0003: |
| return "Connection refused - security block"; |
| case 0x0004: |
| return "Connection refused - no resources available"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *status2str(uint16_t status) |
| { |
| switch (status) { |
| case 0x0000: |
| return "No futher information available"; |
| case 0x0001: |
| return "Authentication pending"; |
| case 0x0002: |
| return "Authorization pending"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *confresult2str(uint16_t result) |
| { |
| switch (result) { |
| case L2CAP_CONF_SUCCESS: |
| return "Success"; |
| case L2CAP_CONF_UNACCEPT: |
| return "Failure - unacceptable parameters"; |
| case L2CAP_CONF_REJECT: |
| return "Failure - rejected (no reason provided)"; |
| case L2CAP_CONF_UNKNOWN: |
| return "Failure - unknown options"; |
| case L2CAP_CONF_PENDING: |
| return "Pending"; |
| case L2CAP_CONF_EFS_REJECT: |
| return "Failure - flowspec reject"; |
| default: |
| return "Reserved"; |
| } |
| } |
| static char *inforesult2str(uint16_t result) |
| { |
| switch (result) { |
| case 0x0000: |
| return "Success"; |
| case 0x0001: |
| return "Not supported"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *type2str(uint8_t type) |
| { |
| switch (type) { |
| case L2CAP_SERVTYPE_NOTRAFFIC: |
| return "No traffic"; |
| case L2CAP_SERVTYPE_BESTEFFORT: |
| return "Best Effort"; |
| case L2CAP_SERVTYPE_GUARANTEED: |
| return "Guaranteed"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *mode2str(uint8_t mode) |
| { |
| switch (mode) { |
| case 0x00: |
| return "Basic"; |
| case 0x01: |
| return "Retransmission"; |
| case 0x02: |
| return "Flow control"; |
| case 0x03: |
| return "Enhanced Retransmission"; |
| case 0x04: |
| return "Streaming"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *fcs2str(uint8_t fcs) |
| { |
| switch (fcs) { |
| case 0x00: |
| return "No FCS"; |
| case 0x01: |
| return "CRC16 Check"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *sar2str(uint8_t sar) |
| { |
| switch (sar) { |
| case L2CAP_SAR_UNSEGMENTED: |
| return "Unsegmented"; |
| case L2CAP_SAR_START: |
| return "Start"; |
| case L2CAP_SAR_END: |
| return "End"; |
| case L2CAP_SAR_CONTINUE: |
| return "Continuation"; |
| default: |
| return "Bad SAR"; |
| |
| } |
| } |
| |
| static char *supervisory2str(uint8_t supervisory) |
| { |
| switch (supervisory) { |
| case L2CAP_SUPER_RR: |
| return "Receiver Ready (RR)"; |
| case L2CAP_SUPER_REJ: |
| return "Reject (REJ)"; |
| case L2CAP_SUPER_RNR: |
| return "Receiver Not Ready (RNR)"; |
| case L2CAP_SUPER_SREJ: |
| return "Select Reject (SREJ)"; |
| default: |
| return "Bad Supervisory"; |
| } |
| } |
| |
| static char *ampctrltype2str(uint8_t type) |
| { |
| switch (type) { |
| case HCI_BREDR: |
| return "BR-EDR"; |
| case HCI_AMP: |
| return "802.11 AMP"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *ampctrlstatus2str(uint8_t status) |
| { |
| switch (status) { |
| case AMP_CTRL_POWERED_DOWN: |
| return "Powered down"; |
| case AMP_CTRL_BLUETOOTH_ONLY: |
| return "Bluetooth only"; |
| case AMP_CTRL_NO_CAPACITY: |
| return "No capacity"; |
| case AMP_CTRL_LOW_CAPACITY: |
| return "Low capacity"; |
| case AMP_CTRL_MEDIUM_CAPACITY: |
| return "Medium capacity"; |
| case AMP_CTRL_HIGH_CAPACITY: |
| return "High capacity"; |
| case AMP_CTRL_FULL_CAPACITY: |
| return "Full capacity"; |
| default: |
| return "Reserved"; |
| |
| } |
| } |
| |
| static char *a2mpstatus2str(uint8_t status) |
| { |
| switch (status) { |
| case A2MP_STATUS_SUCCESS: |
| return "Success"; |
| case A2MP_STATUS_INVALID_CTRL_ID: |
| return "Invalid Controller ID"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *a2mpcplstatus2str(uint8_t status) |
| { |
| switch (status) { |
| case A2MP_STATUS_SUCCESS: |
| return "Success"; |
| case A2MP_STATUS_INVALID_CTRL_ID: |
| return "Invalid Controller ID"; |
| case A2MP_STATUS_UNABLE_START_LINK_CREATION: |
| return "Failed - Unable to start link creation"; |
| case A2MP_STATUS_COLLISION_OCCURED: |
| return "Failed - Collision occured"; |
| case A2MP_STATUS_DISCONN_REQ_RECVD: |
| return "Failed - Disconnect physical link received"; |
| case A2MP_STATUS_PHYS_LINK_EXISTS: |
| return "Failed - Physical link already exists"; |
| case A2MP_STATUS_SECURITY_VIOLATION: |
| return "Failed - Security violation"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static char *a2mpdplstatus2str(uint8_t status) |
| { |
| switch (status) { |
| case A2MP_STATUS_SUCCESS: |
| return "Success"; |
| case A2MP_STATUS_INVALID_CTRL_ID: |
| return "Invalid Controller ID"; |
| case A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS: |
| return "Failed - No Physical Link exists"; |
| default: |
| return "Reserved"; |
| } |
| } |
| |
| static inline void command_rej(int level, struct frame *frm) |
| { |
| l2cap_cmd_rej *h = frm->ptr; |
| uint16_t reason = btohs(h->reason); |
| uint32_t cid; |
| |
| printf("Command rej: reason %d", reason); |
| |
| switch (reason) { |
| case 0x0001: |
| printf(" mtu %d\n", get_val(frm->ptr + L2CAP_CMD_REJ_SIZE, 2)); |
| break; |
| case 0x0002: |
| cid = get_val(frm->ptr + L2CAP_CMD_REJ_SIZE, 4); |
| printf(" dcid 0x%4.4x scid 0x%4.4x\n", cid & 0xffff, cid >> 16); |
| break; |
| default: |
| printf("\n"); |
| break; |
| } |
| |
| p_indent(level + 1, frm); |
| printf("%s\n", reason2str(reason)); |
| } |
| |
| static inline void conn_req(int level, struct frame *frm) |
| { |
| l2cap_conn_req *h = frm->ptr; |
| uint16_t psm = btohs(h->psm); |
| uint16_t scid = btohs(h->scid); |
| |
| add_cid(frm->in, frm->handle, scid, psm); |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Connect req: psm %d scid 0x%4.4x\n", psm, scid); |
| } |
| |
| static inline void conn_rsp(int level, struct frame *frm) |
| { |
| l2cap_conn_rsp *h = frm->ptr; |
| uint16_t scid = btohs(h->scid); |
| uint16_t dcid = btohs(h->dcid); |
| uint16_t result = btohs(h->result); |
| uint16_t status = btohs(h->status); |
| uint16_t psm; |
| |
| switch (h->result) { |
| case L2CAP_CR_SUCCESS: |
| if ((psm = get_psm(!frm->in, frm->handle, scid))) |
| add_cid(frm->in, frm->handle, dcid, psm); |
| break; |
| |
| case L2CAP_CR_PEND: |
| break; |
| |
| default: |
| del_cid(frm->in, dcid, scid); |
| break; |
| } |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Connect rsp: dcid 0x%4.4x scid 0x%4.4x result %d status %d\n", |
| dcid, scid, result, status); |
| |
| p_indent(level + 1, frm); |
| printf("%s", connresult2str(result)); |
| |
| if (result == 0x0001) |
| printf(" - %s\n", status2str(status)); |
| else |
| printf("\n"); |
| } |
| |
| static void conf_rfc(void *ptr, int len, int in, uint16_t handle, |
| uint16_t cid) |
| { |
| uint8_t mode; |
| |
| mode = *((uint8_t *) ptr); |
| set_mode(!in, handle, cid, mode); |
| |
| printf("RFC 0x%02x (%s", mode, mode2str(mode)); |
| if (mode >= 0x01 && mode <= 0x04) { |
| uint8_t txwin, maxtrans; |
| uint16_t rto, mto, mps; |
| txwin = *((uint8_t *) (ptr + 1)); |
| maxtrans = *((uint8_t *) (ptr + 2)); |
| rto = get_le16(ptr + 3); |
| mto = get_le16(ptr + 5); |
| mps = get_le16(ptr + 7); |
| printf(", TxWin %d, MaxTx %d, RTo %d, MTo %d, MPS %d", |
| txwin, maxtrans, rto, mto, mps); |
| } |
| printf(")"); |
| } |
| |
| static void conf_efs(void *ptr) |
| { |
| uint8_t id, ser_type; |
| uint16_t max_sdu; |
| uint32_t sdu_itime, access_lat, flush_to; |
| |
| id = get_val(ptr, sizeof(id)); |
| ser_type = get_val(ptr + 1, sizeof(ser_type)); |
| max_sdu = get_val(ptr + 2, sizeof(max_sdu)); |
| sdu_itime = get_val(ptr + 4, sizeof(sdu_itime)); |
| access_lat = get_val(ptr + 8, sizeof(access_lat)); |
| flush_to = get_val(ptr + 12, sizeof(flush_to)); |
| |
| printf("EFS (Id 0x%02x, SerType %s, MaxSDU 0x%04x, SDUitime 0x%08x, " |
| "AccLat 0x%08x, FlushTO 0x%08x)", |
| id, type2str(ser_type), max_sdu, sdu_itime, |
| access_lat, flush_to); |
| } |
| |
| static void conf_fcs(void *ptr, int len) |
| { |
| uint8_t fcs; |
| |
| fcs = *((uint8_t *) ptr); |
| printf("FCS Option"); |
| if (len > 0) |
| printf(" 0x%2.2x (%s)", fcs, fcs2str(fcs)); |
| } |
| |
| static void conf_opt(int level, void *ptr, int len, int in, uint16_t handle, |
| uint16_t cid) |
| { |
| int indent = 0; |
| p_indent(level, 0); |
| while (len > 0) { |
| l2cap_conf_opt *h = ptr; |
| |
| ptr += L2CAP_CONF_OPT_SIZE + h->len; |
| len -= L2CAP_CONF_OPT_SIZE + h->len; |
| |
| if (h->type & 0x80) |
| printf("["); |
| |
| if (indent++) { |
| printf("\n"); |
| p_indent(level, 0); |
| } |
| |
| switch (h->type & 0x7f) { |
| case L2CAP_CONF_MTU: |
| set_mode(in, handle, cid, 0x00); |
| printf("MTU"); |
| if (h->len > 0) |
| printf(" %d", get_val(h->val, h->len)); |
| break; |
| |
| case L2CAP_CONF_FLUSH_TO: |
| printf("FlushTO"); |
| if (h->len > 0) |
| printf(" %d", get_val(h->val, h->len)); |
| break; |
| |
| case L2CAP_CONF_QOS: |
| printf("QoS"); |
| if (h->len > 0) |
| printf(" 0x%02x (%s)", *(h->val + 1), type2str(*(h->val + 1))); |
| break; |
| |
| case L2CAP_CONF_RFC: |
| conf_rfc(h->val, h->len, in, handle, cid); |
| break; |
| |
| case L2CAP_CONF_FCS: |
| conf_fcs(h->val, h->len); |
| break; |
| |
| case L2CAP_CONF_EFS: |
| conf_efs(h->val); |
| break; |
| |
| case L2CAP_CONF_EWS: |
| printf("EWS"); |
| if (h->len > 0) |
| printf(" %d", get_val(h->val, h->len)); |
| set_ext_ctrl(in, handle, cid, 1); |
| break; |
| |
| default: |
| printf("Unknown (type %2.2x, len %d)", h->type & 0x7f, h->len); |
| break; |
| } |
| |
| if (h->type & 0x80) |
| printf("] "); |
| else |
| printf(" "); |
| } |
| printf("\n"); |
| } |
| |
| static void conf_list(int level, uint8_t *list, int len) |
| { |
| int i; |
| |
| p_indent(level, 0); |
| for (i = 0; i < len; i++) { |
| switch (list[i] & 0x7f) { |
| case L2CAP_CONF_MTU: |
| printf("MTU "); |
| break; |
| case L2CAP_CONF_FLUSH_TO: |
| printf("FlushTo "); |
| break; |
| case L2CAP_CONF_QOS: |
| printf("QoS "); |
| break; |
| case L2CAP_CONF_RFC: |
| printf("RFC "); |
| break; |
| case L2CAP_CONF_FCS: |
| printf("FCS "); |
| break; |
| case L2CAP_CONF_EFS: |
| printf("EFS "); |
| break; |
| case L2CAP_CONF_EWS: |
| printf("EWS "); |
| break; |
| default: |
| printf("%2.2x ", list[i] & 0x7f); |
| break; |
| } |
| } |
| printf("\n"); |
| } |
| |
| static inline void conf_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_conf_req *h = frm->ptr; |
| uint16_t dcid = btohs(h->dcid); |
| int clen = btohs(cmd->len) - L2CAP_CONF_REQ_SIZE; |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Config req: dcid 0x%4.4x flags 0x%2.2x clen %d\n", |
| dcid, btohs(h->flags), clen); |
| |
| if (clen > 0) |
| conf_opt(level + 1, h->data, clen, frm->in, frm->handle, |
| dcid); |
| } |
| |
| static inline void conf_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_conf_rsp *h = frm->ptr; |
| uint16_t scid = btohs(h->scid); |
| uint16_t result = btohs(h->result); |
| int clen = btohs(cmd->len) - L2CAP_CONF_RSP_SIZE; |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Config rsp: scid 0x%4.4x flags 0x%2.2x result %d clen %d\n", |
| scid, btohs(h->flags), result, clen); |
| |
| if (clen > 0) { |
| if (result) { |
| p_indent(level + 1, frm); |
| printf("%s\n", confresult2str(result)); |
| } |
| if (result == 0x0003) |
| conf_list(level + 1, h->data, clen); |
| else |
| conf_opt(level + 1, h->data, clen, frm->in, |
| frm->handle, scid); |
| } else { |
| p_indent(level + 1, frm); |
| printf("%s\n", confresult2str(result)); |
| } |
| } |
| |
| static inline void disconn_req(int level, struct frame *frm) |
| { |
| l2cap_disconn_req *h = frm->ptr; |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Disconn req: dcid 0x%4.4x scid 0x%4.4x\n", |
| btohs(h->dcid), btohs(h->scid)); |
| } |
| |
| static inline void disconn_rsp(int level, struct frame *frm) |
| { |
| l2cap_disconn_rsp *h = frm->ptr; |
| uint16_t dcid = btohs(h->dcid); |
| uint16_t scid = btohs(h->scid); |
| |
| del_cid(frm->in, dcid, scid); |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Disconn rsp: dcid 0x%4.4x scid 0x%4.4x\n", |
| btohs(h->dcid), btohs(h->scid)); |
| } |
| |
| static inline void echo_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Echo req: dlen %d\n", btohs(cmd->len)); |
| raw_dump(level, frm); |
| } |
| |
| static inline void echo_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Echo rsp: dlen %d\n", btohs(cmd->len)); |
| raw_dump(level, frm); |
| } |
| |
| static void info_opt(int level, int type, void *ptr, int len) |
| { |
| uint32_t mask; |
| uint64_t fc_mask; |
| int i; |
| |
| p_indent(level, 0); |
| |
| switch (type) { |
| case 0x0001: |
| printf("Connectionless MTU %d\n", get_val(ptr, len)); |
| break; |
| case 0x0002: |
| mask = get_val(ptr, len); |
| printf("Extended feature mask 0x%4.4x\n", mask); |
| if (parser.flags & DUMP_VERBOSE) |
| for (i=0; l2cap_features[i].name; i++) |
| if (mask & l2cap_features[i].flag) { |
| p_indent(level + 1, 0); |
| printf("%s\n", l2cap_features[i].name); |
| } |
| break; |
| case 0x0003: |
| fc_mask = get_le64(ptr); |
| printf("Fixed channel list 0x%8.8" PRIx64 "\n", fc_mask); |
| if (parser.flags & DUMP_VERBOSE) |
| for (i=0; l2cap_fix_chan[i].name; i++) |
| if (fc_mask & l2cap_fix_chan[i].flag) { |
| p_indent(level + 1, 0); |
| printf("%s\n", l2cap_fix_chan[i].name); |
| } |
| break; |
| default: |
| printf("Unknown (len %d)\n", len); |
| break; |
| } |
| } |
| |
| static inline void info_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_info_req *h = frm->ptr; |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Info req: type %d\n", btohs(h->type)); |
| } |
| |
| static inline void info_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_info_rsp *h = frm->ptr; |
| uint16_t type = btohs(h->type); |
| uint16_t result = btohs(h->result); |
| int ilen = btohs(cmd->len) - L2CAP_INFO_RSP_SIZE; |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Info rsp: type %d result %d\n", type, result); |
| |
| if (ilen > 0) { |
| info_opt(level + 1, type, h->data, ilen); |
| } else { |
| p_indent(level + 1, frm); |
| printf("%s\n", inforesult2str(result)); |
| } |
| } |
| |
| static void l2cap_ctrl_ext_parse(int level, struct frame *frm, uint32_t ctrl) |
| { |
| p_indent(level, frm); |
| |
| printf("%s:", ctrl & L2CAP_EXT_CTRL_FRAME_TYPE ? "S-frame" : "I-frame"); |
| |
| if (ctrl & L2CAP_EXT_CTRL_FRAME_TYPE) { |
| printf(" %s", supervisory2str((ctrl & L2CAP_EXT_CTRL_SUPERVISE_MASK) >> |
| L2CAP_EXT_CTRL_SUPER_SHIFT)); |
| |
| if (ctrl & L2CAP_EXT_CTRL_POLL) |
| printf(" P-bit"); |
| } else { |
| uint8_t sar = (ctrl & L2CAP_EXT_CTRL_SAR_MASK) >> |
| L2CAP_EXT_CTRL_SAR_SHIFT; |
| printf(" %s", sar2str(sar)); |
| if (sar == L2CAP_SAR_START) { |
| uint16_t len; |
| len = get_le16(frm->ptr); |
| frm->ptr += L2CAP_SDULEN_SIZE; |
| frm->len -= L2CAP_SDULEN_SIZE; |
| printf(" (len %d)", len); |
| } |
| printf(" TxSeq %d", (ctrl & L2CAP_EXT_CTRL_TXSEQ_MASK) >> |
| L2CAP_EXT_CTRL_TXSEQ_SHIFT); |
| } |
| |
| printf(" ReqSeq %d", (ctrl & L2CAP_EXT_CTRL_REQSEQ_MASK) >> |
| L2CAP_EXT_CTRL_REQSEQ_SHIFT); |
| |
| if (ctrl & L2CAP_EXT_CTRL_FINAL) |
| printf(" F-bit"); |
| } |
| |
| static void l2cap_ctrl_parse(int level, struct frame *frm, uint32_t ctrl) |
| { |
| p_indent(level, frm); |
| |
| printf("%s:", ctrl & L2CAP_CTRL_FRAME_TYPE ? "S-frame" : "I-frame"); |
| |
| if (ctrl & 0x01) { |
| printf(" %s", supervisory2str((ctrl & L2CAP_CTRL_SUPERVISE_MASK) >> |
| L2CAP_CTRL_SUPER_SHIFT)); |
| |
| if (ctrl & L2CAP_CTRL_POLL) |
| printf(" P-bit"); |
| } else { |
| uint8_t sar = (ctrl & L2CAP_CTRL_SAR_MASK) >> L2CAP_CTRL_SAR_SHIFT; |
| printf(" %s", sar2str(sar)); |
| if (sar == L2CAP_SAR_START) { |
| uint16_t len; |
| len = get_le16(frm->ptr); |
| frm->ptr += L2CAP_SDULEN_SIZE; |
| frm->len -= L2CAP_SDULEN_SIZE; |
| printf(" (len %d)", len); |
| } |
| printf(" TxSeq %d", (ctrl & L2CAP_CTRL_TXSEQ_MASK) >> L2CAP_CTRL_TXSEQ_SHIFT); |
| } |
| |
| printf(" ReqSeq %d", (ctrl & L2CAP_CTRL_REQSEQ_MASK) >> L2CAP_CTRL_REQSEQ_SHIFT); |
| |
| if (ctrl & L2CAP_CTRL_FINAL) |
| printf(" F-bit"); |
| } |
| |
| static inline void create_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_create_req *h = frm->ptr; |
| uint16_t psm = btohs(h->psm); |
| uint16_t scid = btohs(h->scid); |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Create chan req: psm 0x%4.4x scid 0x%4.4x ctrl id %d\n", |
| psm, scid, h->id); |
| } |
| |
| static inline void create_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_create_rsp *h = frm->ptr; |
| uint16_t scid = btohs(h->scid); |
| uint16_t dcid = btohs(h->dcid); |
| uint16_t result = btohs(h->result); |
| uint16_t status = btohs(h->status); |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Create chan rsp: dcid 0x%4.4x scid 0x%4.4x result %d status %d\n", |
| dcid, scid, result, status); |
| } |
| |
| static inline void move_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_move_req *h = frm->ptr; |
| uint16_t icid = btohs(h->icid); |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Move chan req: icid 0x%4.4x ctrl id %d\n", icid, h->id); |
| } |
| |
| static inline void move_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_move_rsp *h = frm->ptr; |
| uint16_t icid = btohs(h->icid); |
| uint16_t result = btohs(h->result); |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Move chan rsp: icid 0x%4.4x result %d\n", icid, result); |
| } |
| |
| static inline void move_cfm(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_move_cfm *h = frm->ptr; |
| uint16_t icid = btohs(h->icid); |
| uint16_t result = btohs(h->result); |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Move chan cfm: icid 0x%4.4x result %d\n", icid, result); |
| } |
| |
| static inline void move_cfm_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm) |
| { |
| l2cap_move_cfm_rsp *h = frm->ptr; |
| uint16_t icid = btohs(h->icid); |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| printf("Move chan cfm rsp: icid 0x%4.4x\n", icid); |
| } |
| |
| static inline void a2mp_command_rej(int level, struct frame *frm) |
| { |
| struct a2mp_command_rej *h = frm->ptr; |
| uint16_t reason = btohs(h->reason); |
| |
| printf("Command Reject: reason %d\n", reason); |
| p_indent(level + 1, 0); |
| printf("%s\n", a2mpreason2str(reason)); |
| } |
| |
| static inline void a2mp_discover_req(int level, struct frame *frm, uint16_t len) |
| { |
| struct a2mp_discover_req *h = frm->ptr; |
| uint16_t mtu = btohs(h->mtu); |
| uint8_t *octet = (uint8_t *)&(h->mask); |
| uint16_t mask; |
| uint8_t extension; |
| |
| printf("Discover req: mtu/mps %d ", mtu); |
| len -= 2; |
| |
| printf("mask:"); |
| |
| do { |
| len -= 2; |
| mask = get_le16(octet); |
| printf(" 0x%4.4x", mask); |
| |
| extension = octet[1] & 0x80; |
| octet += 2; |
| } while ((extension != 0) && (len >= 2)); |
| |
| printf("\n"); |
| } |
| |
| static inline void a2mp_ctrl_list_dump(int level, struct a2mp_ctrl *list, uint16_t len) |
| { |
| p_indent(level, 0); |
| printf("Controller list:\n"); |
| |
| while (len >= 3) { |
| p_indent(level + 1, 0); |
| printf("id %d type %d (%s) status 0x%2.2x (%s)\n", |
| list->id, list->type, ampctrltype2str(list->type), list->status, ampctrlstatus2str(list->status)); |
| list++; |
| len -= 3; |
| } |
| |
| } |
| |
| static inline void a2mp_discover_rsp(int level, struct frame *frm, uint16_t len) |
| { |
| struct a2mp_discover_rsp *h = frm->ptr; |
| uint16_t mtu = btohs(h->mtu); |
| uint8_t *octet = (uint8_t *)&(h->mask); |
| uint16_t mask; |
| uint8_t extension; |
| |
| printf("Discover rsp: mtu/mps %d ", mtu); |
| len -= 2; |
| |
| printf("mask:"); |
| |
| do { |
| len -= 2; |
| mask = get_le16(octet); |
| printf(" 0x%4.4x", mask); |
| |
| extension = octet[1] & 0x80; |
| octet += 2; |
| } while ((extension != 0) && (len >= 2)); |
| |
| printf("\n"); |
| |
| if (len >= 3) { |
| a2mp_ctrl_list_dump(level + 1, (struct a2mp_ctrl *) octet, len); |
| } |
| } |
| |
| static inline void a2mp_change_notify(int level, struct frame *frm, uint16_t len) |
| { |
| struct a2mp_ctrl *list = frm->ptr; |
| |
| printf("Change Notify\n"); |
| |
| if (len >= 3) { |
| a2mp_ctrl_list_dump(level + 1, list, len); |
| } |
| } |
| |
| static inline void a2mp_change_rsp(int level, struct frame *frm) |
| { |
| printf("Change Response\n"); |
| } |
| |
| static inline void a2mp_info_req(int level, struct frame *frm) |
| { |
| struct a2mp_info_req *h = frm->ptr; |
| |
| printf("Get Info req: id %d\n", h->id); |
| } |
| |
| static inline void a2mp_info_rsp(int level, struct frame *frm) |
| { |
| struct a2mp_info_rsp *h = frm->ptr; |
| |
| printf("Get Info rsp: id %d status %d (%s)\n", |
| h->id, h->status, a2mpstatus2str(h->status)); |
| |
| p_indent(level + 1, 0); |
| printf("Total bandwidth %d\n", btohl(h->total_bw)); |
| p_indent(level + 1, 0); |
| printf("Max guaranteed bandwidth %d\n", btohl(h->max_bw)); |
| p_indent(level + 1, 0); |
| printf("Min latency %d\n", btohl(h->min_latency)); |
| p_indent(level + 1, 0); |
| printf("Pal capabilities 0x%4.4x\n", btohs(h->pal_caps)); |
| p_indent(level + 1, 0); |
| printf("Assoc size %d\n", btohs(h->assoc_size)); |
| } |
| |
| static inline void a2mp_assoc_req(int level, struct frame *frm) |
| { |
| struct a2mp_assoc_req *h = frm->ptr; |
| |
| printf("Get AMP Assoc req: id %d\n", h->id); |
| } |
| |
| static inline void a2mp_assoc_rsp(int level, struct frame *frm, uint16_t len) |
| { |
| struct a2mp_assoc_rsp *h = frm->ptr; |
| |
| printf("Get AMP Assoc rsp: id %d status (%d) %s\n", |
| h->id, h->status, a2mpstatus2str(h->status)); |
| amp_assoc_dump(level + 1, h->assoc_data, len - sizeof(*h)); |
| } |
| |
| static inline void a2mp_create_req(int level, struct frame *frm, uint16_t len) |
| { |
| struct a2mp_create_req *h = frm->ptr; |
| |
| printf("Create Physical Link req: local id %d remote id %d\n", |
| h->local_id, h->remote_id); |
| amp_assoc_dump(level + 1, h->assoc_data, len - sizeof(*h)); |
| } |
| |
| static inline void a2mp_create_rsp(int level, struct frame *frm) |
| { |
| struct a2mp_create_rsp *h = frm->ptr; |
| |
| printf("Create Physical Link rsp: local id %d remote id %d status %d\n", |
| h->local_id, h->remote_id, h->status); |
| p_indent(level+1, 0); |
| printf("%s\n", a2mpcplstatus2str(h->status)); |
| } |
| |
| static inline void a2mp_disconn_req(int level, struct frame *frm) |
| { |
| struct a2mp_disconn_req *h = frm->ptr; |
| |
| printf("Disconnect Physical Link req: local id %d remote id %d\n", |
| h->local_id, h->remote_id); |
| } |
| |
| static inline void a2mp_disconn_rsp(int level, struct frame *frm) |
| { |
| struct a2mp_disconn_rsp *h = frm->ptr; |
| |
| printf("Disconnect Physical Link rsp: local id %d remote id %d status %d\n", |
| h->local_id, h->remote_id, h->status); |
| p_indent(level+1, 0); |
| printf("%s\n", a2mpdplstatus2str(h->status)); |
| } |
| |
| static void l2cap_parse(int level, struct frame *frm) |
| { |
| l2cap_hdr *hdr = (void *)frm->ptr; |
| uint16_t dlen = btohs(hdr->len); |
| uint16_t cid = btohs(hdr->cid); |
| uint16_t psm; |
| |
| frm->ptr += L2CAP_HDR_SIZE; |
| frm->len -= L2CAP_HDR_SIZE; |
| |
| if (cid == 0x1) { |
| /* Signaling channel */ |
| |
| while (frm->len >= L2CAP_CMD_HDR_SIZE) { |
| l2cap_cmd_hdr *hdr = frm->ptr; |
| |
| frm->ptr += L2CAP_CMD_HDR_SIZE; |
| frm->len -= L2CAP_CMD_HDR_SIZE; |
| |
| if (!p_filter(FILT_L2CAP)) { |
| p_indent(level, frm); |
| printf("L2CAP(s): "); |
| } |
| |
| switch (hdr->code) { |
| case L2CAP_COMMAND_REJ: |
| command_rej(level, frm); |
| break; |
| |
| case L2CAP_CONN_REQ: |
| conn_req(level, frm); |
| break; |
| |
| case L2CAP_CONN_RSP: |
| conn_rsp(level, frm); |
| break; |
| |
| case L2CAP_CONF_REQ: |
| conf_req(level, hdr, frm); |
| break; |
| |
| case L2CAP_CONF_RSP: |
| conf_rsp(level, hdr, frm); |
| break; |
| |
| case L2CAP_DISCONN_REQ: |
| disconn_req(level, frm); |
| break; |
| |
| case L2CAP_DISCONN_RSP: |
| disconn_rsp(level, frm); |
| break; |
| |
| case L2CAP_ECHO_REQ: |
| echo_req(level, hdr, frm); |
| break; |
| |
| case L2CAP_ECHO_RSP: |
| echo_rsp(level, hdr, frm); |
| break; |
| |
| case L2CAP_INFO_REQ: |
| info_req(level, hdr, frm); |
| break; |
| |
| case L2CAP_INFO_RSP: |
| info_rsp(level, hdr, frm); |
| break; |
| |
| case L2CAP_CREATE_REQ: |
| create_req(level, hdr, frm); |
| break; |
| |
| case L2CAP_CREATE_RSP: |
| create_rsp(level, hdr, frm); |
| break; |
| |
| case L2CAP_MOVE_REQ: |
| move_req(level, hdr, frm); |
| break; |
| |
| case L2CAP_MOVE_RSP: |
| move_rsp(level, hdr, frm); |
| break; |
| |
| case L2CAP_MOVE_CFM: |
| move_cfm(level, hdr, frm); |
| break; |
| |
| case L2CAP_MOVE_CFM_RSP: |
| move_cfm_rsp(level, hdr, frm); |
| break; |
| |
| default: |
| if (p_filter(FILT_L2CAP)) |
| break; |
| printf("code 0x%2.2x ident %d len %d\n", |
| hdr->code, hdr->ident, btohs(hdr->len)); |
| raw_dump(level, frm); |
| } |
| |
| if (frm->len > btohs(hdr->len)) { |
| frm->len -= btohs(hdr->len); |
| frm->ptr += btohs(hdr->len); |
| } else |
| frm->len = 0; |
| } |
| } else if (cid == 0x2) { |
| /* Connectionless channel */ |
| |
| if (p_filter(FILT_L2CAP)) |
| return; |
| |
| psm = get_le16(frm->ptr); |
| frm->ptr += 2; |
| frm->len -= 2; |
| |
| p_indent(level, frm); |
| printf("L2CAP(c): len %d psm %d\n", dlen, psm); |
| raw_dump(level, frm); |
| } else if (cid == 0x3) { |
| /* AMP Manager channel */ |
| |
| if (p_filter(FILT_A2MP)) |
| return; |
| |
| /* Adjust for ERTM control bytes */ |
| frm->ptr += 2; |
| frm->len -= 2; |
| |
| while (frm->len >= A2MP_HDR_SIZE) { |
| struct a2mp_hdr *hdr = frm->ptr; |
| |
| frm->ptr += A2MP_HDR_SIZE; |
| frm->len -= A2MP_HDR_SIZE; |
| |
| p_indent(level, frm); |
| printf("A2MP: "); |
| |
| switch (hdr->code) { |
| case A2MP_COMMAND_REJ: |
| a2mp_command_rej(level, frm); |
| break; |
| case A2MP_DISCOVER_REQ: |
| a2mp_discover_req(level, frm, btohs(hdr->len)); |
| break; |
| case A2MP_DISCOVER_RSP: |
| a2mp_discover_rsp(level, frm, btohs(hdr->len)); |
| break; |
| case A2MP_CHANGE_NOTIFY: |
| a2mp_change_notify(level, frm, btohs(hdr->len)); |
| break; |
| case A2MP_CHANGE_RSP: |
| a2mp_change_rsp(level, frm); |
| break; |
| case A2MP_INFO_REQ: |
| a2mp_info_req(level, frm); |
| break; |
| case A2MP_INFO_RSP: |
| a2mp_info_rsp(level, frm); |
| break; |
| case A2MP_ASSOC_REQ: |
| a2mp_assoc_req(level, frm); |
| break; |
| case A2MP_ASSOC_RSP: |
| a2mp_assoc_rsp(level, frm, btohs(hdr->len)); |
| break; |
| case A2MP_CREATE_REQ: |
| a2mp_create_req(level, frm, btohs(hdr->len)); |
| break; |
| case A2MP_CREATE_RSP: |
| a2mp_create_rsp(level, frm); |
| break; |
| case A2MP_DISCONN_REQ: |
| a2mp_disconn_req(level, frm); |
| break; |
| case A2MP_DISCONN_RSP: |
| a2mp_disconn_rsp(level, frm); |
| break; |
| default: |
| printf("code 0x%2.2x ident %d len %d\n", |
| hdr->code, hdr->ident, btohs(hdr->len)); |
| raw_dump(level, frm); |
| } |
| if (frm->len > btohs(hdr->len)) { |
| frm->len -= btohs(hdr->len); |
| frm->ptr += btohs(hdr->len); |
| } else |
| frm->len = 0; |
| } |
| } else if (cid == 0x04) { |
| if (!p_filter(FILT_ATT)) |
| att_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| } else if (cid == 0x06) { |
| if (!p_filter(FILT_SMP)) |
| smp_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| } else { |
| /* Connection oriented channel */ |
| |
| uint8_t mode = get_mode(!frm->in, frm->handle, cid); |
| uint8_t ext_ctrl = get_ext_ctrl(!frm->in, frm->handle, cid); |
| uint16_t psm = get_psm(!frm->in, frm->handle, cid); |
| uint16_t fcs = 0; |
| uint32_t proto, ctrl = 0; |
| |
| frm->cid = cid; |
| frm->num = get_num(!frm->in, frm->handle, cid); |
| |
| if (mode > 0) { |
| if (ext_ctrl) { |
| ctrl = get_val(frm->ptr, 4); |
| frm->ptr += 4; |
| frm->len -= 6; |
| } else { |
| ctrl = get_val(frm->ptr, 2); |
| frm->ptr += 2; |
| frm->len -= 4; |
| } |
| fcs = get_le16(frm->ptr + frm->len); |
| } |
| |
| if (!p_filter(FILT_L2CAP)) { |
| p_indent(level, frm); |
| printf("L2CAP(d): cid 0x%4.4x len %d", cid, dlen); |
| if (mode > 0) { |
| if (ext_ctrl) |
| printf(" ext_ctrl 0x%8.8x fcs 0x%4.4x", ctrl, fcs); |
| else |
| printf(" ctrl 0x%4.4x fcs 0x%4.4x", ctrl, fcs); |
| } |
| |
| printf(" [psm %d]\n", psm); |
| level++; |
| if (mode > 0) { |
| if (ext_ctrl) |
| l2cap_ctrl_ext_parse(level, frm, ctrl); |
| else |
| l2cap_ctrl_parse(level, frm, ctrl); |
| |
| printf("\n"); |
| } |
| } |
| |
| switch (psm) { |
| case 0x01: |
| if (!p_filter(FILT_SDP)) |
| sdp_dump(level + 1, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| case 0x03: |
| if (!p_filter(FILT_RFCOMM)) |
| rfcomm_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| case 0x0f: |
| if (!p_filter(FILT_BNEP)) |
| bnep_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| case 0x11: |
| case 0x13: |
| if (!p_filter(FILT_HIDP)) |
| hidp_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| case 0x17: |
| case 0x1B: |
| if (!p_filter(FILT_AVCTP)) |
| avctp_dump(level, frm, psm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| case 0x19: |
| if (!p_filter(FILT_AVDTP)) |
| avdtp_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| case 0x1f: |
| if (!p_filter(FILT_ATT)) |
| att_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| default: |
| proto = get_proto(frm->handle, psm, 0); |
| |
| switch (proto) { |
| case SDP_UUID_CMTP: |
| if (!p_filter(FILT_CMTP)) |
| cmtp_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| case SDP_UUID_HARDCOPY_CONTROL_CHANNEL: |
| if (!p_filter(FILT_HCRP)) |
| hcrp_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| case SDP_UUID_OBEX: |
| if (!p_filter(FILT_OBEX)) |
| obex_dump(level, frm); |
| else |
| raw_dump(level + 1, frm); |
| break; |
| |
| default: |
| if (p_filter(FILT_L2CAP)) |
| break; |
| |
| raw_dump(level, frm); |
| break; |
| } |
| break; |
| } |
| } |
| } |
| |
| void l2cap_dump(int level, struct frame *frm) |
| { |
| struct frame *fr; |
| l2cap_hdr *hdr; |
| uint16_t dlen; |
| |
| if ((frm->flags & ACL_START) || frm->flags == ACL_START_NO_FLUSH) { |
| hdr = frm->ptr; |
| dlen = btohs(hdr->len); |
| |
| if (dlen + L2CAP_HDR_SIZE < (int) frm->len) { |
| /* invalid frame */ |
| raw_dump(level,frm); |
| return; |
| } |
| |
| if ((int) frm->len == (dlen + L2CAP_HDR_SIZE)) { |
| /* Complete frame */ |
| l2cap_parse(level, frm); |
| return; |
| } |
| |
| if (!(fr = get_frame(frm->handle))) { |
| fprintf(stderr, "Not enough connection handles\n"); |
| raw_dump(level, frm); |
| return; |
| } |
| |
| if (fr->data) |
| free(fr->data); |
| |
| if (!(fr->data = malloc(dlen + L2CAP_HDR_SIZE))) { |
| perror("Can't allocate L2CAP reassembly buffer"); |
| return; |
| } |
| memcpy(fr->data, frm->ptr, frm->len); |
| fr->data_len = dlen + L2CAP_HDR_SIZE; |
| fr->len = frm->len; |
| fr->ptr = fr->data; |
| fr->dev_id = frm->dev_id; |
| fr->in = frm->in; |
| fr->ts = frm->ts; |
| fr->handle = frm->handle; |
| fr->cid = frm->cid; |
| fr->num = frm->num; |
| fr->dlci = frm->dlci; |
| fr->channel = frm->channel; |
| fr->pppdump_fd = frm->pppdump_fd; |
| fr->audio_fd = frm->audio_fd; |
| } else { |
| if (!(fr = get_frame(frm->handle))) { |
| fprintf(stderr, "Not enough connection handles\n"); |
| raw_dump(level, frm); |
| return; |
| } |
| |
| if (!fr->data) { |
| /* Unexpected fragment */ |
| raw_dump(level, frm); |
| return; |
| } |
| |
| if (frm->len > (fr->data_len - fr->len)) { |
| /* Bad fragment */ |
| raw_dump(level, frm); |
| free(fr->data); fr->data = NULL; |
| return; |
| } |
| |
| memcpy(fr->data + fr->len, frm->ptr, frm->len); |
| fr->len += frm->len; |
| |
| if (fr->len == fr->data_len) { |
| /* Complete frame */ |
| l2cap_parse(level, fr); |
| |
| free(fr->data); fr->data = NULL; |
| return; |
| } |
| } |
| } |
| |
| void l2cap_clear(uint16_t handle) |
| { |
| del_handle(handle); |
| } |