Commit 895ba04b authored by Make Shi's avatar Make Shi Committed by Chris Friedt
Browse files

Bluetooth: AVCTP: Refactors the AVCTP to support multiple L2CAP PSMs



- This patch refactors the AVCTP layer to support multiple L2CAP PSMs
- Introduced bt_avctp_server structure for dynamic L2CAP server
  registration and new server register apis
- Added k_sem protected server list to support multiple AVCTP servers

Signed-off-by: default avatarMake Shi <make.shi@nxp.com>
parent 009114a6
Loading
Loading
Loading
Loading
+85 −76
Original line number Diff line number Diff line
@@ -31,8 +31,9 @@
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;
/* L2CAP Server list */
static sys_slist_t avctp_l2cap_server = SYS_SLIST_STATIC_INIT(&avctp_l2cap_server);
static struct k_sem avctp_server_lock;

static void avctp_l2cap_connected(struct bt_l2cap_chan *chan)
{
@@ -80,7 +81,6 @@ static int avctp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
	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;
	int err;

@@ -90,32 +90,22 @@ static int avctp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
	}

	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;
	LOG_DBG("AVCTP msg received, cr:0x%X, tid:0x%X, pid: 0x%04X",
		cr, tid, sys_be16_to_cpu(hdr->pid));

	if (sys_be16_to_cpu(hdr->pid) == session->pid) {
		return session->ops->recv(session, buf);
	}

	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) {
		if (rsp == NULL) {
			__ASSERT(0, "Failed to create AVCTP response PDU");
			return -ENOMEM;
		}

@@ -123,15 +113,13 @@ static int avctp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
		if (err < 0) {
			net_buf_unref(rsp);
			LOG_ERR("AVCTP send fail, err = %d", err);
			bt_avctp_disconnect(session);
			return err;
		}
	}
	return 0; /* No need to report to the upper layer */
}

	return session->ops->recv(session, buf);
}

static const struct bt_l2cap_chan_ops ops = {
	.connected = avctp_l2cap_connected,
	.disconnected = avctp_l2cap_disconnected,
@@ -139,17 +127,14 @@ static const struct bt_l2cap_chan_ops ops = {
	.recv = avctp_l2cap_recv,
};

int bt_avctp_connect(struct bt_conn *conn, struct bt_avctp *session)
int bt_avctp_connect(struct bt_conn *conn, uint16_t psm, struct bt_avctp *session)
{
	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);
	return bt_l2cap_chan_connect(conn, &session->br_chan.chan, psm);
}

int bt_avctp_disconnect(struct bt_avctp *session)
@@ -163,6 +148,19 @@ int bt_avctp_disconnect(struct bt_avctp *session)
	return bt_l2cap_chan_disconnect(&session->br_chan.chan);
}

void bt_avctp_set_header(struct bt_avctp_header *avctp_hdr, bt_avctp_cr_t cr,
			 bt_avctp_pkt_type_t pkt_type, bt_avctp_ipid_t ipid,
			 uint8_t tid, uint16_t pid)
{
	LOG_DBG("");

	BT_AVCTP_HDR_SET_TRANSACTION_LABLE(avctp_hdr, tid);
	BT_AVCTP_HDR_SET_PACKET_TYPE(avctp_hdr, pkt_type);
	BT_AVCTP_HDR_SET_CR(avctp_hdr, cr);
	BT_AVCTP_HDR_SET_IPID(avctp_hdr, ipid);
	avctp_hdr->pid = pid;
}

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)
@@ -179,11 +177,7 @@ struct net_buf *bt_avctp_create_pdu(struct bt_avctp *session, bt_avctp_cr_t cr,
	}

	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;
	bt_avctp_set_header(hdr, cr, pkt_type, ipid, tid, pid);

	LOG_DBG("cr:0x%lX, tid:0x%02lX", BT_AVCTP_HDR_GET_CR(hdr),
		BT_AVCTP_HDR_GET_TRANSACTION_LABLE(hdr));
@@ -195,63 +189,78 @@ int bt_avctp_send(struct bt_avctp *session, struct net_buf *buf)
	return bt_l2cap_chan_send(&session->br_chan.chan, buf);
}

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)
{
	struct bt_avctp *session = NULL;
	struct bt_avctp_server *avctp_server;
	int err;

	LOG_DBG("conn %p", conn);

	if (!event_cb) {
		LOG_WRN("AVCTP server is unsupported");
		return -ENOTSUP;
	avctp_server = CONTAINER_OF(server, struct bt_avctp_server, l2cap);

	k_sem_take(&avctp_server_lock, K_FOREVER);

	if (!sys_slist_find(&avctp_l2cap_server, &avctp_server->node, NULL)) {
		LOG_WRN("Invalid l2cap server");
		k_sem_give(&avctp_server_lock);
		return -EINVAL;
	}

	k_sem_give(&avctp_server_lock);

	/* Get the AVCTP session from upper layer */
	err = event_cb->accept(conn, &session);
	err = avctp_server->accept(conn, &session);
	if (err < 0) {
		LOG_ERR("Get the AVCTP session failed %d", err);
		LOG_ERR("Incoming connection rejected");
		return err;
	}

	session->br_chan.rx.mtu = BT_L2CAP_RX_MTU;
	session->br_chan.psm = BT_L2CAP_PSM_AVCTP;
	session->br_chan.chan.ops = &ops;
	*chan = &session->br_chan.chan;

	return 0;
}

int bt_avctp_init(void)
int bt_avctp_server_register(struct bt_avctp_server *server)
{
	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 ((server == NULL) || (server->accept == NULL)) {
		LOG_DBG("Invalid parameter");
		return -EINVAL;
	}

	k_sem_take(&avctp_server_lock, K_FOREVER);

	if (sys_slist_find(&avctp_l2cap_server, &server->node, NULL)) {
		LOG_WRN("L2CAP server has been registered");
		k_sem_give(&avctp_server_lock);
		return -EEXIST;
	}

	server->l2cap.accept = avctp_l2cap_accept;
	err = bt_l2cap_br_server_register(&server->l2cap);
	if (err < 0) {
		LOG_ERR("AVCTP L2CAP registration failed %d", err);
		k_sem_give(&avctp_server_lock);
		return err;
	}

	LOG_DBG("Register L2CAP server %p", server);
	sys_slist_append(&avctp_l2cap_server, &server->node);

	k_sem_give(&avctp_server_lock);

	return err;
}

int bt_avctp_init(void)
{
	LOG_DBG("Initializing AVCTP");
	/* Locking semaphore initialized to 1 (unlocked) */
	k_sem_init(&avctp_server_lock, 1, 1);
	return 0;
}
+53 −4
Original line number Diff line number Diff line
@@ -9,8 +9,6 @@
 * SPDX-License-Identifier: Apache-2.0
 */

#define BT_L2CAP_PSM_AVCTP 0x0017

typedef enum __packed {
	BT_AVCTP_IPID_NONE = 0b0,
	BT_AVCTP_IPID_INVALID = 0b1,
@@ -82,6 +80,52 @@ struct bt_avctp_ops_cb {
struct bt_avctp {
	struct bt_l2cap_br_chan br_chan;
	const struct bt_avctp_ops_cb *ops;
	uint16_t pid;  /** Profile Identifier */
};

/**
 * @brief AVCTP L2CAP Server structure
 *
 * This structure defines the L2CAP server used for AVCTP over L2CAP transport.
 */
struct bt_avctp_server {
	/**
	 * @brief L2CAP server parameters
	 *
	 * This field is used to register the L2CAP server. The `psm` field can be set
	 * to a specific value (not recommended), or set to 0 to allow automatic PSM
	 * allocation during registration via @ref bt_avctp_server_register.
	 *
	 * The `sec_level` field specifies the minimum required security level.
	 *
	 * @note The `struct bt_l2cap_server::accept` callback of `l2cap` can not be used
	 * by AVCTP applications. Instead, use the `struct bt_avctp_server::accept`
	 * callback defined in this structure.
	 */
	struct bt_l2cap_server l2cap;

	/**
	 * @brief Accept callback for incoming AVCTP connections
	 *
	 * This callback is invoked when a new incoming AVCTP connection is received.
	 * The application is responsible for authorizing the connection and allocating
	 * a new AVCTP session object.
	 *
	 * @warning The caller must ensure that the parent object of the AVCTP session
	 * is properly zero-initialized before use.
	 *
	 * @param conn   The Bluetooth connection requesting authorization.
	 * @param session Pointer to receive the allocated AVCTP session object.
	 *
	 * @retval 0        Success.
	 * @retval -ENOMEM  No available space for a new session.
	 * @retval -EACCES  Connection not authorized by the application.
	 * @retval -EPERM   Encryption key size is insufficient.
	 */
	int (*accept)(struct bt_conn *conn, struct bt_avctp **session);

	/** @brief Internal node for list management */
	sys_snode_t node;
};

struct bt_avctp_event_cb {
@@ -92,10 +136,10 @@ struct bt_avctp_event_cb {
int bt_avctp_init(void);

/* Application register with AVCTP layer */
int bt_avctp_register(const struct bt_avctp_event_cb *cb);
int bt_avctp_server_register(struct bt_avctp_server *server);

/* AVCTP connect */
int bt_avctp_connect(struct bt_conn *conn, struct bt_avctp *session);
int bt_avctp_connect(struct bt_conn *conn, uint16_t psm, struct bt_avctp *session);

/* AVCTP disconnect */
int bt_avctp_disconnect(struct bt_avctp *session);
@@ -105,5 +149,10 @@ 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);

/* Set AVCTP header */
void bt_avctp_set_header(struct bt_avctp_header *avctp_hdr, bt_avctp_cr_t cr,
			 bt_avctp_pkt_type_t pkt_type, bt_avctp_ipid_t ipid,
			 uint8_t tid, uint16_t pid);

/* Send AVCTP PDU */
int bt_avctp_send(struct bt_avctp *session, struct net_buf *buf);