// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include "parser.h" static char *si2str(uint8_t si) { switch (si & 0x7f) { case 0x01: return "Discover"; case 0x02: return "Capabilities"; case 0x03: return "Set config"; case 0x04: return "Get config"; case 0x05: return "Reconfigure"; case 0x06: return "Open"; case 0x07: return "Start"; case 0x08: return "Close"; case 0x09: return "Suspend"; case 0x0a: return "Abort"; case 0x0b: return "Security"; case 0x0c: return "All Capabilities"; case 0x0d: return "Delay Report"; default: return "Unknown"; } } static char *pt2str(uint8_t hdr) { switch (hdr & 0x0c) { case 0x00: return "Single"; case 0x04: return "Start"; case 0x08: return "Cont"; case 0x0c: return "End"; default: return "Unk"; } } static char *mt2str(uint8_t hdr) { switch (hdr & 0x03) { case 0x00: return "cmd"; case 0x02: return "rsp"; case 0x03: return "rej"; default: return "rfd"; } } static char *media2str(uint8_t type) { switch (type) { case 0: return "Audio"; case 1: return "Video"; case 2: return "Multimedia"; default: return "Reserved"; } } static char *codec2str(uint8_t type, uint8_t codec) { switch (type) { case 0: switch (codec) { case 0: return "SBC"; case 1: return "MPEG-1,2 Audio"; case 2: return "MPEG-2,4 AAC"; case 4: return "ATRAC family"; case 255: return "non-A2DP"; default: return "Reserved"; } break; case 1: switch (codec) { case 1: return "H.263 baseline"; case 2: return "MPEG-4 Visual Simple Profile"; case 3: return "H.263 profile 3"; case 4: return "H.263 profile 8"; case 255: return "Non-VDP"; default: return "Reserved"; } break; } return "Unknown"; } static char *vndcodec2str(uint32_t vendor, uint16_t vndcodec) { if (vendor == 0x0000004f && vndcodec == 0x0001) return "aptX"; else if (vendor == 0x0000012d && vndcodec == 0x00aa) return "LDAC"; return "Unknown"; } static char *cat2str(uint8_t cat) { switch (cat) { case 1: return "Media Transport"; case 2: return "Reporting"; case 3: return "Recovery"; case 4: return "Content Protection"; case 5: return "Header Compression"; case 6: return "Multiplexing"; case 7: return "Media Codec"; case 8: return "Delay Reporting"; default: return "Reserved"; } } static void errorcode(int level, struct frame *frm) { uint8_t code; p_indent(level, frm); code = p_get_u8(frm); printf("Error code %d\n", code); } static void acp_seid(int level, struct frame *frm) { uint8_t seid; p_indent(level, frm); seid = p_get_u8(frm); printf("ACP SEID %d\n", seid >> 2); } static void acp_int_seid(int level, struct frame *frm) { uint8_t acp_seid, int_seid; p_indent(level, frm); acp_seid = p_get_u8(frm); int_seid = p_get_u8(frm); printf("ACP SEID %d - INT SEID %d\n", acp_seid >> 2, int_seid >> 2); } static void capabilities(int level, struct frame *frm) { uint8_t cat, len; while (frm->len > 1) { p_indent(level, frm); cat = p_get_u8(frm); len = p_get_u8(frm); if (cat == 7) { uint8_t type, codec; uint16_t tmp, freq, vndcodec = 0; uint32_t bitrate, vendor = 0; int i; type = p_get_u8(frm); codec = p_get_u8(frm); if (codec == 255) { vendor = btohl(htonl(p_get_u32(frm))); vndcodec = btohs(htons(p_get_u16(frm))); printf("%s - %s (%s)\n", cat2str(cat), codec2str(type, codec), vndcodec2str(vendor, vndcodec)); } else { printf("%s - %s\n", cat2str(cat), codec2str(type, codec)); } switch (codec) { case 0: tmp = p_get_u8(frm); p_indent(level + 1, frm); if (tmp & 0x80) printf("16kHz "); if (tmp & 0x40) printf("32kHz "); if (tmp & 0x20) printf("44.1kHz "); if (tmp & 0x10) printf("48kHz "); printf("\n"); p_indent(level + 1, frm); if (tmp & 0x08) printf("Mono "); if (tmp & 0x04) printf("DualChannel "); if (tmp & 0x02) printf("Stereo "); if (tmp & 0x01) printf("JointStereo "); printf("\n"); tmp = p_get_u8(frm); p_indent(level + 1, frm); if (tmp & 0x80) printf("4 "); if (tmp & 0x40) printf("8 "); if (tmp & 0x20) printf("12 "); if (tmp & 0x10) printf("16 "); printf("Blocks\n"); p_indent(level + 1, frm); if (tmp & 0x08) printf("4 "); if (tmp & 0x04) printf("8 "); printf("Subbands\n"); p_indent(level + 1, frm); if (tmp & 0x02) printf("SNR "); if (tmp & 0x01) printf("Loudness "); printf("\n"); tmp = p_get_u8(frm); p_indent(level + 1, frm); printf("Bitpool Range %d-%d\n", tmp, p_get_u8(frm)); break; case 1: tmp = p_get_u8(frm); p_indent(level + 1, frm); printf("Layers: "); if (tmp & 0x80) printf("1 "); if (tmp & 0x40) printf("2 "); if (tmp & 0x20) printf("3 "); printf("\n"); p_indent(level + 1, frm); printf("CRC Protection: %s\n", tmp & 0x10 ? "Yes" : "No"); p_indent(level + 1, frm); if (tmp & 0x08) printf("Mono "); if (tmp & 0x04) printf("DualChannel "); if (tmp & 0x02) printf("Stereo "); if (tmp & 0x01) printf("JointStereo "); printf("\n"); tmp = p_get_u8(frm); p_indent(level + 1, frm); printf("Media Payload Format: RFC-2250 %s\n", tmp & 0x40 ? "RFC-3119" : ""); p_indent(level + 1, frm); if (tmp & 0x20) printf("16kHz "); if (tmp & 0x10) printf("22.05kHz "); if (tmp & 0x08) printf("24kHz "); if (tmp & 0x04) printf("32kHz "); if (tmp & 0x02) printf("44.1kHz "); if (tmp & 0x01) printf("48kHz "); printf("\n"); tmp = p_get_u16(frm); p_indent(level + 1, frm); printf("VBR: %s\n", tmp & 0x8000 ? "Yes" : "No"); p_indent(level + 1, frm); printf("Bit Rate Indexes: "); if (tmp & 0x8000) { printf("n/a"); } else { for (i = 0; i < 15; i++, tmp >>= 1) if (tmp & 0x0001) printf("%d ", i); } printf("\n"); break; case 2: tmp = p_get_u8(frm); p_indent(level + 1, frm); if (tmp & 0x80) printf("MPEG-2 AAC LC "); if (tmp & 0x40) printf("MPEG-4 AAC LC "); if (tmp & 0x20) printf("MPEG-4 AAC LTP "); if (tmp & 0x10) printf("MPEG-4 AAC scalable "); printf("\n"); tmp = p_get_u16(frm); freq = tmp >> 4; p_indent(level + 1, frm); if (freq & 0x0800) printf("8kHz "); if (freq & 0x0400) printf("11.025kHz "); if (freq & 0x0200) printf("12kHz "); if (freq & 0x0100) printf("16kHz "); if (freq & 0x0080) printf("22.05kHz "); if (freq & 0x0040) printf("24kHz "); if (freq & 0x0020) printf("32kHz "); if (freq & 0x0010) printf("44.1kHz "); if (freq & 0x0008) printf("48kHz "); if (freq & 0x0004) printf("64kHz "); if (freq & 0x0002) printf("88.2kHz "); if (freq & 0x0001) printf("96kHz "); printf("\n"); tmp >>= 2; p_indent(level + 1, frm); if (tmp & 0x02) printf("1 "); if (tmp & 0x01) printf("2 "); printf("Channels\n"); tmp = p_get_u8(frm); bitrate = ((tmp & 0x7f) << 16) | p_get_u16(frm); p_indent(level + 1, frm); printf("%ubps ", bitrate); printf("%s\n", tmp & 0x80 ? "VBR" : ""); break; case 255: if (vendor == 0x0000004f && vndcodec == 0x0001) { tmp = p_get_u8(frm); p_indent(level + 1, frm); if (tmp & 0x80) printf("16kHz "); if (tmp & 0x40) printf("32kHz "); if (tmp & 0x20) printf("44.1kHz "); if (tmp & 0x10) printf("48kHz "); printf("\n"); p_indent(level + 1, frm); if (tmp & 0x02) printf("Stereo "); if (tmp & 0x01) printf("Mono "); printf("\n"); break; } else { hex_dump(level + 1, frm, len - 8); frm->ptr += (len - 8); frm->len -= (len - 8); } break; default: hex_dump(level + 1, frm, len - 2); frm->ptr += (len - 2); frm->len -= (len - 2); break; } } else { printf("%s\n", cat2str(cat)); hex_dump(level + 1, frm, len); frm->ptr += len; frm->len -= len; } } } static inline void discover(int level, uint8_t hdr, struct frame *frm) { uint8_t seid, type; switch (hdr & 0x03) { case 0x02: while (frm->len > 1) { p_indent(level, frm); seid = p_get_u8(frm); type = p_get_u8(frm); printf("ACP SEID %d - %s %s%s\n", seid >> 2, media2str(type >> 4), type & 0x08 ? "Sink" : "Source", seid & 0x02 ? " (InUse)" : ""); } break; case 0x03: errorcode(level, frm); break; } } static inline void get_capabilities(int level, uint8_t hdr, struct frame *frm) { switch (hdr & 0x03) { case 0x00: acp_seid(level, frm); break; case 0x02: capabilities(level, frm); break; case 0x03: errorcode(level, frm); break; } } static inline void set_configuration(int level, uint8_t hdr, struct frame *frm) { uint8_t cat; switch (hdr & 0x03) { case 0x00: acp_int_seid(level, frm); capabilities(level, frm); break; case 0x03: p_indent(level, frm); cat = p_get_u8(frm); printf("%s\n", cat2str(cat)); errorcode(level, frm); break; } } static inline void get_configuration(int level, uint8_t hdr, struct frame *frm) { switch (hdr & 0x03) { case 0x00: acp_seid(level, frm); break; case 0x02: capabilities(level, frm); break; case 0x03: errorcode(level, frm); break; } } static inline void reconfigure(int level, uint8_t hdr, struct frame *frm) { uint8_t cat; switch (hdr & 0x03) { case 0x00: acp_seid(level, frm); capabilities(level, frm); break; case 0x03: p_indent(level, frm); cat = p_get_u8(frm); printf("%s\n", cat2str(cat)); errorcode(level, frm); break; } } static inline void open_close_stream(int level, uint8_t hdr, struct frame *frm) { switch (hdr & 0x03) { case 0x00: acp_seid(level, frm); break; case 0x03: errorcode(level, frm); break; } } static inline void start_suspend_stream(int level, uint8_t hdr, struct frame *frm) { switch (hdr & 0x03) { case 0x00: while (frm->len > 0) acp_seid(level, frm); break; case 0x03: acp_seid(level, frm); errorcode(level, frm); break; } } static inline void abort_streaming(int level, uint8_t hdr, struct frame *frm) { switch (hdr & 0x03) { case 0x00: acp_seid(level, frm); break; } } static inline void security(int level, uint8_t hdr, struct frame *frm) { switch (hdr & 0x03) { case 0x00: acp_seid(level, frm); break; case 0x02: hex_dump(level + 1, frm, frm->len); frm->ptr += frm->len; frm->len = 0; break; case 0x03: errorcode(level, frm); break; } } static inline void delay_report(int level, uint8_t hdr, struct frame *frm) { uint8_t seid; uint16_t delay; switch (hdr & 0x03) { case 0x00: p_indent(level, frm); seid = p_get_u8(frm); delay = p_get_u16(frm); printf("ACP SEID %d delay %u.%ums\n", seid >> 2, delay / 10, delay % 10); break; case 0x03: errorcode(level, frm); break; } } void avdtp_dump(int level, struct frame *frm) { uint8_t hdr, sid, nsp, type; uint16_t seqn; uint32_t time, ssrc; switch (frm->num) { case 1: p_indent(level, frm); hdr = p_get_u8(frm); nsp = (hdr & 0x0c) == 0x04 ? p_get_u8(frm) : 0; sid = hdr & 0x08 ? 0x00 : p_get_u8(frm); printf("AVDTP(p): %s %s: transaction %d nsp 0x%02x\n", hdr & 0x08 ? pt2str(hdr) : si2str(sid), mt2str(hdr), hdr >> 4, nsp); switch (sid & 0x7f) { case 0x01: discover(level + 1, hdr, frm); break; case 0x02: case 0x0c: get_capabilities(level + 1, hdr, frm); break; case 0x03: set_configuration(level + 1, hdr, frm); break; case 0x04: get_configuration(level + 1, hdr, frm); break; case 0x05: reconfigure(level + 1, hdr, frm); break; case 0x06: open_close_stream(level + 1, hdr, frm); break; case 0x07: start_suspend_stream(level + 1, hdr, frm); break; case 0x08: open_close_stream(level + 1, hdr, frm); break; case 0x09: start_suspend_stream(level + 1, hdr, frm); break; case 0x0a: abort_streaming(level + 1, hdr, frm); break; case 0x0b: security(level + 1, hdr, frm); break; case 0x0d: delay_report(level + 1, hdr, frm); break; } break; case 2: p_indent(level, frm); hdr = p_get_u8(frm); type = p_get_u8(frm); seqn = p_get_u16(frm); time = p_get_u32(frm); ssrc = p_get_u32(frm); printf("AVDTP(c): ver %d %s%scc" " %d %spt %d seqn %d time %d ssrc %d\n", hdr >> 6, hdr & 0x20 ? "pad " : "", hdr & 0x10 ? "ext " : "", hdr & 0xf, type & 0x80 ? "mark " : "", type & 0x7f, seqn, time, ssrc); break; } raw_dump(level, frm); }