1 /* l2cap.c - Bluetooth L2CAP Tester */
2
3 /*
4 * Copyright (c) 2016 Intel Corporation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/bluetooth/bluetooth.h>
10
11 #include <errno.h>
12 #include <zephyr/bluetooth/l2cap.h>
13 #include <zephyr/bluetooth/att.h>
14 #include <zephyr/sys/byteorder.h>
15
16 #include <zephyr/logging/log.h>
17 #define LOG_MODULE_NAME bttester_l2cap
18 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
19
20 #include "btp/btp.h"
21
22 #define L2CAP_MPS 96
23 #define DATA_MTU (3 * L2CAP_MPS)
24 #define DATA_MTU_INITIAL (2 * L2CAP_MPS)
25
26 #define CHANNELS 2
27 #define SERVERS 1
28
29 NET_BUF_POOL_FIXED_DEFINE(data_pool, CHANNELS, BT_L2CAP_SDU_BUF_SIZE(DATA_MTU),
30 CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
31
32 static bool authorize_flag;
33 static uint8_t req_keysize;
34
35 static struct channel {
36 uint8_t chan_id; /* Internal number that identifies L2CAP channel. */
37 struct bt_l2cap_le_chan le;
38 bool in_use;
39 bool hold_credit;
40 #if defined(CONFIG_BT_L2CAP_SEG_RECV)
41 unsigned int pending_credits;
42 uint8_t recv_cb_buf[DATA_MTU + sizeof(struct btp_l2cap_data_received_ev)];
43 #else
44 struct net_buf *pending_credit;
45 #endif
46 } channels[CHANNELS];
47
48 /* TODO Extend to support multiple servers */
49 static struct bt_l2cap_server servers[SERVERS];
50
51 #if defined(CONFIG_BT_L2CAP_SEG_RECV)
seg_recv_cb(struct bt_l2cap_chan * l2cap_chan,size_t sdu_len,off_t seg_offset,struct net_buf_simple * seg)52 static void seg_recv_cb(struct bt_l2cap_chan *l2cap_chan, size_t sdu_len, off_t seg_offset,
53 struct net_buf_simple *seg)
54 {
55 struct btp_l2cap_data_received_ev *ev;
56 struct bt_l2cap_le_chan *l2cap_le_chan =
57 CONTAINER_OF(l2cap_chan, struct bt_l2cap_le_chan, chan);
58 struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
59
60 ev = (void *)chan->recv_cb_buf;
61 memcpy(&ev->data[seg_offset], seg->data, seg->len);
62
63 /* complete SDU received */
64 if (seg_offset + seg->len == sdu_len) {
65 ev->chan_id = chan->chan_id;
66 ev->data_length = sys_cpu_to_le16(sdu_len);
67
68 tester_event(BTP_SERVICE_ID_L2CAP, BTP_L2CAP_EV_DATA_RECEIVED, chan->recv_cb_buf,
69 sizeof(*ev) + sdu_len);
70 }
71
72 if (chan->hold_credit) {
73 chan->pending_credits++;
74 } else {
75 bt_l2cap_chan_give_credits(l2cap_chan, 1);
76 }
77 }
78 #else
alloc_buf_cb(struct bt_l2cap_chan * chan)79 static struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
80 {
81 return net_buf_alloc(&data_pool, K_FOREVER);
82 }
83
84 static uint8_t recv_cb_buf[DATA_MTU + sizeof(struct btp_l2cap_data_received_ev)];
85
recv_cb(struct bt_l2cap_chan * l2cap_chan,struct net_buf * buf)86 static int recv_cb(struct bt_l2cap_chan *l2cap_chan, struct net_buf *buf)
87 {
88 struct btp_l2cap_data_received_ev *ev = (void *) recv_cb_buf;
89 struct bt_l2cap_le_chan *l2cap_le_chan = CONTAINER_OF(
90 l2cap_chan, struct bt_l2cap_le_chan, chan);
91 struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
92
93 ev->chan_id = chan->chan_id;
94 ev->data_length = sys_cpu_to_le16(buf->len);
95 memcpy(ev->data, buf->data, buf->len);
96
97 tester_event(BTP_SERVICE_ID_L2CAP, BTP_L2CAP_EV_DATA_RECEIVED,
98 recv_cb_buf, sizeof(*ev) + buf->len);
99
100 if (chan->hold_credit && !chan->pending_credit) {
101 /* no need for extra ref, as when returning EINPROGRESS user
102 * becomes owner of the netbuf
103 */
104 chan->pending_credit = buf;
105 return -EINPROGRESS;
106 }
107
108 return 0;
109 }
110 #endif
111
connected_cb(struct bt_l2cap_chan * l2cap_chan)112 static void connected_cb(struct bt_l2cap_chan *l2cap_chan)
113 {
114 struct btp_l2cap_connected_ev ev;
115 struct bt_l2cap_le_chan *l2cap_le_chan = CONTAINER_OF(
116 l2cap_chan, struct bt_l2cap_le_chan, chan);
117 struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
118 struct bt_conn_info info;
119
120 ev.chan_id = chan->chan_id;
121 /* TODO: ev.psm */
122 if (!bt_conn_get_info(l2cap_chan->conn, &info)) {
123 switch (info.type) {
124 case BT_CONN_TYPE_LE:
125 ev.mtu_remote = sys_cpu_to_le16(chan->le.tx.mtu);
126 ev.mps_remote = sys_cpu_to_le16(chan->le.tx.mps);
127 ev.mtu_local = sys_cpu_to_le16(chan->le.rx.mtu);
128 ev.mps_local = sys_cpu_to_le16(chan->le.rx.mps);
129 bt_addr_le_copy(&ev.address, info.le.dst);
130 break;
131 case BT_CONN_TYPE_BR:
132 default:
133 /* TODO figure out how (if) want to handle BR/EDR */
134 return;
135 }
136 }
137
138 tester_event(BTP_SERVICE_ID_L2CAP, BTP_L2CAP_EV_CONNECTED, &ev, sizeof(ev));
139 }
140
disconnected_cb(struct bt_l2cap_chan * l2cap_chan)141 static void disconnected_cb(struct bt_l2cap_chan *l2cap_chan)
142 {
143 struct btp_l2cap_disconnected_ev ev;
144 struct bt_l2cap_le_chan *l2cap_le_chan = CONTAINER_OF(
145 l2cap_chan, struct bt_l2cap_le_chan, chan);
146 struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
147 struct bt_conn_info info;
148
149 #if !defined(CONFIG_BT_L2CAP_SEG_RECV)
150 /* release netbuf on premature disconnection */
151 if (chan->pending_credit) {
152 net_buf_unref(chan->pending_credit);
153 chan->pending_credit = NULL;
154 }
155 #endif
156
157 (void)memset(&ev, 0, sizeof(struct btp_l2cap_disconnected_ev));
158
159 /* TODO: ev.result */
160 ev.chan_id = chan->chan_id;
161 /* TODO: ev.psm */
162 if (!bt_conn_get_info(l2cap_chan->conn, &info)) {
163 switch (info.type) {
164 case BT_CONN_TYPE_LE:
165 bt_addr_le_copy(&ev.address, info.le.dst);
166 break;
167 case BT_CONN_TYPE_BR:
168 default:
169 /* TODO figure out how (if) want to handle BR/EDR */
170 return;
171 }
172 }
173
174 chan->in_use = false;
175
176 tester_event(BTP_SERVICE_ID_L2CAP, BTP_L2CAP_EV_DISCONNECTED, &ev, sizeof(ev));
177 }
178
179 #if defined(CONFIG_BT_L2CAP_ECRED)
reconfigured_cb(struct bt_l2cap_chan * l2cap_chan)180 static void reconfigured_cb(struct bt_l2cap_chan *l2cap_chan)
181 {
182 struct btp_l2cap_reconfigured_ev ev;
183 struct bt_l2cap_le_chan *l2cap_le_chan = CONTAINER_OF(
184 l2cap_chan, struct bt_l2cap_le_chan, chan);
185 struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
186
187 (void)memset(&ev, 0, sizeof(ev));
188
189 ev.chan_id = chan->chan_id;
190 ev.mtu_remote = sys_cpu_to_le16(chan->le.tx.mtu);
191 ev.mps_remote = sys_cpu_to_le16(chan->le.tx.mps);
192 ev.mtu_local = sys_cpu_to_le16(chan->le.rx.mtu);
193 ev.mps_local = sys_cpu_to_le16(chan->le.rx.mps);
194
195 tester_event(BTP_SERVICE_ID_L2CAP, BTP_L2CAP_EV_RECONFIGURED, &ev, sizeof(ev));
196 }
197 #endif
198
199 static const struct bt_l2cap_chan_ops l2cap_ops = {
200 #if defined(CONFIG_BT_L2CAP_SEG_RECV)
201 .seg_recv = seg_recv_cb,
202 #else
203 .alloc_buf = alloc_buf_cb,
204 .recv = recv_cb,
205 #endif
206 .connected = connected_cb,
207 .disconnected = disconnected_cb,
208 #if defined(CONFIG_BT_L2CAP_ECRED)
209 .reconfigured = reconfigured_cb,
210 #endif
211 };
212
get_free_channel()213 static struct channel *get_free_channel()
214 {
215 uint8_t i;
216 struct channel *chan;
217
218 for (i = 0U; i < CHANNELS; i++) {
219 if (channels[i].in_use) {
220 continue;
221 }
222
223 chan = &channels[i];
224
225 (void)memset(chan, 0, sizeof(*chan));
226 chan->chan_id = i;
227
228 channels[i].in_use = true;
229
230 return chan;
231 }
232
233 return NULL;
234 }
235
connect(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)236 static uint8_t connect(const void *cmd, uint16_t cmd_len,
237 void *rsp, uint16_t *rsp_len)
238 {
239 const struct btp_l2cap_connect_cmd *cp = cmd;
240 struct btp_l2cap_connect_rp *rp = rsp;
241 struct bt_conn *conn;
242 struct channel *chan = NULL;
243 struct bt_l2cap_chan *allocated_channels[5] = {};
244 uint16_t mtu = sys_le16_to_cpu(cp->mtu);
245 uint16_t psm = sys_le16_to_cpu(cp->psm);
246 uint8_t i = 0;
247 bool ecfc = cp->options & BTP_L2CAP_CONNECT_OPT_ECFC;
248 int err;
249
250 if (cp->num == 0 || cp->num > CHANNELS || mtu > DATA_MTU_INITIAL) {
251 return BTP_STATUS_FAILED;
252 }
253
254 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
255 if (!conn) {
256 return BTP_STATUS_FAILED;
257 }
258
259 for (i = 0U; i < cp->num; i++) {
260 chan = get_free_channel();
261 if (!chan) {
262 goto fail;
263 }
264 chan->le.chan.ops = &l2cap_ops;
265 chan->le.rx.mtu = mtu;
266 #if defined(CONFIG_BT_L2CAP_SEG_RECV)
267 chan->le.rx.mps = L2CAP_MPS;
268 #endif
269 rp->chan_id[i] = chan->chan_id;
270 allocated_channels[i] = &chan->le.chan;
271
272 chan->hold_credit = cp->options & BTP_L2CAP_CONNECT_OPT_HOLD_CREDIT;
273
274 bt_l2cap_chan_give_credits(&chan->le.chan, 1);
275 }
276
277 if (cp->num == 1 && !ecfc) {
278 err = bt_l2cap_chan_connect(conn, &chan->le.chan, psm);
279 if (err < 0) {
280 goto fail;
281 }
282 } else if (ecfc) {
283 #if defined(CONFIG_BT_L2CAP_ECRED)
284 err = bt_l2cap_ecred_chan_connect(conn, allocated_channels,
285 psm);
286 if (err < 0) {
287 goto fail;
288 }
289 #else
290 goto fail;
291 #endif
292 } else {
293 LOG_ERR("Invalid 'num' parameter value");
294 goto fail;
295 }
296
297 rp->num = cp->num;
298 *rsp_len = sizeof(*rp) + (rp->num * sizeof(rp->chan_id[0]));
299
300 return BTP_STATUS_SUCCESS;
301
302 fail:
303 for (i = 0U; i < ARRAY_SIZE(allocated_channels); i++) {
304 if (allocated_channels[i]) {
305 channels[BT_L2CAP_LE_CHAN(allocated_channels[i])->ident].in_use = false;
306 }
307 }
308 return BTP_STATUS_FAILED;
309 }
310
disconnect(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)311 static uint8_t disconnect(const void *cmd, uint16_t cmd_len,
312 void *rsp, uint16_t *rsp_len)
313 {
314 const struct btp_l2cap_disconnect_cmd *cp = cmd;
315 struct channel *chan;
316 int err;
317
318 if (cp->chan_id >= CHANNELS) {
319 return BTP_STATUS_FAILED;
320 }
321
322 chan = &channels[cp->chan_id];
323
324 err = bt_l2cap_chan_disconnect(&chan->le.chan);
325 if (err) {
326 return BTP_STATUS_FAILED;
327 }
328
329 return BTP_STATUS_SUCCESS;
330 }
331
332 #if defined(CONFIG_BT_L2CAP_ECRED)
reconfigure(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)333 static uint8_t reconfigure(const void *cmd, uint16_t cmd_len,
334 void *rsp, uint16_t *rsp_len)
335 {
336 const struct btp_l2cap_reconfigure_cmd *cp = cmd;
337 uint16_t mtu;
338 uint16_t mps;
339 struct bt_conn *conn;
340 int err;
341 struct bt_l2cap_chan *reconf_channels[CHANNELS + 1] = {};
342
343 if (cmd_len < sizeof(*cp) ||
344 cmd_len != sizeof(*cp) + cp->num) {
345 return BTP_STATUS_FAILED;
346 }
347
348 if (cp->num > CHANNELS) {
349 return BTP_STATUS_FAILED;
350 }
351
352 mtu = sys_le16_to_cpu(cp->mtu);
353 if (mtu > DATA_MTU) {
354 return BTP_STATUS_FAILED;
355 }
356
357 for (int i = 0; i < cp->num; i++) {
358 if (cp->chan_id[i] > CHANNELS) {
359 return BTP_STATUS_FAILED;
360 }
361
362 reconf_channels[i] = &channels[cp->chan_id[i]].le.chan;
363 }
364
365 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
366 if (!conn) {
367 LOG_ERR("Unknown connection");
368 return BTP_STATUS_FAILED;
369 }
370
371 mps = MIN(L2CAP_MPS, BT_L2CAP_RX_MTU);
372 err = bt_l2cap_ecred_chan_reconfigure_explicit(reconf_channels, cp->num, mtu, mps);
373 if (err) {
374 bt_conn_unref(conn);
375 return BTP_STATUS_FAILED;
376 }
377
378 bt_conn_unref(conn);
379 return BTP_STATUS_SUCCESS;
380 }
381 #endif
382
383 #if defined(CONFIG_BT_EATT)
disconnect_eatt_chans(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)384 static uint8_t disconnect_eatt_chans(const void *cmd, uint16_t cmd_len,
385 void *rsp, uint16_t *rsp_len)
386 {
387 const struct btp_l2cap_disconnect_eatt_chans_cmd *cp = cmd;
388 struct bt_conn *conn;
389 int err;
390
391 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
392 if (!conn) {
393 LOG_ERR("Unknown connection");
394 return BTP_STATUS_FAILED;
395 }
396
397 for (int i = 0; i < cp->count; i++) {
398 err = bt_eatt_disconnect_one(conn);
399 if (err) {
400 bt_conn_unref(conn);
401 return BTP_STATUS_FAILED;
402 }
403 }
404
405 bt_conn_unref(conn);
406 return BTP_STATUS_SUCCESS;
407 }
408 #endif
409
410
send_data(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)411 static uint8_t send_data(const void *cmd, uint16_t cmd_len,
412 void *rsp, uint16_t *rsp_len)
413 {
414 const struct btp_l2cap_send_data_cmd *cp = cmd;
415 struct channel *chan;
416 struct net_buf *buf;
417 uint16_t data_len;
418 int ret;
419
420 if (cmd_len < sizeof(*cp) ||
421 cmd_len != sizeof(*cp) + sys_le16_to_cpu(cp->data_len)) {
422 return BTP_STATUS_FAILED;
423 }
424
425 if (cp->chan_id >= CHANNELS) {
426 return BTP_STATUS_FAILED;
427 }
428
429 chan = &channels[cp->chan_id];
430 data_len = sys_le16_to_cpu(cp->data_len);
431
432
433 /* FIXME: For now, fail if data length exceeds buffer length */
434 if (data_len > DATA_MTU) {
435 return BTP_STATUS_FAILED;
436 }
437
438 /* FIXME: For now, fail if data length exceeds remote's L2CAP SDU */
439 if (data_len > chan->le.tx.mtu) {
440 return BTP_STATUS_FAILED;
441 }
442
443 buf = net_buf_alloc(&data_pool, K_FOREVER);
444 net_buf_reserve(buf, BT_L2CAP_SDU_CHAN_SEND_RESERVE);
445
446 net_buf_add_mem(buf, cp->data, data_len);
447 ret = bt_l2cap_chan_send(&chan->le.chan, buf);
448 if (ret < 0) {
449 LOG_ERR("Unable to send data: %d", -ret);
450 net_buf_unref(buf);
451 return BTP_STATUS_FAILED;
452 }
453
454 return BTP_STATUS_SUCCESS;
455 }
456
get_free_server(void)457 static struct bt_l2cap_server *get_free_server(void)
458 {
459 uint8_t i;
460
461 for (i = 0U; i < SERVERS ; i++) {
462 if (servers[i].psm) {
463 continue;
464 }
465
466 return &servers[i];
467 }
468
469 return NULL;
470 }
471
is_free_psm(uint16_t psm)472 static bool is_free_psm(uint16_t psm)
473 {
474 uint8_t i;
475
476 for (i = 0U; i < ARRAY_SIZE(servers); i++) {
477 if (servers[i].psm == psm) {
478 return false;
479 }
480 }
481
482 return true;
483 }
484
accept(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** l2cap_chan)485 static int accept(struct bt_conn *conn, struct bt_l2cap_server *server,
486 struct bt_l2cap_chan **l2cap_chan)
487 {
488 struct channel *chan;
489
490 if (bt_conn_enc_key_size(conn) < req_keysize) {
491 return -EPERM;
492 }
493
494 if (authorize_flag) {
495 return -EACCES;
496 }
497
498 chan = get_free_channel();
499 if (!chan) {
500 return -ENOMEM;
501 }
502
503 chan->le.chan.ops = &l2cap_ops;
504 chan->le.rx.mtu = DATA_MTU_INITIAL;
505 #if defined(CONFIG_BT_L2CAP_SEG_RECV)
506 chan->le.rx.mps = L2CAP_MPS;
507 #endif
508
509 *l2cap_chan = &chan->le.chan;
510
511 bt_l2cap_chan_give_credits(&chan->le.chan, 1);
512
513 return 0;
514 }
515
listen(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)516 static uint8_t listen(const void *cmd, uint16_t cmd_len,
517 void *rsp, uint16_t *rsp_len)
518 {
519 const struct btp_l2cap_listen_cmd *cp = cmd;
520 struct bt_l2cap_server *server;
521 uint16_t psm = sys_le16_to_cpu(cp->psm);
522
523 /* TODO: Handle cmd->transport flag */
524
525 if (psm == 0 || !is_free_psm(psm)) {
526 return BTP_STATUS_FAILED;
527 }
528
529 server = get_free_server();
530 if (!server) {
531 return BTP_STATUS_FAILED;
532 }
533
534 server->accept = accept;
535 server->psm = psm;
536
537 switch (cp->response) {
538 case BTP_L2CAP_CONNECTION_RESPONSE_SUCCESS:
539 break;
540 case BTP_L2CAP_CONNECTION_RESPONSE_INSUFF_ENC_KEY:
541 /* TSPX_psm_encryption_key_size_required */
542 req_keysize = 16;
543 break;
544 case BTP_L2CAP_CONNECTION_RESPONSE_INSUFF_AUTHOR:
545 authorize_flag = true;
546 break;
547 case BTP_L2CAP_CONNECTION_RESPONSE_INSUFF_AUTHEN:
548 server->sec_level = BT_SECURITY_L3;
549 break;
550 case BTP_L2CAP_CONNECTION_RESPONSE_INSUFF_ENCRYPTION:
551 server->sec_level = BT_SECURITY_L2;
552 break;
553 default:
554 return BTP_STATUS_FAILED;
555 }
556
557 if (bt_l2cap_server_register(server) < 0) {
558 server->psm = 0U;
559 return BTP_STATUS_FAILED;
560 }
561
562 return BTP_STATUS_SUCCESS;
563 }
564
credits(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)565 static uint8_t credits(const void *cmd, uint16_t cmd_len,
566 void *rsp, uint16_t *rsp_len)
567 {
568 const struct btp_l2cap_credits_cmd *cp = cmd;
569 struct channel *chan;
570
571 if (cp->chan_id >= CHANNELS) {
572 return BTP_STATUS_FAILED;
573 }
574
575 chan = &channels[cp->chan_id];
576
577 if (!chan->in_use) {
578 return BTP_STATUS_FAILED;
579 }
580 #if defined(CONFIG_BT_L2CAP_SEG_RECV)
581 if (chan->pending_credits) {
582 if (bt_l2cap_chan_give_credits(&chan->le.chan, chan->pending_credits) < 0) {
583 return BTP_STATUS_FAILED;
584 }
585
586 chan->pending_credits = 0;
587 }
588 #else
589 if (chan->pending_credit) {
590 if (bt_l2cap_chan_recv_complete(&chan->le.chan,
591 chan->pending_credit) < 0) {
592 return BTP_STATUS_FAILED;
593 }
594
595 chan->pending_credit = NULL;
596 }
597 #endif
598
599 return BTP_STATUS_SUCCESS;
600 }
601
supported_commands(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)602 static uint8_t supported_commands(const void *cmd, uint16_t cmd_len,
603 void *rsp, uint16_t *rsp_len)
604 {
605 struct btp_l2cap_read_supported_commands_rp *rp = rsp;
606
607 /* octet 0 */
608 tester_set_bit(rp->data, BTP_L2CAP_READ_SUPPORTED_COMMANDS);
609 tester_set_bit(rp->data, BTP_L2CAP_CONNECT);
610 tester_set_bit(rp->data, BTP_L2CAP_DISCONNECT);
611 tester_set_bit(rp->data, BTP_L2CAP_SEND_DATA);
612 tester_set_bit(rp->data, BTP_L2CAP_LISTEN);
613 #if defined(CONFIG_BT_L2CAP_ECRED)
614 tester_set_bit(rp->data, BTP_L2CAP_RECONFIGURE);
615 #endif
616 /* octet 1 */
617 tester_set_bit(rp->data, BTP_L2CAP_CREDITS);
618 #if defined(CONFIG_BT_EATT)
619 tester_set_bit(rp->data, BTP_L2CAP_DISCONNECT_EATT_CHANS);
620 #endif
621
622 *rsp_len = sizeof(*rp) + 2;
623
624 return BTP_STATUS_SUCCESS;
625 }
626
627 static const struct btp_handler handlers[] = {
628 {
629 .opcode = BTP_L2CAP_READ_SUPPORTED_COMMANDS,
630 .index = BTP_INDEX_NONE,
631 .expect_len = 0,
632 .func = supported_commands,
633 },
634 {
635 .opcode = BTP_L2CAP_CONNECT,
636 .expect_len = sizeof(struct btp_l2cap_connect_cmd),
637 .func = connect,
638 },
639 {
640 .opcode = BTP_L2CAP_DISCONNECT,
641 .expect_len = sizeof(struct btp_l2cap_disconnect_cmd),
642 .func = disconnect,
643 },
644 {
645 .opcode = BTP_L2CAP_SEND_DATA,
646 .expect_len = BTP_HANDLER_LENGTH_VARIABLE,
647 .func = send_data,
648 },
649 {
650 .opcode = BTP_L2CAP_LISTEN,
651 .expect_len = sizeof(struct btp_l2cap_listen_cmd),
652 .func = listen,
653 },
654 {
655 .opcode = BTP_L2CAP_RECONFIGURE,
656 .expect_len = BTP_HANDLER_LENGTH_VARIABLE,
657 .func = reconfigure,
658 },
659 {
660 .opcode = BTP_L2CAP_CREDITS,
661 .expect_len = sizeof(struct btp_l2cap_credits_cmd),
662 .func = credits,
663 },
664 {
665 .opcode = BTP_L2CAP_DISCONNECT_EATT_CHANS,
666 .expect_len = sizeof(struct btp_l2cap_disconnect_eatt_chans_cmd),
667 .func = disconnect_eatt_chans,
668 },
669 };
670
tester_init_l2cap(void)671 uint8_t tester_init_l2cap(void)
672 {
673 tester_register_command_handlers(BTP_SERVICE_ID_L2CAP, handlers,
674 ARRAY_SIZE(handlers));
675
676 return BTP_STATUS_SUCCESS;
677 }
678
tester_unregister_l2cap(void)679 uint8_t tester_unregister_l2cap(void)
680 {
681 return BTP_STATUS_SUCCESS;
682 }
683