static bool avrcp_set_player_value(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t num; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) return true; if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t attr, value; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); } return true; }
static inline bool mcc_msc(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_lmsc msc; if (!l2cap_frame_get_u8(frame, &msc.dlci)) return false; print_field("%*cdlci %d ", indent, ' ', RFCOMM_GET_DLCI(msc.dlci)); if (!l2cap_frame_get_u8(frame, &msc.v24_sig)) return false; /* v24 control signals */ print_field("%*cfc %d rtc %d rtr %d ic %d dv %d", indent, ' ', GET_V24_FC(msc.v24_sig), GET_V24_RTC(msc.v24_sig), GET_V24_RTR(msc.v24_sig), GET_V24_IC(msc.v24_sig), GET_V24_DV(msc.v24_sig)); if (frame->size < 2) goto done; /* * TODO: Implement the break signals decoding. */ packet_hexdump(frame->data, frame->size); done: return true; }
static bool avrcp_list_player_attributes(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t num; int i; if (len == 0) return true; if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num); for (i = 0; num > 0; num--, i++) { uint8_t attr; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); } return true; }
static bool avrcp_displayable_charset(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t num; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) return true; if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cCharsetCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint16_t charset; if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetID: 0x%04x (%s)", (indent - 8), ' ', charset, charset2str(charset)); } return true; }
static inline bool mcc_rls(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_rls rls; if (!l2cap_frame_get_u8(frame, &rls.dlci)) return false; if (!l2cap_frame_get_u8(frame, &rls.error)) return false; print_field("%*cdlci %d error: %d", indent, ' ', RFCOMM_GET_DLCI(rls.dlci), GET_ERROR(rls.error)); return true; }
static bool avrcp_set_addressed_player(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint16_t id; uint8_t status; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_be16(frame, &id)) return false; print_field("%*cPlayerID: 0x%04x (%u)", (indent - 8), ' ', id, id); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", (indent - 8), ' ', status, error2str(status)); return true; }
void avctp_packet(const struct l2cap_frame *frame) { struct l2cap_frame *l2cap_frame; struct avctp_frame avctp_frame; const char *pdu_color; l2cap_frame_pull(&avctp_frame.l2cap_frame, frame, 0); l2cap_frame = &avctp_frame.l2cap_frame; if (!l2cap_frame_get_u8(l2cap_frame, &avctp_frame.hdr) || !l2cap_frame_get_be16(l2cap_frame, &avctp_frame.pid)) { print_text(COLOR_ERROR, "frame too short"); packet_hexdump(frame->data, frame->size); return; } if (frame->in) pdu_color = COLOR_MAGENTA; else pdu_color = COLOR_BLUE; print_indent(6, pdu_color, "AVCTP", "", COLOR_OFF, " %s: %s: type 0x%02x label %d PID 0x%04x", frame->psm == 23 ? "Control" : "Browsing", avctp_frame.hdr & 0x02 ? "Response" : "Command", avctp_frame.hdr & 0x0c, avctp_frame.hdr >> 4, avctp_frame.pid); if (avctp_frame.pid == 0x110e || avctp_frame.pid == 0x110c) avrcp_packet(&avctp_frame); else packet_hexdump(frame->data, frame->size); }
static bool avrcp_get_play_status(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint32_t interval; uint8_t status; if (ctype <= AVC_CTYPE_GENERAL_INQUIRY) return true; if (!l2cap_frame_get_be32(frame, &interval)) return false; print_field("%*cSongLength: 0x%08x (%u miliseconds)", (indent - 8), ' ', interval, interval); if (!l2cap_frame_get_be32(frame, &interval)) return false; print_field("%*cSongPosition: 0x%08x (%u miliseconds)", (indent - 8), ' ', interval, interval); if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cPlayStatus: 0x%02x (%s)", (indent - 8), ' ', status, playstatus2str(status)); return true; }
void sdp_packet(const struct l2cap_frame *frame) { uint8_t pdu; uint16_t tid, plen; struct l2cap_frame sdp_frame; struct tid_data *tid_info; const struct sdp_data *sdp_data = NULL; const char *pdu_color, *pdu_str; int i; l2cap_frame_pull(&sdp_frame, frame, 0); if (!l2cap_frame_get_u8(&sdp_frame, &pdu) || !l2cap_frame_get_be16(&sdp_frame, &tid) || !l2cap_frame_get_be16(&sdp_frame, &plen)) { print_text(COLOR_ERROR, "frame too short"); packet_hexdump(frame->data, frame->size); return; } if (sdp_frame.size != plen) { print_text(COLOR_ERROR, "invalid frame size"); packet_hexdump(sdp_frame.data, sdp_frame.size); return; } for (i = 0; sdp_table[i].str; i++) { if (sdp_table[i].pdu == pdu) { sdp_data = &sdp_table[i]; break; } } if (sdp_data) { if (sdp_data->func) { if (frame->in) pdu_color = COLOR_MAGENTA; else pdu_color = COLOR_BLUE; } else pdu_color = COLOR_WHITE_BG; pdu_str = sdp_data->str; } else { pdu_color = COLOR_WHITE_BG; pdu_str = "Unknown"; } print_indent(6, pdu_color, "SDP: ", pdu_str, COLOR_OFF, " (0x%2.2x) tid %d len %d", pdu, tid, plen); tid_info = get_tid(tid, frame->chan); if (!sdp_data || !sdp_data->func || !tid_info) { packet_hexdump(sdp_frame.data, sdp_frame.size); return; } sdp_data->func(&sdp_frame, tid_info); }
static inline bool mcc_frame(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { uint8_t length, ex_length, type; const char *type_str; int i; struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_lmcc mcc; const struct mcc_data *mcc_data = NULL; if (!l2cap_frame_get_u8(frame, &mcc.type) || !l2cap_frame_get_u8(frame, &length)) return false; if (RFCOMM_TEST_EA(length)) mcc.length = (uint16_t) GET_LEN8(length); else { if (!l2cap_frame_get_u8(frame, &ex_length)) return false; mcc.length = ((uint16_t) length << 8) | ex_length; mcc.length = GET_LEN16(mcc.length); } type = RFCOMM_GET_MCC_TYPE(mcc.type); for (i = 0; mcc_table[i].str; i++) { if (mcc_table[i].type == type) { mcc_data = &mcc_table[i]; break; } } if (mcc_data) type_str = mcc_data->str; else type_str = "Unknown"; print_field("%*cMCC Message type: %s %s(0x%2.2x)", indent, ' ', type_str, CR_STR(mcc.type), type); print_field("%*cLength: %d", indent+2, ' ', mcc.length); rfcomm_frame->mcc = mcc; packet_hexdump(frame->data, frame->size); return true; }
static bool avrcp_pdu_packet(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t pduid; uint16_t len; int i; const struct avrcp_ctrl_pdu_data *ctrl_pdu_data = NULL; if (!l2cap_frame_get_u8(frame, &pduid)) return false; if (!l2cap_frame_get_u8(frame, &avctp_frame->pt)) return false; if (!l2cap_frame_get_be16(frame, &len)) return false; print_indent(indent, COLOR_OFF, "AVRCP: ", pdu2str(pduid), COLOR_OFF, " pt %s len 0x%04x", pt2str(avctp_frame->pt), len); if (frame->size != len) return false; if (ctype == 0xA) return avrcp_rejected_packet(frame, indent + 2); for (i = 0; avrcp_ctrl_pdu_table[i].func; i++) { if (avrcp_ctrl_pdu_table[i].pduid == pduid) { ctrl_pdu_data = &avrcp_ctrl_pdu_table[i]; break; } } if (!ctrl_pdu_data || !ctrl_pdu_data->func) { packet_hexdump(frame->data, frame->size); return true; } return ctrl_pdu_data->func(avctp_frame, ctype, len, indent + 2); }
static bool avrcp_add_to_now_playing(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint64_t uid; uint16_t uidcounter; uint8_t scope, status; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_u8(frame, &scope)) return false; print_field("%*cScope: 0x%02x (%s)", (indent - 8), ' ', scope, scope2str(scope)); if (!l2cap_frame_get_be64(frame, &uid)) return false; print_field("%*cUID: 0x%16" PRIx64 " (%" PRIu64 ")", (indent - 8), ' ', uid, uid); if (!l2cap_frame_get_be16(frame, &uidcounter)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ', uidcounter, uidcounter); return true; response: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cStatus: 0x%02x (%s)", (indent - 8), ' ', status, error2str(status)); return true; }
static bool cmd_nt_understood(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint8_t ptype; if (!l2cap_frame_get_u8(frame, &ptype)) return false; print_field("%*cType: 0x%02x ", indent, ' ', ptype); return true; }
static bool avrcp_rejected_packet(struct l2cap_frame *frame, uint8_t indent) { uint8_t status; if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cError: 0x%02x (%s)", (indent - 8), ' ', status, error2str(status)); return true; }
static inline bool mcc_nsc(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_nsc nsc; if (!l2cap_frame_get_u8(frame, &nsc.cmd_type)) return false; print_field("%*ccr %d, mcc_cmd_type %x", indent, ' ', GET_CR(nsc.cmd_type), RFCOMM_GET_MCC_TYPE(nsc.cmd_type)); return true; }
static bool setup_conn_req(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint8_t uuid_size; uint32_t src_uuid = 0, dst_uuid = 0; if (!l2cap_frame_get_u8(frame, &uuid_size)) return false; print_field("%*cSize: 0x%02x ", indent, ' ', uuid_size); switch (uuid_size) { case 2: if (!l2cap_frame_get_be16(frame, (uint16_t *) &dst_uuid)) return false; if (!l2cap_frame_get_be16(frame, (uint16_t *) &src_uuid)) return false; break; case 4: if (!l2cap_frame_get_be32(frame, &dst_uuid)) return false; if (!l2cap_frame_get_be32(frame, &src_uuid)) return false; break; case 16: if (!l2cap_frame_get_be32(frame, &dst_uuid)) return false; l2cap_frame_pull(frame, frame, 12); if (!l2cap_frame_get_be32(frame, &src_uuid)) return false; l2cap_frame_pull(frame, frame, 12); break; default: l2cap_frame_pull(frame, frame, (uuid_size * 2)); return true; } print_field("%*cDst: 0x%x(%s)", indent, ' ', dst_uuid, bt_uuid32_to_str(dst_uuid)); print_field("%*cSrc: 0x%x(%s)", indent, ' ', src_uuid, bt_uuid32_to_str(src_uuid)); return true; }
static bool get_macaddr(struct bnep_frame *bnep_frame, char *str) { uint8_t addr[6]; struct l2cap_frame *frame = &bnep_frame->l2cap_frame; int i; for (i = 0; i < 6; i++) if (!l2cap_frame_get_u8(frame, &addr[i])) return false; sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); return true; }
static bool bnep_eval_extension(struct bnep_frame *bnep_frame, uint8_t indent) { struct l2cap_frame *frame = &bnep_frame->l2cap_frame; uint8_t type, length; int extension; if (!l2cap_frame_get_u8(frame, &type)) return false; if (!l2cap_frame_get_u8(frame, &length)) return false; extension = GET_EXTENSION(type); type = GET_PKT_TYPE(type); switch (type) { case BNEP_EXTENSION_CONTROL: print_field("%*cExt Control(0x%02x|%s) len 0x%02x", indent, ' ', type, extension ? "1" : "0", length); if (!bnep_control(bnep_frame, indent+2, length)) return false; break; default: print_field("%*cExt Unknown(0x%02x|%s) len 0x%02x", indent, ' ', type, extension ? "1" : "0", length); packet_hexdump(frame->data, length); l2cap_frame_pull(frame, frame, length); } if (extension) if (!bnep_eval_extension(bnep_frame, indent)) return false; return true; }
static bool avrcp_set_absolute_volume(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t value; if (!l2cap_frame_get_u8(frame, &value)) return false; value &= 0x7F; print_field("%*cVolume: %.2f%% (%d/127)", (indent - 8), ' ', value/1.27, value); return true; }
static inline bool mcc_test(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; uint8_t data; printf("%*cTest Data: 0x ", indent, ' '); while (frame->size > 1) { if (!l2cap_frame_get_u8(frame, &data)) return false; printf("%2.2x ", data); } printf("\n"); return true; }
static bool avrcp_get_capabilities(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t cap, count; int i; if (!l2cap_frame_get_u8(frame, &cap)) return false; print_field("%*cCapabilityID: 0x%02x (%s)", (indent - 8), ' ', cap, cap2str(cap)); if (len == 1) return true; if (!l2cap_frame_get_u8(frame, &count)) return false; print_field("%*cCapabilityCount: 0x%02x", (indent - 8), ' ', count); switch (cap) { case 0x2: for (; count > 0; count--) { uint8_t company[3]; if (!l2cap_frame_get_u8(frame, &company[0]) || !l2cap_frame_get_u8(frame, &company[1]) || !l2cap_frame_get_u8(frame, &company[2])) return false; print_field("%*c%s: 0x%02x%02x%02x", (indent - 8), ' ', cap2str(cap), company[0], company[1], company[2]); } break; case 0x3: for (i = 0; count > 0; count--, i++) { uint8_t event; if (!l2cap_frame_get_u8(frame, &event)) return false; print_field("%*c%s: 0x%02x (%s)", (indent - 8), ' ', cap2str(cap), event, event2str(event)); } break; default: packet_hexdump(frame->data, frame->size); } return true; }
static bool avrcp_control_packet(struct avctp_frame *avctp_frame) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t ctype, address, subunit, opcode, company[3], indent = 2; if (!l2cap_frame_get_u8(frame, &ctype) || !l2cap_frame_get_u8(frame, &address) || !l2cap_frame_get_u8(frame, &opcode)) return false; print_field("AV/C: %s: address 0x%02x opcode 0x%02x", ctype2str(ctype), address, opcode); subunit = address >> 3; print_field("%*cSubunit: %s", indent, ' ', subunit2str(subunit)); print_field("%*cOpcode: %s", indent, ' ', opcode2str(opcode)); /* Skip non-panel subunit packets */ if (subunit != 0x09) { packet_hexdump(frame->data, frame->size); return true; } /* Not implemented should not contain any operand */ if (ctype == 0x8) { packet_hexdump(frame->data, frame->size); return true; } switch (opcode) { case 0x7c: return avrcp_passthrough_packet(avctp_frame); case 0x00: if (!l2cap_frame_get_u8(frame, &company[0]) || !l2cap_frame_get_u8(frame, &company[1]) || !l2cap_frame_get_u8(frame, &company[2])) return false; print_field("%*cCompany ID: 0x%02x%02x%02x", indent, ' ', company[0], company[1], company[2]); return avrcp_pdu_packet(avctp_frame, ctype, 10); default: packet_hexdump(frame->data, frame->size); return true; } }
static bool uih_frame(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { uint8_t credits; struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_lhdr *hdr = &rfcomm_frame->hdr; if (!RFCOMM_GET_CHANNEL(hdr->address)) return mcc_frame(rfcomm_frame, indent); /* fetching credits from UIH frame */ if (GET_PF(hdr->control)) { if (!l2cap_frame_get_u8(frame, &credits)) return false; hdr->credits = credits; print_field("%*cCredits: %d", indent, ' ', hdr->credits); } packet_hexdump(frame->data, frame->size); return true; }
static inline bool mcc_rpn(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_rpn rpn; if (!l2cap_frame_get_u8(frame, &rpn.dlci)) return false; print_field("%*cdlci %d", indent, ' ', RFCOMM_GET_DLCI(rpn.dlci)); if (frame->size < 7) goto done; /* port value octets (optional) */ if (!l2cap_frame_get_u8(frame, &rpn.bit_rate)) return false; if (!l2cap_frame_get_u8(frame, &rpn.parity)) return false; if (!l2cap_frame_get_u8(frame, &rpn.io)) return false; print_field("%*cbr %d db %d sb %d p %d pt %d xi %d xo %d", indent, ' ', rpn.bit_rate, GET_RPN_DB(rpn.parity), GET_RPN_SB(rpn.parity), GET_RPN_PARITY(rpn.parity), GET_RPN_PTYPE(rpn.parity), GET_RPN_XIN(rpn.io), GET_RPN_XOUT(rpn.io)); if (!l2cap_frame_get_u8(frame, &rpn.xon)) return false; if (!l2cap_frame_get_u8(frame, &rpn.xoff)) return false; print_field("%*crtri %d rtro %d rtci %d rtco %d xon %d xoff %d", indent, ' ', GET_RPN_RTRI(rpn.io), GET_RPN_RTRO(rpn.io), GET_RPN_RTCI(rpn.io), GET_RPN_RTCO(rpn.io), rpn.xon, rpn.xoff); if (!l2cap_frame_get_le16(frame, &rpn.pm)) return false; print_field("%*cpm 0x%04x", indent, ' ', rpn.pm); done: return true; }
static bool bnep_control(struct bnep_frame *bnep_frame, uint8_t indent, int hdr_len) { uint8_t ctype; struct l2cap_frame *frame = &bnep_frame->l2cap_frame; const struct bnep_control_data *bnep_control_data = NULL; const char *type_str; int i; if (!l2cap_frame_get_u8(frame, &ctype)) return false; for (i = 0; bnep_control_table[i].str; i++) { if (bnep_control_table[i].type == ctype) { bnep_control_data = &bnep_control_table[i]; break; } } if (bnep_control_data) type_str = bnep_control_data->str; else type_str = "Unknown control type"; print_field("%*c%s (0x%02x) ", indent, ' ', type_str, ctype); if (!bnep_control_data || !bnep_control_data->func) { packet_hexdump(frame->data, hdr_len - 1); l2cap_frame_pull(frame, frame, hdr_len - 1); goto done; } if (!bnep_control_data->func(bnep_frame, indent+2)) return false; done: return true; }
static inline bool mcc_pn(struct rfcomm_frame *rfcomm_frame, uint8_t indent) { struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame; struct rfcomm_pn pn; /* rfcomm_pn struct is defined in rfcomm.h */ if (!l2cap_frame_get_u8(frame, &pn.dlci)) return false; if (!l2cap_frame_get_u8(frame, &pn.flow_ctrl)) return false; if (!l2cap_frame_get_u8(frame, &pn.priority)) return false; print_field("%*cdlci %d frame_type %d credit_flow %d pri %d", indent, ' ', GET_PN_DLCI(pn.dlci), GET_FRM_TYPE(pn.flow_ctrl), GET_CRT_FLOW(pn.flow_ctrl), GET_PRIORITY(pn.priority)); if (!l2cap_frame_get_u8(frame, &pn.ack_timer)) return false; if (!l2cap_frame_get_le16(frame, &pn.mtu)) return false; if (!l2cap_frame_get_u8(frame, &pn.max_retrans)) return false; if (!l2cap_frame_get_u8(frame, &pn.credits)) return false; print_field("%*cack_timer %d frame_size %d max_retrans %d credits %d", indent, ' ', pn.ack_timer, pn.mtu, pn.max_retrans, pn.credits); return true; }
void rfcomm_packet(const struct l2cap_frame *frame) { uint8_t ctype, length, ex_length, indent = 1; const char *frame_str, *frame_color; struct l2cap_frame *l2cap_frame, tmp_frame; struct rfcomm_frame rfcomm_frame; struct rfcomm_lhdr hdr; const struct rfcomm_data *rfcomm_data = NULL; int i; l2cap_frame_pull(&rfcomm_frame.l2cap_frame, frame, 0); l2cap_frame = &rfcomm_frame.l2cap_frame; if (frame->size < 4) goto fail; if (!l2cap_frame_get_u8(l2cap_frame, &hdr.address) || !l2cap_frame_get_u8(l2cap_frame, &hdr.control) || !l2cap_frame_get_u8(l2cap_frame, &length)) goto fail; /* length maybe 1 or 2 octets */ if (RFCOMM_TEST_EA(length)) hdr.length = (uint16_t) GET_LEN8(length); else { if (!l2cap_frame_get_u8(l2cap_frame, &ex_length)) goto fail; hdr.length = ((uint16_t)length << 8) | ex_length; hdr.length = GET_LEN16(hdr.length); } l2cap_frame_pull(&tmp_frame, l2cap_frame, l2cap_frame->size-1); if(!l2cap_frame_get_u8(&tmp_frame, &hdr.fcs)) goto fail; /* Decoding frame type */ ctype = RFCOMM_GET_TYPE(hdr.control); for (i = 0; rfcomm_table[i].str; i++) { if (rfcomm_table[i].frame == ctype) { rfcomm_data = &rfcomm_table[i]; break; } } if (rfcomm_data) { if (frame->in) frame_color = COLOR_MAGENTA; else frame_color = COLOR_BLUE; frame_str = rfcomm_data->str; } else { frame_color = COLOR_WHITE_BG; frame_str = "Unknown"; } if (!rfcomm_data) { packet_hexdump(frame->data, frame->size); return; } print_indent(6, frame_color, "RFCOMM: ", frame_str, COLOR_OFF, "(0x%2.2x)", ctype); rfcomm_frame.hdr = hdr; print_rfcomm_hdr(&rfcomm_frame, indent); /* UIH frame */ if (ctype == 0xef) if (!uih_frame(&rfcomm_frame, indent)) goto fail; return; fail: print_text(COLOR_ERROR, "Frame too short"); packet_hexdump(frame->data, frame->size); return; }
static bool avrcp_get_player_value_text(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; static uint8_t attr = 0; uint8_t num; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t value; if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); } return true; response: if (!l2cap_frame_get_u8(frame, &num)) return false; print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num); for (; num > 0; num--) { uint8_t value, len; uint16_t charset; if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); if (!l2cap_frame_get_be16(frame, &charset)) return false; print_field("%*cCharsetIDID: 0x%02x (%s)", (indent - 8), ' ', charset, charset2str(charset)); if (!l2cap_frame_get_u8(frame, &len)) return false; print_field("%*cStringLength: 0x%02x", (indent - 8), ' ', len); printf("String: "); for (; len > 0; len--) { uint8_t c; if (!l2cap_frame_get_u8(frame, &c)) return false; printf("%1c", isprint(c) ? c : '.'); } printf("\n"); } return true; }
static bool avrcp_register_notification(struct avctp_frame *avctp_frame, uint8_t ctype, uint8_t len, uint8_t indent) { struct l2cap_frame *frame = &avctp_frame->l2cap_frame; uint8_t event, status; uint16_t uid; uint32_t interval; uint64_t id; if (ctype > AVC_CTYPE_GENERAL_INQUIRY) goto response; if (!l2cap_frame_get_u8(frame, &event)) return false; print_field("%*cEventID: 0x%02x (%s)", (indent - 8), ' ', event, event2str(event)); if (!l2cap_frame_get_be32(frame, &interval)) return false; print_field("%*cInterval: 0x%08x (%u seconds)", (indent - 8), ' ', interval, interval); return true; response: if (!l2cap_frame_get_u8(frame, &event)) return false; print_field("%*cEventID: 0x%02x (%s)", (indent - 8), ' ', event, event2str(event)); switch (event) { case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cPlayStatus: 0x%02x (%s)", (indent - 8), ' ', status, playstatus2str(status)); break; case AVRCP_EVENT_TRACK_CHANGED: if (!l2cap_frame_get_be64(frame, &id)) return false; print_field("%*cIdentifier: 0x%16" PRIx64 " (%" PRIu64 ")", (indent - 8), ' ', id, id); break; case AVRCP_EVENT_PLAYBACK_POS_CHANGED: if (!l2cap_frame_get_be32(frame, &interval)) return false; print_field("%*cPosition: 0x%08x (%u miliseconds)", (indent - 8), ' ', interval, interval); break; case AVRCP_EVENT_BATT_STATUS_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cBatteryStatus: 0x%02x (%s)", (indent - 8), ' ', status, status2str(status)); break; case AVRCP_EVENT_SYSTEM_STATUS_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cSystemStatus: 0x%02x ", (indent - 8), ' ', status); switch (status) { case 0x00: printf("(POWER_ON)\n"); break; case 0x01: printf("(POWER_OFF)\n"); break; case 0x02: printf("(UNPLUGGED)\n"); break; default: printf("(UNKOWN)\n"); break; } break; case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', status); for (; status > 0; status--) { uint8_t attr, value; if (!l2cap_frame_get_u8(frame, &attr)) return false; print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ', attr, attr2str(attr)); if (!l2cap_frame_get_u8(frame, &value)) return false; print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ', value, value2str(attr, value)); } break; case AVRCP_EVENT_VOLUME_CHANGED: if (!l2cap_frame_get_u8(frame, &status)) return false; status &= 0x7F; print_field("%*cVolume: %.2f%% (%d/127)", (indent - 8), ' ', status/1.27, status); break; case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: if (!l2cap_frame_get_be16(frame, &uid)) return false; print_field("%*cPlayerID: 0x%04x (%u)", (indent - 8), ' ', uid, uid); if (!l2cap_frame_get_be16(frame, &uid)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ', uid, uid); break; case AVRCP_EVENT_UIDS_CHANGED: if (!l2cap_frame_get_be16(frame, &uid)) return false; print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ', uid, uid); break; } return true; }
void bnep_packet(const struct l2cap_frame *frame) { uint8_t type, indent = 1; struct bnep_frame bnep_frame; struct l2cap_frame *l2cap_frame; const struct bnep_data *bnep_data = NULL; const char *pdu_color, *pdu_str; int i; l2cap_frame_pull(&bnep_frame.l2cap_frame, frame, 0); l2cap_frame = &bnep_frame.l2cap_frame; if (!l2cap_frame_get_u8(l2cap_frame, &type)) goto fail; bnep_frame.extension = GET_EXTENSION(type); bnep_frame.type = GET_PKT_TYPE(type); for (i = 0; bnep_table[i].str; i++) { if (bnep_table[i].type == bnep_frame.type) { bnep_data = &bnep_table[i]; break; } } if (bnep_data) { if (bnep_data->func) { if (frame->in) pdu_color = COLOR_MAGENTA; else pdu_color = COLOR_BLUE; } else pdu_color = COLOR_WHITE_BG; pdu_str = bnep_data->str; } else { pdu_color = COLOR_WHITE_BG; pdu_str = "Unknown packet type"; } print_indent(6, pdu_color, "BNEP: ", pdu_str, COLOR_OFF, " (0x%02x|%s)", bnep_frame.type, bnep_frame.extension ? "1" : "0"); if (!bnep_data || !bnep_data->func) { packet_hexdump(l2cap_frame->data, l2cap_frame->size); return; } if (!bnep_data->func(&bnep_frame, indent, -1)) goto fail; /* Extension info */ if (bnep_frame.extension) if (!bnep_eval_extension(&bnep_frame, indent+2)) goto fail; /* Control packet => No payload info */ if (bnep_frame.type == BNEP_CONTROL) return; /* TODO: Handle BNEP IP packet */ packet_hexdump(l2cap_frame->data, l2cap_frame->size); return; fail: print_text(COLOR_ERROR, "frame too short"); packet_hexdump(frame->data, frame->size); }