XenevaOS
Loading...
Searching...
No Matches
avdtp.h
Go to the documentation of this file.
1
30#ifndef __BT_AVDTP_H__
31#define __BT_AVDTP_H__
32
33#include <stdint.h>
34
35/* Minimal AVDTP (Audio/Video Distribution Transport Protocol) for the A2DP
36 * source role: talk to a headset sink (Sony ULT WEAR and friends) as
37 * BlueZ does — L2CAP to PSM 25 for signaling, DISCOVER, GET_CAPABILITIES,
38 * SET_CONFIGURATION (SBC), OPEN, a second L2CAP channel to PSM 25 for the
39 * media transport, START, then RTP/SBC media packets. LE Audio (CIS/LC3)
40 * is untouched and stays available; production gets both transports.
41 * Everything here is plain <stdint.h> so the host unit test covers it. */
42
43/* L2CAP fixed signaling channel + commands we use as initiator. */
44#define AVDTP_PSM 25
45#define L2CAP_CID_SIGNAL 0x0001
46#define L2CAP_CONN_REQ 0x02
47#define L2CAP_CONN_RSP 0x03
48#define L2CAP_CONF_REQ 0x04
49#define L2CAP_CONF_RSP 0x05
50#define L2CAP_CONF_SUCCESS 0x0000
51#define L2CAP_CONN_OK 0x0000
52#define L2CAP_CONN_PENDING 0x0001
53#define L2CAP_OPT_MTU 0x01
54
55/* AVDTP header (single packet): byte0 = trans(4)<<4 | ptype(2)<<2 | msg(2). */
56#define AVDTP_PKT_SINGLE 0x00
57#define AVDTP_MSG_CMD 0x00
58#define AVDTP_MSG_ACCEPT 0x02
59#define AVDTP_MSG_REJECT 0x03
60
61#define AVDTP_DISCOVER 0x01
62#define AVDTP_GET_CAPABILITIES 0x02
63#define AVDTP_SET_CONFIGURATION 0x03
64#define AVDTP_GET_CONFIGURATION 0x04
65#define AVDTP_RECONFIGURE 0x05
66#define AVDTP_OPEN 0x06
67#define AVDTP_START 0x07
68#define AVDTP_CLOSE 0x08
69#define AVDTP_SUSPEND 0x09
70#define AVDTP_ABORT 0x0A
71#define AVDTP_GET_ALLCAPABILITIES 0x0C
72#define AVDTP_DELAYREPORT 0x0D
73
74#define AVDTP_SEP_SINK 1
75#define AVDTP_MEDIA_AUDIO 0
76
77#define AVDTP_CAT_TRANSPORT 0x01
78#define AVDTP_CAT_REPORTING 0x02
79#define AVDTP_CAT_RECOVERY 0x03
80#define AVDTP_CAT_PROTECTION 0x04
81#define AVDTP_CAT_HDRCMPR 0x05
82#define AVDTP_CAT_MUX 0x06
83#define AVDTP_CAT_CODEC 0x07
84/* BlueZ avdtp.h AVDTP_DELAY_REPORTING. Category 0x08, not 0x09. */
85#define AVDTP_CAT_DELAYREP 0x08
86
87#define A2DP_CODEC_SBC 0x00
88/* SBC codec info, A2DP endian: {freq|mode, blocks|subbands|alloc, min, max}.
89 * 0x21 0x15 = 44.1 kHz + joint stereo, 16 blocks, 8 subbands, loudness. */
90#define A2DP_SBC_FREQ_44100 0x20
91#define A2DP_SBC_FREQ_48000 0x10
92#define A2DP_SBC_MODE_JOINT 0x01
93#define A2DP_SBC_BLK16 0x10
94#define A2DP_SBC_SB8 0x04
95#define A2DP_SBC_LOUDNESS 0x01
96
97#define AVDTP_RTP_PT_SBC 96
98
99/* Our fixed offer: 44.1 kHz joint stereo, 16 blocks, 8 subbands, loudness,
100 * bitpool 2..53 (A2DP high quality, ~328 kbit/s). */
101#define AVDTP_SBC_OFFER_0 (A2DP_SBC_FREQ_44100 | A2DP_SBC_MODE_JOINT)
102#define AVDTP_SBC_OFFER_1 (A2DP_SBC_BLK16 | A2DP_SBC_SB8 | A2DP_SBC_LOUDNESS)
103#define AVDTP_SBC_BITPOOL 53
104
105/* ---- L2CAP signaling builders (payload after the 4-byte L2CAP header).
106 * All return the payload length. */
107
108static inline int l2cap_build_conn_req(uint8_t* o, uint8_t id, uint16_t psm,
109 uint16_t scid) {
110 o[0] = L2CAP_CONN_REQ;
111 o[1] = id;
112 o[2] = 4;
113 o[3] = 0;
114 o[4] = (uint8_t)psm;
115 o[5] = (uint8_t)(psm >> 8);
116 o[6] = (uint8_t)scid;
117 o[7] = (uint8_t)(scid >> 8);
118 return 8;
119}
120
121/* Parse a connection response (payload after the L2CAP header:
122 * code id len(2) dcid(2) scid(2) result(2) status(2)).
123 * Returns 0 on success and fills dcid. */
124static inline int l2cap_parse_conn_rsp(const uint8_t* p, int len, uint8_t id,
125 uint16_t* dcid) {
126 uint16_t result;
127 if (len < 12 || p[0] != L2CAP_CONN_RSP || p[1] != id)
128 return -1;
129 *dcid = (uint16_t)(p[4] | (p[5] << 8));
130 result = (uint16_t)(p[8] | (p[9] << 8));
131 return result == L2CAP_CONN_OK ? 0 : -2;
132}
133
134/* Raw result word of a connection response (after L2CAP header):
135 * L2CAP_CONN_OK (0) or L2CAP_CONN_PENDING (1). Returns -1 when the frame
136 * is not a matching ConnRsp. Lets the caller keep waiting on PENDING
137 * like BlueZ (l2cap_conn_rsp) instead of failing Sony-style deferred
138 * accepts. */
139static inline int l2cap_conn_rsp_result(const uint8_t* p, int len, uint8_t id,
140 uint16_t* dcid) {
141 if (len < 12 || p[0] != L2CAP_CONN_RSP || p[1] != id)
142 return -1;
143 *dcid = (uint16_t)(p[4] | (p[5] << 8));
144 return (int)(uint16_t)(p[8] | (p[9] << 8));
145}
146
147static inline int l2cap_build_conf_req(uint8_t* o, uint8_t id, uint16_t dcid,
148 uint16_t mtu) {
149 o[0] = L2CAP_CONF_REQ;
150 o[1] = id;
151 o[2] = 8;
152 o[3] = 0;
153 o[4] = (uint8_t)dcid;
154 o[5] = (uint8_t)(dcid >> 8);
155 o[6] = 0;
156 o[7] = 0;
157 o[8] = L2CAP_OPT_MTU;
158 o[9] = 2;
159 o[10] = (uint8_t)mtu;
160 o[11] = (uint8_t)(mtu >> 8);
161 return 12;
162}
163
164/* Parse a config response (code id len(2) scid(2) flags(2) result(2)...).
165 * Returns 0 when our SCID was accepted. */
166static inline int l2cap_parse_conf_rsp(const uint8_t* p, int len, uint8_t id,
167 uint16_t scid) {
168 uint16_t got, res;
169 if (len < 10 || p[0] != L2CAP_CONF_RSP || p[1] != id)
170 return -1;
171 got = (uint16_t)(p[4] | (p[5] << 8));
172 res = (uint16_t)(p[8] | (p[9] << 8));
173 return (got == scid && res == L2CAP_CONF_SUCCESS) ? 0 : -2;
174}
175
176/* Config response. scid is the requester's channel id (the peer's),
177 * not the Destination CID from their request (that one is our SCID). */
178
179/* Config response carrying our MTU. Returns 14. */
180static inline int l2cap_build_conf_rsp_mtu(uint8_t* o, uint8_t id, uint16_t scid,
181 uint16_t mtu) {
182 o[0] = L2CAP_CONF_RSP;
183 o[1] = id;
184 o[2] = 10;
185 o[3] = 0;
186 o[4] = (uint8_t)scid;
187 o[5] = (uint8_t)(scid >> 8);
188 o[6] = 0;
189 o[7] = 0;
190 o[8] = 0;
191 o[9] = 0;
192 o[10] = L2CAP_OPT_MTU;
193 o[11] = 2;
194 o[12] = (uint8_t)mtu;
195 o[13] = (uint8_t)(mtu >> 8);
196 return 14;
197}
198
199static inline int l2cap_build_conf_rsp_ok(uint8_t* o, uint8_t id, uint16_t scid) {
200 return l2cap_build_conf_rsp_mtu(o, id, scid, 672);
201}
202
203/* Parse the peer's MTU out of a config request payload (after the L2CAP
204 * header). Returns 0 and fills *mtu when the MTU option is present. */
205static inline int l2cap_parse_conf_req_mtu(const uint8_t* p, int len,
206 uint16_t* mtu) {
207 int off;
208 if (len < 8 || p[0] != L2CAP_CONF_REQ)
209 return -1;
210 off = 8;
211 while (off + 2 <= len) {
212 uint8_t type = p[off], l = p[off + 1];
213 if (type == L2CAP_OPT_MTU && l == 2 && off + 4 <= len) {
214 *mtu = (uint16_t)(p[off + 2] | (p[off + 3] << 8));
215 return 0;
216 }
217 off += 2 + l;
218 }
219 return -1;
220}
221
222/* ---- AVDTP builders. trans is our 4-bit transaction label. ---- */
223
224static inline int avdtp_build_discover(uint8_t* o, uint8_t trans) {
225 o[0] = (uint8_t)((trans << 4) | AVDTP_MSG_CMD);
226 o[1] = AVDTP_DISCOVER;
227 return 2;
228}
229
230static inline int avdtp_build_getcaps(uint8_t* o, uint8_t trans, uint8_t seid) {
231 o[0] = (uint8_t)((trans << 4) | AVDTP_MSG_CMD);
233 o[2] = (uint8_t)(seid << 2);
234 return 3;
235}
236
237/* GET_ALLCAPABILITIES (spec 8.9): fallback when a sink rejects
238 * GET_CAPABILITIES. Same wire shape, different signal id. */
239static inline int avdtp_build_getallcaps(uint8_t* o, uint8_t trans, uint8_t seid) {
240 o[0] = (uint8_t)((trans << 4) | AVDTP_MSG_CMD);
242 o[2] = (uint8_t)(seid << 2);
243 return 3;
244}
245
246/* 1 when a GET_*CAPABILITIES accept lists Delay Reporting (category
247 * 0x08, length 0). BlueZ avdtp_set_configuration appends that category
248 * when both sides support it. */
249static inline int avdtp_caps_have_delay(const uint8_t* p, int len) {
250 int off = 2;
251 if (len < 4 || (p[0] & 3) != AVDTP_MSG_ACCEPT)
252 return 0;
253 while (off + 2 <= len) {
254 uint8_t cat = p[off], l = p[off + 1];
255 if (cat == AVDTP_CAT_DELAYREP && l == 0)
256 return 1;
257 off += 2 + l;
258 }
259 return 0;
260}
261
262/* caps = transport + SBC codec, plus Delay Reporting when delay is set.
263 * Returns length. */
264static inline int avdtp_build_setconfig(uint8_t* o, uint8_t trans, uint8_t acp,
265 uint8_t intu, uint8_t sbc0, uint8_t sbc1,
266 uint8_t minbp, uint8_t maxbp, int delay) {
267 o[0] = (uint8_t)((trans << 4) | AVDTP_MSG_CMD);
269 o[2] = (uint8_t)(acp << 2);
270 o[3] = (uint8_t)(intu << 2);
271 o[4] = AVDTP_CAT_TRANSPORT;
272 o[5] = 0;
273 o[6] = AVDTP_CAT_CODEC;
274 o[7] = 6;
275 o[8] = (uint8_t)(AVDTP_MEDIA_AUDIO << 4);
276 o[9] = A2DP_CODEC_SBC;
277 o[10] = sbc0;
278 o[11] = sbc1;
279 o[12] = minbp;
280 o[13] = maxbp;
281 if (!delay)
282 return 14;
283 o[14] = AVDTP_CAT_DELAYREP;
284 o[15] = 0;
285 return 16;
286}
287
288static inline int avdtp_build_open(uint8_t* o, uint8_t trans, uint8_t seid) {
289 o[0] = (uint8_t)((trans << 4) | AVDTP_MSG_CMD);
290 o[1] = AVDTP_OPEN;
291 o[2] = (uint8_t)(seid << 2);
292 return 3;
293}
294
295static inline int avdtp_build_start(uint8_t* o, uint8_t trans, uint8_t seid) {
296 o[0] = (uint8_t)((trans << 4) | AVDTP_MSG_CMD);
297 o[1] = AVDTP_START;
298 o[2] = (uint8_t)(seid << 2);
299 return 3;
300}
301
302static inline int avdtp_build_suspend(uint8_t* o, uint8_t trans, uint8_t seid) {
303 o[0] = (uint8_t)((trans << 4) | AVDTP_MSG_CMD);
304 o[1] = AVDTP_SUSPEND;
305 o[2] = (uint8_t)(seid << 2);
306 return 3;
307}
308
309static inline int avdtp_build_close(uint8_t* o, uint8_t trans, uint8_t seid) {
310 o[0] = (uint8_t)((trans << 4) | AVDTP_MSG_CMD);
311 o[1] = AVDTP_CLOSE;
312 o[2] = (uint8_t)(seid << 2);
313 return 3;
314}
315
316/* 0 = accept, nonzero = reject/error for our transaction label. */
317static inline int avdtp_is_accept(const uint8_t* p, int len, uint8_t trans,
318 uint8_t signal) {
319 if (len < 2)
320 return -1;
321 if ((p[0] >> 4) != trans || p[1] != signal)
322 return -1;
323 return ((p[0] & 3) == AVDTP_MSG_ACCEPT) ? 0 : 1;
324}
325
326/* DISCOVER accept payload is a packed list of 2-byte SEID records
327 * (BlueZ struct seid_info). There is no count byte. p includes the
328 * 2-byte AVDTP header. Writes unused audio-sink SEIDs in wire order.
329 * Returns the count, or -1 if the PDU is not an accept. */
330static inline int avdtp_list_sink_seids(const uint8_t* p, int len,
331 uint8_t* out, int max) {
332 int nrec, i, n = 0;
333 if (len < 4 || (p[0] & 3) != AVDTP_MSG_ACCEPT || max < 1)
334 return -1;
335 nrec = (len - 2) / 2;
336 for (i = 0; i < nrec && n < max; i++) {
337 const uint8_t* e = p + 2 + i * 2;
338 int seid = e[0] >> 2;
339 int inuse = (e[0] >> 1) & 1;
340 int type = (e[1] >> 3) & 1;
341 int media = e[1] >> 4;
342 if (!inuse && type == AVDTP_SEP_SINK && media == AVDTP_MEDIA_AUDIO &&
343 seid >= 1 && seid <= 62)
344 out[n++] = (uint8_t)seid;
345 }
346 return n;
347}
348
349/* First unused audio sink in a DISCOVER accept, or -1. */
350static inline int avdtp_pick_sink_seid(const uint8_t* p, int len) {
351 uint8_t seid;
352 int n = avdtp_list_sink_seids(p, len, &seid, 1);
353 if (n <= 0)
354 return -1;
355 return seid;
356}
357
358/* Media codec type from a GET_CAPABILITIES / GET_ALL_CAPABILITIES accept.
359 * A2DP SBC is 0x00, MPEG-2/4 AAC is 0x02. -1 when the category is absent. */
360static inline int avdtp_media_codec(const uint8_t* p, int len) {
361 int off = 2;
362 if (len < 2 || (p[0] & 3) != AVDTP_MSG_ACCEPT)
363 return -1;
364 while (off + 2 <= len) {
365 uint8_t cat = p[off], l = p[off + 1];
366 if (cat == AVDTP_CAT_CODEC && l >= 2 && off + 4 <= len)
367 return p[off + 3];
368 off += 2 + l;
369 }
370 return -1;
371}
372
373/* Parse a GET_CAPABILITIES accept into SBC codec bytes. p includes header.
374 * Fills sbc[4] with the sink's SBC capability bytes. 0 on success. */
375static inline int avdtp_parse_sbc_caps(const uint8_t* p, int len, uint8_t* sbc) {
376 int off = 2;
377 if (len < 2 || (p[0] & 3) != AVDTP_MSG_ACCEPT)
378 return -1;
379 while (off + 2 <= len) {
380 uint8_t cat = p[off], l = p[off + 1];
381 if (cat == AVDTP_CAT_CODEC && l >= 6 && off + 2 + 6 <= len &&
382 (p[off + 2] >> 4) == AVDTP_MEDIA_AUDIO && p[off + 3] == A2DP_CODEC_SBC) {
383 sbc[0] = p[off + 4];
384 sbc[1] = p[off + 5];
385 sbc[2] = p[off + 6];
386 sbc[3] = p[off + 7];
387 return 0;
388 }
389 off += 2 + l;
390 }
391 return -1;
392}
393
394/* Intersect our fixed offer with sink caps. Fills use[4]. Prefer 48 kHz
395 * when the sink offers it: Deodhai mixes at 48000, and a 44.1 kHz SBC
396 * stream of that PCM is the wrong clock (headset stays quiet or plays
397 * at the wrong speed). 44.1 kHz is the fallback. Bitpool min and max are
398 * the single selected value, which is what BlueZ puts in SetConfiguration. */
399static inline int avdtp_pick_sbc_config(const uint8_t* caps, uint8_t* use,
400 int* freq44100) {
401 uint8_t f = caps[0];
402 uint8_t b = caps[1];
403 use[0] = 0;
404 use[1] = 0;
405 if (f & A2DP_SBC_FREQ_48000) {
406 use[0] |= A2DP_SBC_FREQ_48000;
407 *freq44100 = 0;
408 } else if (f & A2DP_SBC_FREQ_44100) {
409 use[0] |= A2DP_SBC_FREQ_44100;
410 *freq44100 = 1;
411 } else {
412 return -1;
413 }
414 /* Joint stereo required by our encoder. */
415 if (!(f & A2DP_SBC_MODE_JOINT))
416 return -1;
417 use[0] |= A2DP_SBC_MODE_JOINT;
418 /* 16 blocks, 8 subbands, loudness (encoder-fixed). */
419 if (!(b & A2DP_SBC_BLK16) || !(b & A2DP_SBC_SB8) || !(b & A2DP_SBC_LOUDNESS))
420 return -1;
422 /* Bitpool inside the sink range, capped at our HQ 53. */
423 {
424 int lo = caps[2], hi = caps[3], bp = AVDTP_SBC_BITPOOL;
425 if (hi < lo)
426 return -1;
427 if (bp > hi)
428 bp = hi;
429 if (bp < lo)
430 bp = lo;
431 if (bp < 2)
432 return -1;
433 use[2] = (uint8_t)bp;
434 use[3] = (uint8_t)bp;
435 }
436 return 0;
437}
438
439/* RTP header (12 bytes) + 1-byte SBC frame count. Returns 13. */
440static inline int avdtp_build_media_hdr(uint8_t* o, uint16_t seq, uint32_t ts,
441 uint32_t ssrc, uint8_t nframes) {
442 o[0] = 0x80;
443 o[1] = AVDTP_RTP_PT_SBC;
444 o[2] = (uint8_t)(seq >> 8);
445 o[3] = (uint8_t)seq;
446 o[4] = (uint8_t)(ts >> 24);
447 o[5] = (uint8_t)(ts >> 16);
448 o[6] = (uint8_t)(ts >> 8);
449 o[7] = (uint8_t)ts;
450 o[8] = (uint8_t)(ssrc >> 24);
451 o[9] = (uint8_t)(ssrc >> 16);
452 o[10] = (uint8_t)(ssrc >> 8);
453 o[11] = (uint8_t)ssrc;
454 o[12] = nframes;
455 return 13;
456}
457
458#endif
#define signal(signum, handler)
signal – register a signal handler to a specific signal number
Definition signal.h:42
size_t b
Definition main.cpp:156
#define out(...)
Definition main.cpp:65
unsigned int uint32_t
Definition acefiex.h:163
unsigned char uint8_t
Definition acefiex.h:161
unsigned short int uint16_t
Definition acefiex.h:162
#define L2CAP_CONF_RSP
Definition avdtp.h:49
#define L2CAP_CONF_REQ
Definition avdtp.h:48
#define L2CAP_CONN_OK
Definition avdtp.h:51
#define A2DP_SBC_MODE_JOINT
Definition avdtp.h:92
#define AVDTP_SET_CONFIGURATION
Definition avdtp.h:63
#define A2DP_SBC_SB8
Definition avdtp.h:94
#define A2DP_SBC_FREQ_48000
Definition avdtp.h:91
#define AVDTP_DISCOVER
Definition avdtp.h:61
#define AVDTP_MSG_ACCEPT
Definition avdtp.h:58
#define A2DP_SBC_FREQ_44100
Definition avdtp.h:90
#define AVDTP_SUSPEND
Definition avdtp.h:69
#define L2CAP_CONF_SUCCESS
Definition avdtp.h:50
#define L2CAP_CONN_REQ
Definition avdtp.h:46
#define AVDTP_CLOSE
Definition avdtp.h:68
#define AVDTP_SBC_BITPOOL
Definition avdtp.h:103
#define AVDTP_CAT_CODEC
Definition avdtp.h:83
#define L2CAP_CONN_RSP
Definition avdtp.h:47
#define A2DP_SBC_BLK16
Definition avdtp.h:93
#define AVDTP_RTP_PT_SBC
Definition avdtp.h:97
#define A2DP_SBC_LOUDNESS
Definition avdtp.h:95
#define AVDTP_OPEN
Definition avdtp.h:66
#define AVDTP_GET_ALLCAPABILITIES
Definition avdtp.h:71
#define AVDTP_GET_CAPABILITIES
Definition avdtp.h:62
#define AVDTP_MSG_CMD
Definition avdtp.h:57
#define AVDTP_CAT_TRANSPORT
Definition avdtp.h:77
#define AVDTP_CAT_DELAYREP
Definition avdtp.h:85
#define AVDTP_START
Definition avdtp.h:67
#define A2DP_CODEC_SBC
Definition avdtp.h:87
#define L2CAP_OPT_MTU
Definition avdtp.h:53
#define AVDTP_MEDIA_AUDIO
Definition avdtp.h:75
#define AVDTP_SEP_SINK
Definition avdtp.h:74
const char * f
Definition drm_print.h:205
size_t len
Definition initial.c:23
#define max(a, b)
Definition minmax.h:28