zephyr/subsys/bluetooth/host/classic/avctp.c
Zihao Gao 498f81e45f Bluetooth: AVCTP: add error handling for fragmented message.
Fragmented AVCTP message is not supported now. Therefore
A error message is printed.

Signed-off-by: Zihao Gao <gaozihao@xiaomi.com>
2024-11-18 13:19:07 -05:00

244 lines
5.3 KiB
C

/** @file
* @brief Audio Video Control Transport Protocol
*/
/*
* Copyright (c) 2015-2016 Intel Corporation
* Copyright (C) 2024 Xiaomi Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/classic/sdp.h>
#include "avctp_internal.h"
#include "host/hci_core.h"
#include "host/conn_internal.h"
#include "l2cap_br_internal.h"
#define LOG_LEVEL CONFIG_BT_AVCTP_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_avctp);
#define AVCTP_CHAN(_ch) CONTAINER_OF(_ch, struct bt_avctp, br_chan.chan)
static const struct bt_avctp_event_cb *event_cb;
static void avctp_l2cap_connected(struct bt_l2cap_chan *chan)
{
struct bt_avctp *session;
if (!chan) {
LOG_ERR("Invalid AVCTP chan");
return;
}
session = AVCTP_CHAN(chan);
LOG_DBG("chan %p session %p", chan, session);
if (session->ops && session->ops->connected) {
session->ops->connected(session);
}
}
static void avctp_l2cap_disconnected(struct bt_l2cap_chan *chan)
{
struct bt_avctp *session;
if (!chan) {
LOG_ERR("Invalid AVCTP chan");
return;
}
session = AVCTP_CHAN(chan);
LOG_DBG("chan %p session %p", chan, session);
session->br_chan.chan.conn = NULL;
if (session->ops && session->ops->disconnected) {
session->ops->disconnected(session);
}
}
static void avctp_l2cap_encrypt_changed(struct bt_l2cap_chan *chan, uint8_t status)
{
LOG_DBG("");
}
static int avctp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
struct net_buf *rsp;
struct bt_avctp *session = AVCTP_CHAN(chan);
struct bt_avctp_header *hdr = (void *)buf->data;
uint8_t tid;
bt_avctp_pkt_type_t pkt_type;
bt_avctp_cr_t cr;
if (buf->len < sizeof(*hdr)) {
LOG_ERR("invalid AVCTP header received");
return -EINVAL;
}
tid = BT_AVCTP_HDR_GET_TRANSACTION_LABLE(hdr);
pkt_type = BT_AVCTP_HDR_GET_PACKET_TYPE(hdr);
cr = BT_AVCTP_HDR_GET_CR(hdr);
switch (pkt_type) {
case BT_AVCTP_PKT_TYPE_SINGLE:
break;
case BT_AVCTP_PKT_TYPE_START:
case BT_AVCTP_PKT_TYPE_CONTINUE:
case BT_AVCTP_PKT_TYPE_END:
default:
LOG_ERR("fragmented AVCTP message is not supported, pkt_type = %d", pkt_type);
return -EINVAL;
}
switch (hdr->pid) {
#if defined(CONFIG_BT_AVRCP)
case sys_cpu_to_be16(BT_SDP_AV_REMOTE_SVCLASS):
break;
#endif
default:
LOG_ERR("unsupported AVCTP PID received: 0x%04x", sys_be16_to_cpu(hdr->pid));
if (cr == BT_AVCTP_CMD) {
rsp = bt_avctp_create_pdu(session, BT_AVCTP_RESPONSE,
BT_AVCTP_PKT_TYPE_SINGLE, BT_AVCTP_IPID_INVALID,
&tid, hdr->pid);
if (!rsp) {
return -ENOMEM;
}
return bt_avctp_send(session, rsp);
}
return 0; /* No need to report to the upper layer */
}
return session->ops->recv(session, buf);
}
int bt_avctp_connect(struct bt_conn *conn, struct bt_avctp *session)
{
static const struct bt_l2cap_chan_ops ops = {
.connected = avctp_l2cap_connected,
.disconnected = avctp_l2cap_disconnected,
.encrypt_change = avctp_l2cap_encrypt_changed,
.recv = avctp_l2cap_recv,
};
if (!session) {
return -EINVAL;
}
session->br_chan.rx.mtu = BT_L2CAP_RX_MTU;
session->br_chan.chan.ops = &ops;
session->br_chan.required_sec_level = BT_SECURITY_L2;
return bt_l2cap_chan_connect(conn, &session->br_chan.chan, BT_L2CAP_PSM_AVCTP);
}
int bt_avctp_disconnect(struct bt_avctp *session)
{
if (!session) {
return -EINVAL;
}
LOG_DBG("session %p", session);
return bt_l2cap_chan_disconnect(&session->br_chan.chan);
}
struct net_buf *bt_avctp_create_pdu(struct bt_avctp *session, bt_avctp_cr_t cr,
bt_avctp_pkt_type_t pkt_type, bt_avctp_ipid_t ipid,
uint8_t *tid, uint16_t pid)
{
struct net_buf *buf;
struct bt_avctp_header *hdr;
LOG_DBG("");
buf = bt_l2cap_create_pdu(NULL, 0);
if (!buf) {
LOG_ERR("No buff available");
return buf;
}
hdr = net_buf_add(buf, sizeof(*hdr));
BT_AVCTP_HDR_SET_TRANSACTION_LABLE(hdr, *tid);
BT_AVCTP_HDR_SET_PACKET_TYPE(hdr, pkt_type);
BT_AVCTP_HDR_SET_CR(hdr, cr);
BT_AVCTP_HDR_SET_IPID(hdr, ipid);
hdr->pid = pid;
if (cr == BT_AVCTP_CMD) {
*tid = (*tid + 1) & 0x0F; /* Incremented by one */
}
LOG_DBG("cr:0x%lX, tid:0x%02lX", BT_AVCTP_HDR_GET_CR(hdr),
BT_AVCTP_HDR_GET_TRANSACTION_LABLE(hdr));
return buf;
}
int bt_avctp_send(struct bt_avctp *session, struct net_buf *buf)
{
int err;
err = bt_l2cap_chan_send(&session->br_chan.chan, buf);
if (err < 0) {
net_buf_unref(buf);
LOG_ERR("L2CAP send fail err = %d", err);
return err;
}
return err;
}
int bt_avctp_register(const struct bt_avctp_event_cb *cb)
{
LOG_DBG("");
if (event_cb) {
return -EALREADY;
}
event_cb = cb;
return 0;
}
static int avctp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *server,
struct bt_l2cap_chan **chan)
{
/* TODO */
return -ENOTSUP;
}
int bt_avctp_init(void)
{
int err;
static struct bt_l2cap_server avctp_l2cap = {
.psm = BT_L2CAP_PSM_AVCTP,
.sec_level = BT_SECURITY_L2,
.accept = avctp_l2cap_accept,
};
LOG_DBG("");
/* Register AVCTP PSM with L2CAP */
err = bt_l2cap_br_server_register(&avctp_l2cap);
if (err < 0) {
LOG_ERR("AVCTP L2CAP registration failed %d", err);
}
return err;
}