diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 0709fb3212f969..b7533dff3d01e5 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -2579,33 +2579,51 @@ static int btintel_get_codec_config_data(struct hci_dev *hdev, *ven_len = 0; *ven_data = NULL; - if (link != ESCO_LINK) { - bt_dev_err(hdev, "Invalid link type(%u)", link); - return -EINVAL; - } - *ven_data = kmalloc(sizeof(__u8), GFP_KERNEL); if (!*ven_data) { err = -ENOMEM; goto error; } - /* supports only CVSD and mSBC offload codecs */ - switch (codec->id) { - case 0x02: - **ven_data = 0x00; + switch (link) { + case ESCO_LINK: + case SCO_LINK: + switch (codec->id) { + case 0x00: + case 0x01: + case 0x02: + case 0x04: + case 0x03: + **ven_data = 0x00; + break; + case 0x05: + **ven_data = 0x01; + break; + default: + err = -EINVAL; + bt_dev_err(hdev, "Invalid SCO codec id(%u)", codec->id); + goto error; + } + break; + case ACL_LINK: + /* Preset 0x22 configures 48 kHz 16-bit stereo SSP offload */ + **ven_data = 0x22; break; - case 0x05: + case CIS_LINK: + case BIS_LINK: + /* Preset 0x01 configures 16 kHz 16-bit SSP offload */ **ven_data = 0x01; break; default: err = -EINVAL; - bt_dev_err(hdev, "Invalid codec id(%u)", codec->id); + bt_dev_err(hdev, "Invalid link type(%u)", link); goto error; } + /* codec and its capabilities are pre-defined to ids * preset id = 0x00 represents CVSD codec with sampling rate 8K - * preset id = 0x01 represents mSBC codec with sampling rate 16K + * preset id = 0x01 represents mSBC/16K mono SCO codec + * preset id = 0x22 represents 48K stereo A2DP / LE Audio offload */ *ven_len = sizeof(__u8); return err; @@ -2625,10 +2643,21 @@ static int btintel_get_data_path_id(struct hci_dev *hdev, __u8 *data_path_id) static int btintel_configure_offload(struct hci_dev *hdev) { + static const u8 ddc_offload_enable[] = { + /* DDC 0x0130: enable LC3, SCO, A2DP SBC/AAC, LE Audio, and delay reporting */ + 0x03, 0x30, 0x01, 0x3f, + /* DDC 0x0120: enable SCO (BIT(0)), LE ISO (BIT(1)), and A2DP (BIT(3)) */ + 0x0a, 0x20, 0x01, 0x0b, 0x00, 0x81, 0xf1, 0x01, 0x00, 0x00, 0x00, + }; struct sk_buff *skb; int err = 0; struct intel_offload_use_cases *use_cases; + skb = __hci_cmd_sync(hdev, 0xfc8b, sizeof(ddc_offload_enable), + ddc_offload_enable, HCI_INIT_TIMEOUT); + if (!IS_ERR(skb)) + kfree_skb(skb); + skb = __hci_cmd_sync(hdev, 0xfc86, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading offload use cases failed (%ld)", @@ -2648,9 +2677,10 @@ static int btintel_configure_offload(struct hci_dev *hdev) goto error; } - if (use_cases->preset[0] & 0x03) { + if (use_cases->preset[0] & 0x0b) { hdev->get_data_path_id = btintel_get_data_path_id; hdev->get_codec_config_data = btintel_get_codec_config_data; + hci_dev_set_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED); } error: kfree_skb(skb); @@ -3650,9 +3680,6 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev, */ btintel_load_ddc_config(hdev, ddcname); - /* Read supported use cases and set callbacks to fetch datapath id */ - btintel_configure_offload(hdev); - hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT); /* Send sar values to controller */ @@ -3672,6 +3699,12 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev, *ver = new_ver; finish: + /* Read supported use cases and set callbacks to fetch datapath id. + * Run this in finish: so warm reboots (where ver->img_type is already + * BTINTEL_IMG_OP) also register offload callbacks. + */ + btintel_configure_offload(hdev); + /* Set the event mask for Intel specific vendor events. This enables * a few extra events that are useful during general operation. It * does not enable any debugging related events. diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index cd918924456668..bbf19d492fbe89 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -247,6 +247,8 @@ struct bt_codecs { #define BT_CODEC_CVSD 0x02 #define BT_CODEC_TRANSPARENT 0x03 #define BT_CODEC_MSBC 0x05 +#define BT_CODEC_LC3 0x06 +#define BT_CODEC_VENDOR 0xff #define BT_ISO_BASE 20 diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 9dc714d9145eea..766ddc8a7540e2 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1023,6 +1023,34 @@ struct hci_cp_enhanced_setup_sync_conn { __u8 retrans_effort; } __packed; +#define HCI_OP_ENHANCED_ACCEPT_SYNC_CONN 0x043e +struct hci_cp_enhanced_accept_sync_conn { + bdaddr_t bdaddr; + __le32 tx_bandwidth; + __le32 rx_bandwidth; + struct hci_coding_format tx_coding_format; + struct hci_coding_format rx_coding_format; + __le16 tx_codec_frame_size; + __le16 rx_codec_frame_size; + __le32 in_bandwidth; + __le32 out_bandwidth; + struct hci_coding_format in_coding_format; + struct hci_coding_format out_coding_format; + __le16 in_coded_data_size; + __le16 out_coded_data_size; + __u8 in_pcm_data_format; + __u8 out_pcm_data_format; + __u8 in_pcm_sample_payload_msb_pos; + __u8 out_pcm_sample_payload_msb_pos; + __u8 in_data_path; + __u8 out_data_path; + __u8 in_transport_unit_size; + __u8 out_transport_unit_size; + __le16 max_latency; + __le16 pkt_type; + __u8 retrans_effort; +} __packed; + struct hci_rp_logical_link_cancel { __u8 status; __u8 phy_handle; @@ -3484,7 +3512,10 @@ static inline __u16 hci_acl_dlen(const struct sk_buff *skb) #define hci_iso_data_flags(h) ((h) >> 14) /* codec transport types */ +#define HCI_TRANSPORT_ACL 0x00 #define HCI_TRANSPORT_SCO_ESCO 0x01 +#define HCI_TRANSPORT_LE_CIS 0x02 +#define HCI_TRANSPORT_LE_BIS 0x03 /* le24 support */ static inline void hci_cpu_to_le24(__u32 val, __u8 dst[3]) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index aa04dec4515341..e3ad97bb622e55 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1631,7 +1631,10 @@ static inline bool hci_is_le_conn_scanning(struct hci_dev *hdev) } int hci_disconnect(struct hci_conn *conn, __u8 reason); +int hci_configure_datapath(struct hci_dev *hdev, __u8 link, + struct bt_codec *codec); bool hci_setup_sync(struct hci_conn *conn, __u16 handle); +bool hci_accept_sync(struct hci_conn *conn); void hci_sco_setup(struct hci_conn *conn, __u8 status); bool hci_iso_setup_path(struct hci_conn *conn); int hci_le_create_cis_pending(struct hci_dev *hdev); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 54eddcdd341f98..1cfefefa77dc9a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -531,6 +531,7 @@ struct l2cap_chan { __u8 mode; __u8 chan_type; __u8 chan_policy; + __u16 avdtp_handle; __u8 sec_level; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 29da3fe2b95384..490524325702cc 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -229,7 +229,52 @@ static bool find_next_esco_param(struct hci_conn *conn, return conn->attempt <= size; } -static int configure_datapath_sync(struct hci_dev *hdev, struct bt_codec *codec) +int hci_configure_datapath(struct hci_dev *hdev, __u8 link, + struct bt_codec *codec) +{ + int err; + __u8 vnd_len, *vnd_data = NULL; + struct hci_op_configure_data_path *cmd = NULL; + + if (!codec->data_path || !hdev->get_codec_config_data) + return 0; + + err = hdev->get_codec_config_data(hdev, link, codec, &vnd_len, + &vnd_data); + if (err < 0) + goto error; + + cmd = kzalloc(sizeof(*cmd) + vnd_len, GFP_ATOMIC); + if (!cmd) { + err = -ENOMEM; + goto error; + } + + err = hdev->get_data_path_id(hdev, &cmd->data_path_id); + if (err < 0) + goto error; + + cmd->vnd_len = vnd_len; + memcpy(cmd->vnd_data, vnd_data, vnd_len); + + cmd->direction = 0x00; + err = hci_send_cmd(hdev, HCI_CONFIGURE_DATA_PATH, + sizeof(*cmd) + vnd_len, cmd); + if (err < 0) + goto error; + + cmd->direction = 0x01; + err = hci_send_cmd(hdev, HCI_CONFIGURE_DATA_PATH, + sizeof(*cmd) + vnd_len, cmd); + +error: + kfree(cmd); + kfree(vnd_data); + return err; +} + +static int configure_datapath_sync(struct hci_dev *hdev, __u8 link, + struct bt_codec *codec) { int err; __u8 vnd_len, *vnd_data = NULL; @@ -242,7 +287,7 @@ static int configure_datapath_sync(struct hci_dev *hdev, struct bt_codec *codec) if (!codec->data_path || !hdev->get_codec_config_data) return 0; - err = hdev->get_codec_config_data(hdev, ESCO_LINK, codec, &vnd_len, + err = hdev->get_codec_config_data(hdev, link, codec, &vnd_len, &vnd_data); if (err < 0) goto error; @@ -288,7 +333,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) bt_dev_dbg(hdev, "hcon %p", conn); - configure_datapath_sync(hdev, &conn->codec); + configure_datapath_sync(hdev, ESCO_LINK, &conn->codec); conn->state = BT_CONNECT; conn->out = true; @@ -357,6 +402,8 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) cp.out_transport_unit_size = 1; break; + case 0x00: /* u-law */ + case 0x01: /* A-law */ case BT_CODEC_CVSD: if (conn->parent && lmp_esco_capable(conn->parent)) { if (!find_next_esco_param(conn, esco_param_cvsd, @@ -368,8 +415,8 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) goto unlock; param = &sco_param_cvsd[conn->attempt - 1]; } - cp.tx_coding_format.id = 2; - cp.rx_coding_format.id = 2; + cp.tx_coding_format.id = conn->codec.id; + cp.rx_coding_format.id = conn->codec.id; cp.tx_codec_frame_size = __cpu_to_le16(60); cp.rx_codec_frame_size = __cpu_to_le16(60); cp.in_bandwidth = __cpu_to_le32(16000); @@ -495,6 +542,138 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle) return hci_setup_sync_conn(conn, handle); } +static int hci_enhanced_accept_sync(struct hci_dev *hdev, void *data) +{ + struct hci_conn *conn = data; + struct hci_cp_enhanced_accept_sync_conn cp; + + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + + bt_dev_dbg(hdev, "hcon %p", conn); + + configure_datapath_sync(hdev, ESCO_LINK, &conn->codec); + + memset(&cp, 0x00, sizeof(cp)); + + bacpy(&cp.bdaddr, &conn->dst); + cp.pkt_type = cpu_to_le16(conn->pkt_type); + cp.tx_bandwidth = cpu_to_le32(0x00001f40); + cp.rx_bandwidth = cpu_to_le32(0x00001f40); + + switch (conn->codec.id) { + case BT_CODEC_MSBC: + cp.tx_coding_format.id = 0x05; + cp.rx_coding_format.id = 0x05; + cp.tx_codec_frame_size = __cpu_to_le16(60); + cp.rx_codec_frame_size = __cpu_to_le16(60); + cp.in_bandwidth = __cpu_to_le32(32000); + cp.out_bandwidth = __cpu_to_le32(32000); + cp.in_coding_format.id = 0x04; + cp.out_coding_format.id = 0x04; + cp.in_coded_data_size = __cpu_to_le16(16); + cp.out_coded_data_size = __cpu_to_le16(16); + cp.in_pcm_data_format = 2; + cp.out_pcm_data_format = 2; + cp.in_pcm_sample_payload_msb_pos = 0; + cp.out_pcm_sample_payload_msb_pos = 0; + cp.in_data_path = conn->codec.data_path; + cp.out_data_path = conn->codec.data_path; + cp.in_transport_unit_size = 1; + cp.out_transport_unit_size = 1; + if (conn->pkt_type & ESCO_2EV3) + cp.max_latency = cpu_to_le16(0x0008); + else + cp.max_latency = cpu_to_le16(0x000D); + cp.retrans_effort = 0x02; + break; + + case BT_CODEC_TRANSPARENT: + cp.tx_coding_format.id = 0x03; + cp.rx_coding_format.id = 0x03; + cp.tx_codec_frame_size = __cpu_to_le16(60); + cp.rx_codec_frame_size = __cpu_to_le16(60); + cp.in_bandwidth = __cpu_to_le32(0x1f40); + cp.out_bandwidth = __cpu_to_le32(0x1f40); + cp.in_coding_format.id = 0x03; + cp.out_coding_format.id = 0x03; + cp.in_coded_data_size = __cpu_to_le16(16); + cp.out_coded_data_size = __cpu_to_le16(16); + cp.in_pcm_data_format = 2; + cp.out_pcm_data_format = 2; + cp.in_pcm_sample_payload_msb_pos = 0; + cp.out_pcm_sample_payload_msb_pos = 0; + cp.in_data_path = conn->codec.data_path; + cp.out_data_path = conn->codec.data_path; + cp.in_transport_unit_size = 1; + cp.out_transport_unit_size = 1; + if (conn->pkt_type & ESCO_2EV3) + cp.max_latency = cpu_to_le16(0x0008); + else + cp.max_latency = cpu_to_le16(0x000D); + cp.retrans_effort = 0x02; + break; + + case 0x00: /* u-law */ + case 0x01: /* A-law */ + case BT_CODEC_CVSD: + default: + cp.tx_coding_format.id = (conn->codec.id <= 0x02) ? conn->codec.id : 2; + cp.rx_coding_format.id = (conn->codec.id <= 0x02) ? conn->codec.id : 2; + cp.tx_codec_frame_size = __cpu_to_le16(60); + cp.rx_codec_frame_size = __cpu_to_le16(60); + cp.in_bandwidth = __cpu_to_le32(16000); + cp.out_bandwidth = __cpu_to_le32(16000); + cp.in_coding_format.id = 4; + cp.out_coding_format.id = 4; + cp.in_coded_data_size = __cpu_to_le16(16); + cp.out_coded_data_size = __cpu_to_le16(16); + cp.in_pcm_data_format = 2; + cp.out_pcm_data_format = 2; + cp.in_pcm_sample_payload_msb_pos = 0; + cp.out_pcm_sample_payload_msb_pos = 0; + cp.in_data_path = conn->codec.data_path; + cp.out_data_path = conn->codec.data_path; + cp.in_transport_unit_size = 16; + cp.out_transport_unit_size = 16; + cp.max_latency = cpu_to_le16(0xffff); + cp.retrans_effort = 0xff; + break; + } + + if (hci_send_cmd(hdev, HCI_OP_ENHANCED_ACCEPT_SYNC_CONN, + sizeof(cp), &cp) < 0) + return -EIO; + + return 0; +} + +static void hci_enhanced_accept_sync_destroy(struct hci_dev *hdev, void *data, + int err) +{ + struct hci_conn *conn = data; + + hci_conn_put(conn); +} + +bool hci_accept_sync(struct hci_conn *conn) +{ + int result; + + if (!enhanced_sync_conn_capable(conn->hdev)) + return false; + + hci_conn_get(conn); + result = hci_cmd_sync_queue(conn->hdev, hci_enhanced_accept_sync, + conn, hci_enhanced_accept_sync_destroy); + if (result < 0) { + hci_conn_put(conn); + return false; + } + + return true; +} + struct le_conn_update_data { struct hci_conn *conn; u16 min; @@ -2166,28 +2345,70 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, bool hci_iso_setup_path(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_setup_iso_path cmd; + struct { + struct hci_cp_le_setup_iso_path cp; + __u8 cfg[10]; + } __packed cmd; + __u8 path = conn->codec.data_path; + __u8 codec = path ? conn->codec.id : 0x03; /* Transparent Data */ + __le16 cid = path ? cpu_to_le16(conn->codec.cid) : 0; + __le16 vid = path ? cpu_to_le16(conn->codec.vid) : 0; + size_t cmd_len = sizeof(cmd.cp); + + if (path) + hci_configure_datapath(hdev, conn->type, &conn->codec); memset(&cmd, 0, sizeof(cmd)); - if (conn->iso_qos.ucast.out.sdu) { - cmd.handle = cpu_to_le16(conn->handle); - cmd.direction = 0x00; /* Input (Host to Controller) */ - cmd.path = 0x00; /* HCI path if enabled */ - cmd.codec = 0x03; /* Transparent Data */ + if (path && codec == BT_CODEC_LC3) { + __u16 sdu = conn->iso_qos.ucast.out.sdu ? + conn->iso_qos.ucast.out.sdu : + conn->iso_qos.ucast.in.sdu; + __u32 interval = conn->iso_qos.ucast.out.interval ? + conn->iso_qos.ucast.out.interval : + conn->iso_qos.ucast.in.interval; + + if (!sdu) + sdu = 40; + cmd.cp.codec_cfg_len = sizeof(cmd.cfg); + /* Sampling_Frequency (0x01): 0x03 = 16 kHz, 0x08 = 48 kHz */ + cmd.cfg[0] = 0x02; + cmd.cfg[1] = 0x01; + cmd.cfg[2] = (sdu >= 80) ? 0x08 : 0x03; + /* Frame_Duration (0x02): 0x00 = 7.5 ms, 0x01 = 10 ms */ + cmd.cfg[3] = 0x02; + cmd.cfg[4] = 0x02; + cmd.cfg[5] = (interval == 7500) ? 0x00 : 0x01; + /* Octets_Per_Codec_Frame (0x04) */ + cmd.cfg[6] = 0x03; + cmd.cfg[7] = 0x04; + cmd.cfg[8] = sdu & 0xff; + cmd.cfg[9] = (sdu >> 8) & 0xff; + cmd_len += sizeof(cmd.cfg); + } - if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, sizeof(cmd), + if (conn->iso_qos.ucast.out.sdu) { + cmd.cp.handle = cpu_to_le16(conn->handle); + cmd.cp.direction = 0x00; /* Input (Host to Controller) */ + cmd.cp.path = path; + cmd.cp.codec = codec; + cmd.cp.codec_cid = cid; + cmd.cp.codec_vid = vid; + + if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, cmd_len, &cmd) < 0) return false; } if (conn->iso_qos.ucast.in.sdu) { - cmd.handle = cpu_to_le16(conn->handle); - cmd.direction = 0x01; /* Output (Controller to Host) */ - cmd.path = 0x00; /* HCI path if enabled */ - cmd.codec = 0x03; /* Transparent Data */ - - if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, sizeof(cmd), + cmd.cp.handle = cpu_to_le16(conn->handle); + cmd.cp.direction = 0x01; /* Output (Controller to Host) */ + cmd.cp.path = path; + cmd.cp.codec = codec; + cmd.cp.codec_cid = cid; + cmd.cp.codec_vid = vid; + + if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, cmd_len, &cmd) < 0) return false; } diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index c1fdd9846eeb01..23029004541733 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -79,6 +79,7 @@ struct iso_pinfo { bool qos_user_set; __u8 base_len; __u8 base[BASE_MAX_LENGTH]; + struct bt_codec codec; struct iso_conn *conn; struct delayed_work timeout_work; }; @@ -468,6 +469,8 @@ static int iso_connect_bis(struct sock *sk) lockdep_assert_held(&hcon->hdev->lock); + hcon->codec = iso_pi(sk)->codec; + conn = iso_conn_add(hcon); if (!conn) { hci_conn_drop(hcon); @@ -581,6 +584,8 @@ static int iso_connect_cis(struct sock *sk) goto unlock; } + hcon->codec = iso_pi(sk)->codec; + conn = iso_conn_add(hcon); if (!conn) { hci_conn_drop(hcon); @@ -990,6 +995,7 @@ static void iso_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; bt_sk(sk)->flags = bt_sk(parent)->flags; + iso_pi(sk)->codec = iso_pi(parent)->codec; security_sk_clone(parent, sk); } } @@ -1046,6 +1052,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock, iso_pi(sk)->qos = default_qos; iso_pi(sk)->sync_handle = -1; + iso_pi(sk)->codec.id = BT_CODEC_TRANSPARENT; + iso_pi(sk)->codec.data_path = 0x00; INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout); @@ -1392,6 +1400,8 @@ static int iso_listen_bis(struct sock *sk) lockdep_assert_held(&hcon->hdev->lock); + hcon->codec = iso_pi(sk)->codec; + conn = iso_conn_add(hcon); if (!conn) { hci_conn_drop(hcon); @@ -1960,6 +1970,9 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname, struct sock *sk = sock->sk; int err = 0; struct bt_iso_qos qos = default_qos; + struct bt_codecs *codecs; + struct hci_dev *hdev; + __u8 buffer[255]; u32 opt; BT_DBG("sk %p", sk); @@ -2035,6 +2048,56 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname, break; + case BT_CODEC: + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND && + sk->sk_state != BT_LISTEN && sk->sk_state != BT_CONNECT2) { + err = -EINVAL; + break; + } + + hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src, + iso_pi(sk)->src_type); + if (!hdev) { + err = -EBADFD; + break; + } + + if (!hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED) || + !hdev->get_data_path_id) { + hci_dev_put(hdev); + err = -EOPNOTSUPP; + break; + } + + if (optlen < sizeof(struct bt_codecs) || + optlen > sizeof(buffer)) { + hci_dev_put(hdev); + err = -EINVAL; + break; + } + + err = copy_struct_from_sockptr(buffer, sizeof(buffer), optval, + optlen); + if (err) { + hci_dev_put(hdev); + break; + } + + codecs = (void *)buffer; + + if (codecs->num_codecs != 1 || + optlen < struct_size(codecs, codecs, codecs->num_codecs)) { + hci_dev_put(hdev); + err = -EINVAL; + break; + } + + iso_pi(sk)->codec = codecs->codecs[0]; + if (iso_pi(sk)->conn && iso_pi(sk)->conn->hcon) + iso_pi(sk)->conn->hcon->codec = iso_pi(sk)->codec; + hci_dev_put(hdev); + break; + default: err = -ENOPROTOOPT; break; @@ -2050,6 +2113,12 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, struct sock *sk = sock->sk; struct bt_iso_qos *qos; int len, val, err = 0; + int buf_len; + struct codec_list *c; + u8 num_codecs, i; + struct hci_dev *hdev; + struct hci_codec_caps *caps; + struct bt_codec codec; u8 base_len; u8 *base; @@ -2106,6 +2175,94 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, break; + case BT_CODEC: + num_codecs = 0; + buf_len = 0; + + hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src, + iso_pi(sk)->src_type); + if (!hdev) { + err = -EBADFD; + break; + } + + if (!hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED) || + !hdev->get_data_path_id) { + hci_dev_put(hdev); + err = -EOPNOTSUPP; + break; + } + + release_sock(sk); + + hci_dev_lock(hdev); + list_for_each_entry(c, &hdev->local_codecs, list) { + if (c->transport != HCI_TRANSPORT_LE_CIS && + c->transport != HCI_TRANSPORT_LE_BIS) + continue; + num_codecs++; + for (i = 0, caps = c->caps; i < c->num_caps; i++) { + buf_len += 1 + caps->len; + caps = (void *)&caps->data[caps->len]; + } + buf_len += sizeof(struct bt_codec); + } + hci_dev_unlock(hdev); + + buf_len += sizeof(struct bt_codecs); + if (buf_len > len) { + hci_dev_put(hdev); + return -ENOBUFS; + } + + if (copy_to_iter(&num_codecs, sizeof(num_codecs), + &opt->iter_out) != sizeof(num_codecs)) { + hci_dev_put(hdev); + return -EFAULT; + } + + hci_dev_lock(hdev); + list_for_each_entry(c, &hdev->local_codecs, list) { + if (c->transport != HCI_TRANSPORT_LE_CIS && + c->transport != HCI_TRANSPORT_LE_BIS) + continue; + + codec.id = c->id; + codec.cid = c->cid; + codec.vid = c->vid; + err = hdev->get_data_path_id(hdev, &codec.data_path); + if (err < 0) + break; + codec.num_caps = c->num_caps; + if (copy_to_iter(&codec, sizeof(codec), &opt->iter_out) + != sizeof(codec)) { + err = -EFAULT; + break; + } + + len = 0; + for (i = 0, caps = c->caps; i < c->num_caps; i++) { + len += 1 + caps->len; + caps = (void *)&caps->data[caps->len]; + } + + if (len && + copy_to_iter(c->caps, len, &opt->iter_out) != len) { + err = -EFAULT; + break; + } + } + + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + lock_sock(sk); + + if (!err) + opt->optlen = buf_len; + + break; + default: err = -ENOPROTOOPT; break; @@ -2397,6 +2554,7 @@ static void iso_conn_ready(struct iso_conn *conn) iso_pi(sk)->sync_handle = iso_pi(parent)->sync_handle; memcpy(iso_pi(sk)->base, iso_pi(parent)->base, iso_pi(parent)->base_len); iso_pi(sk)->base_len = iso_pi(parent)->base_len; + hcon->codec = iso_pi(sk)->codec; hci_conn_hold(hcon); iso_chan_add(conn, sk, parent); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 7a2551ef7f50b8..ec773450458714 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -35,6 +35,7 @@ #include #include "smp.h" +#include "msft.h" static struct bt_sock_list l2cap_sk_list = { .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) @@ -591,6 +592,12 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, struct bt_security sec; struct bt_power pwr; int len, mode, err = 0; + int buf_len; + struct codec_list *c; + u8 num_codecs, i; + struct hci_dev *hdev; + struct hci_codec_caps *caps; + struct bt_codec codec; u32 opt; u16 mtu; u8 mval; @@ -745,6 +752,93 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, err = -EFAULT; break; + case BT_CODEC: + num_codecs = 0; + buf_len = 0; + + hdev = hci_get_route(&chan->dst, &chan->src, chan->src_type); + if (!hdev) { + err = -EBADFD; + break; + } + + if (!hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED) || + !hdev->get_data_path_id) { + hci_dev_put(hdev); + err = -EOPNOTSUPP; + break; + } + + release_sock(sk); + l2cap_chan_unlock(chan); + + hci_dev_lock(hdev); + list_for_each_entry(c, &hdev->local_codecs, list) { + if (c->transport != HCI_TRANSPORT_ACL) + continue; + num_codecs++; + for (i = 0, caps = c->caps; i < c->num_caps; i++) { + buf_len += 1 + caps->len; + caps = (void *)&caps->data[caps->len]; + } + buf_len += sizeof(struct bt_codec); + } + hci_dev_unlock(hdev); + + buf_len += sizeof(struct bt_codecs); + if (buf_len > len) { + hci_dev_put(hdev); + return -ENOBUFS; + } + + if (copy_to_iter(&num_codecs, sizeof(num_codecs), + &sopt->iter_out) != sizeof(num_codecs)) { + hci_dev_put(hdev); + return -EFAULT; + } + + hci_dev_lock(hdev); + list_for_each_entry(c, &hdev->local_codecs, list) { + if (c->transport != HCI_TRANSPORT_ACL) + continue; + + codec.id = c->id; + codec.cid = c->cid; + codec.vid = c->vid; + err = hdev->get_data_path_id(hdev, &codec.data_path); + if (err < 0) + break; + codec.num_caps = c->num_caps; + if (copy_to_iter(&codec, sizeof(codec), &sopt->iter_out) + != sizeof(codec)) { + err = -EFAULT; + break; + } + + len = 0; + for (i = 0, caps = c->caps; i < c->num_caps; i++) { + len += 1 + caps->len; + caps = (void *)&caps->data[caps->len]; + } + + if (len && + copy_to_iter(c->caps, len, &sopt->iter_out) != len) { + err = -EFAULT; + break; + } + } + + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + l2cap_chan_lock(chan); + lock_sock(sk); + + if (!err) + sopt->optlen = buf_len; + + break; + default: err = -ENOPROTOOPT; break; @@ -935,6 +1029,9 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, struct bt_security sec; struct bt_power pwr; struct l2cap_conn *conn; + struct bt_codecs *codecs; + struct hci_dev *hdev; + __u8 buffer[255]; int err = 0; u32 opt, phys; u16 mtu; @@ -1160,6 +1257,63 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; + case BT_CODEC: + hdev = hci_get_route(&chan->dst, &chan->src, chan->src_type); + if (!hdev) { + err = -EBADFD; + break; + } + + if (!hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED) || + !hdev->get_data_path_id) { + hci_dev_put(hdev); + err = -EOPNOTSUPP; + break; + } + + if (optlen < sizeof(struct bt_codecs) || + optlen > sizeof(buffer)) { + hci_dev_put(hdev); + err = -EINVAL; + break; + } + + err = copy_struct_from_sockptr(buffer, sizeof(buffer), optval, + optlen); + if (err) { + hci_dev_put(hdev); + break; + } + + codecs = (void *)buffer; + + if (codecs->num_codecs != 1 || + optlen < struct_size(codecs, codecs, codecs->num_codecs)) { + hci_dev_put(hdev); + err = -EINVAL; + break; + } + + /* Check if the controller supports MSFT AVDTP hardware offload */ + if (msft_avdtp_supported(hdev)) { + size_t caps_len = optlen - + struct_size(codecs, codecs, 1); + + release_sock(sk); + l2cap_chan_unlock(chan); + + err = msft_avdtp_open(hdev, chan, &codecs->codecs[0], + caps_len); + + l2cap_chan_lock(chan); + lock_sock(sk); + } else { + err = hci_configure_datapath(hdev, ACL_LINK, + &codecs->codecs[0]); + } + hci_dev_put(hdev); + break; + default: err = -ENOPROTOOPT; break; @@ -1530,6 +1684,18 @@ static int l2cap_sock_release(struct socket *sock) err = l2cap_sock_shutdown(sock, SHUT_RDWR); chan = l2cap_pi(sk)->chan; + /* Check if an active MSFT AVDTP offload stream needs to be closed */ + if (chan && chan->avdtp_handle) { + struct hci_dev *hdev; + + hdev = hci_get_route(&chan->dst, &chan->src, chan->src_type); + if (hdev) { + msft_avdtp_close(hdev, chan->avdtp_handle); + hci_dev_put(hdev); + } + chan->avdtp_handle = 0; + } + l2cap_chan_hold(chan); l2cap_chan_lock(chan); diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index 2559c5b1bef020..5b46315a74e1f5 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -5,6 +5,7 @@ #include #include +#include #include #include "mgmt_util.h" @@ -79,6 +80,88 @@ struct msft_rp_le_set_advertisement_filter_enable { __u8 sub_opcode; } __packed; +/* Microsoft Bluetooth HCI Vendor-Specific Extension for AVDTP hardware offload + * (feature bit 10 in HCI_VS_MSFT_Read_Supported_Features). Command and event + * structures are wire-protocol HCI packets mapped onto skb->data with __le16 + * fields at unaligned byte offsets, so __packed is required without extra + * struct alignment padding. Capability payloads use standard Bluetooth SIG + * AVDTP v1.3 (Section 8.21.5) Media Codec Service Capability elements and + * A2DP v1.3 (Sections 4.3.2 & 4.5.2) SBC / MPEG-2,4 AAC Codec Information + * Elements. + */ +#define AVDTP_SVC_CAT_MEDIA_CODEC 0x07 +#define AVDTP_CAP_HDR_LEN 2 +#define AVDTP_MEDIA_CODEC_HDR_LEN 2 +#define AVDTP_MEDIA_TYPE_AUDIO 0x00 + +#define A2DP_CODEC_SBC 0x00 +#define A2DP_CODEC_MPEG24 0x02 +#define A2DP_CODEC_VENDOR 0xff + +#define AVDTP_SBC_CIE_LEN 4 +#define AVDTP_SBC_LOSC (AVDTP_MEDIA_CODEC_HDR_LEN + \ + AVDTP_SBC_CIE_LEN) +#define AVDTP_SBC_CAP_LEN (AVDTP_CAP_HDR_LEN + \ + AVDTP_SBC_LOSC) + +#define AVDTP_AAC_CIE_LEN 6 +#define AVDTP_AAC_LOSC (AVDTP_MEDIA_CODEC_HDR_LEN + \ + AVDTP_AAC_CIE_LEN) +#define AVDTP_AAC_CAP_LEN (AVDTP_CAP_HDR_LEN + \ + AVDTP_AAC_LOSC) +#define AVDTP_AAC_VBR_BIT 0x80 +#define AVDTP_AAC_BITRATE_HI_MASK 0x7f +#define AVDTP_AAC_MIN_BITRATE 128000 +#define AVDTP_AAC_MAX_BITRATE 192000 + +/* Intel Bluetooth Assigned Company ID and vendor A2DP offload codec IDs */ +#define BT_COMPID_INTEL 0x0002 +#define BTINTEL_A2DP_VID_SBC 0x0000 +#define BTINTEL_A2DP_VID_AAC 0x0002 + +#define MSFT_OP_AVDTP_CAPABILITIES_CONF 0x07 +struct msft_cp_avdtp_capabilities_conf { + __u8 sub_opcode; + __u8 ext_codec_count; + __u8 audio_if_param_count; +} __packed; + +struct msft_rp_avdtp_capabilities_conf { + __u8 status; + __u8 sub_opcode; + __u8 int_codec_count; + __u8 data[]; +} __packed; + +#define MSFT_OP_AVDTP_OPEN 0x08 +struct msft_cp_avdtp_open { + __u8 sub_opcode; + __le16 acl_handle; + __le16 l2cap_dcid; + __le16 l2cap_mtu; + __u8 data[]; +} __packed; + +struct msft_rp_avdtp_open { + __u8 status; + __u8 sub_opcode; + __le16 avdtp_handle; + __u8 audio_if_param_count; +} __packed; + +#define MSFT_OP_AVDTP_START 0x09 +#define MSFT_OP_AVDTP_SUSPEND 0x0a +#define MSFT_OP_AVDTP_CLOSE 0x0b +struct msft_cp_avdtp_handle { + __u8 sub_opcode; + __le16 avdtp_handle; +} __packed; + +struct msft_rp_avdtp_handle { + __u8 status; + __u8 sub_opcode; +} __packed; + #define MSFT_EV_LE_MONITOR_DEVICE 0x02 struct msft_ev_le_monitor_device { __u8 addr_type; @@ -138,6 +221,411 @@ bool msft_monitor_supported(struct hci_dev *hdev) return !!(msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR); } +bool msft_avdtp_supported(struct hci_dev *hdev) +{ + return !!(msft_get_features(hdev) & MSFT_FEATURE_MASK_AVDTP); +} + +/* Validate an AVDTP Media Codec Service Capability TLV element */ +static bool msft_avdtp_valid_codec_cap(const u8 *pos, size_t rem, + size_t *elem_len, u8 *codec_id) +{ + size_t len; + u8 losc; + u8 id; + + if (rem < AVDTP_CAP_HDR_LEN + AVDTP_MEDIA_CODEC_HDR_LEN) + return false; + + if (pos[0] != AVDTP_SVC_CAT_MEDIA_CODEC) + return false; + + losc = pos[1]; + if (losc < AVDTP_MEDIA_CODEC_HDR_LEN) + return false; + + len = AVDTP_CAP_HDR_LEN + (size_t)losc; + if (len > rem) + return false; + + if (pos[2] != AVDTP_MEDIA_TYPE_AUDIO) + return false; + + id = pos[3]; + switch (id) { + case A2DP_CODEC_SBC: + if (losc != AVDTP_SBC_LOSC) + return false; + break; + case A2DP_CODEC_MPEG24: + if (losc != AVDTP_AAC_LOSC) + return false; + break; + default: + return false; + } + + *elem_len = len; + *codec_id = id; + return true; +} + +/* This function requires the caller holds hci_req_sync_lock */ +static void msft_read_avdtp_capabilities(struct hci_dev *hdev) +{ + struct msft_cp_avdtp_capabilities_conf cp = { + .sub_opcode = MSFT_OP_AVDTP_CAPABILITIES_CONF, + .ext_codec_count = 0, + .audio_if_param_count = 0, + }; + struct msft_rp_avdtp_capabilities_conf *rp; + struct sk_buff *skb; + const u8 *pos; + size_t rem; + u8 i; + + skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Failed to read MSFT AVDTP capabilities (%ld)", + PTR_ERR(skb)); + return; + } + + if (skb->len < sizeof(*rp)) { + bt_dev_err(hdev, "MSFT AVDTP capabilities length mismatch"); + goto done; + } + + rp = (void *)skb->data; + if (rp->status || rp->sub_opcode != MSFT_OP_AVDTP_CAPABILITIES_CONF) + goto done; + + pos = rp->data; + rem = skb->len - sizeof(*rp); + + /* Iterate through each internal AVDTP codec capability block returned by the controller */ + for (i = 0; + i < rp->int_codec_count && + rem >= AVDTP_CAP_HDR_LEN + AVDTP_MEDIA_CODEC_HDR_LEN; + i++) { + struct codec_list *c, *entry; + size_t elem_len; + u8 codec_id; + bool exists = false; + + /* Validate AVDTP Service Category, LOSC bounds, media type, and codec ID */ + if (!msft_avdtp_valid_codec_cap(pos, rem, &elem_len, &codec_id)) + break; + + hci_dev_lock(hdev); + list_for_each_entry(c, &hdev->local_codecs, list) { + if (c->transport == HCI_TRANSPORT_ACL && + c->id == codec_id) { + exists = true; + break; + } + } + + /* Register the AVDTP offload codec if not already present in local_codecs */ + if (!exists) { + entry = kzalloc(sizeof(*entry) + + sizeof(struct hci_codec_caps) + + elem_len, GFP_KERNEL); + if (entry) { + entry->id = codec_id; + entry->transport = HCI_TRANSPORT_ACL; + entry->num_caps = 1; + entry->len = sizeof(struct hci_codec_caps) + + elem_len; + entry->caps[0].len = elem_len; + memcpy(entry->caps[0].data, pos, elem_len); + list_add(&entry->list, &hdev->local_codecs); + bt_dev_info(hdev, + "MSFT AVDTP offload codec 0x%02x registered (%zu bytes)", + codec_id, elem_len); + } + } + hci_dev_unlock(hdev); + + pos += elem_len; + rem -= elem_len; + } + +done: + kfree_skb(skb); +} + +int msft_avdtp_close(struct hci_dev *hdev, __u16 avdtp_handle) +{ + struct msft_cp_avdtp_handle cp; + struct sk_buff *skb; + int err = 0; + + if (!avdtp_handle || !msft_avdtp_supported(hdev)) + return 0; + + cp.sub_opcode = MSFT_OP_AVDTP_SUSPEND; + cp.avdtp_handle = cpu_to_le16(avdtp_handle); + skb = hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp, + HCI_CMD_TIMEOUT); + if (!IS_ERR(skb)) + kfree_skb(skb); + + cp.sub_opcode = MSFT_OP_AVDTP_CLOSE; + cp.avdtp_handle = cpu_to_le16(avdtp_handle); + skb = hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "MSFT AVDTP close failed (%d)", err); + return err; + } + + kfree_skb(skb); + bt_dev_info(hdev, "MSFT AVDTP offload closed: handle 0x%04x", + avdtp_handle); + return 0; +} + +int msft_avdtp_open(struct hci_dev *hdev, struct l2cap_chan *chan, + struct bt_codec *codec, size_t caps_len) +{ + static const u8 default_sbc_cap[AVDTP_SBC_CAP_LEN] = { + AVDTP_SVC_CAT_MEDIA_CODEC, AVDTP_SBC_LOSC, + AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, + 0x11, 0x15, 0x02, 0x33 + }; + static const u8 default_aac_cap[AVDTP_AAC_CAP_LEN] = { + AVDTP_SVC_CAT_MEDIA_CODEC, AVDTP_AAC_LOSC, + AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_MPEG24, + 0x80, 0x00, 0x84, 0x82, 0xee, 0x00 + }; + struct msft_cp_avdtp_open *open_cp; + struct msft_rp_avdtp_open *open_rp; + struct msft_cp_avdtp_handle start_cp; + struct msft_rp_avdtp_handle *start_rp; + u8 custom_cap[AVDTP_AAC_CAP_LEN]; + const u8 *cap_ptr = NULL; + size_t cap_size = 0; + size_t open_len; + struct sk_buff *skb; + u16 acl_handle = 0; + u16 avdtp_handle; + u16 dcid, mtu; + bool is_sbc, is_aac; + bool found_conn = false; + int err; + + if (!msft_avdtp_supported(hdev)) + return -EOPNOTSUPP; + + if (chan->avdtp_handle) { + msft_avdtp_close(hdev, chan->avdtp_handle); + chan->avdtp_handle = 0; + } + + if (!codec->data_path) + return 0; + + /* Match standard A2DP codec IDs or Intel vendor-specific ACL codec IDs */ + is_sbc = (codec->id == A2DP_CODEC_SBC) || + (codec->id == A2DP_CODEC_VENDOR && + codec->cid == BT_COMPID_INTEL && + codec->vid == BTINTEL_A2DP_VID_SBC); + is_aac = (codec->id == A2DP_CODEC_MPEG24) || + (codec->id == A2DP_CODEC_VENDOR && + codec->cid == BT_COMPID_INTEL && + codec->vid == BTINTEL_A2DP_VID_AAC); + + /* Check if user-space supplied codec-specific capability parameters */ + if (codec->num_caps > 0 && caps_len >= sizeof(struct bt_codec_caps)) { + const struct bt_codec_caps *caps = (const void *)&codec[1]; + + /* Verify the capability payload fits within the setsockopt buffer */ + if (caps_len >= sizeof(*caps) + caps->len) { + size_t parsed_len; + u8 parsed_id; + + /* Use a full AVDTP Media Codec Service Capability TLV directly if valid */ + if (msft_avdtp_valid_codec_cap(caps->data, caps->len, + &parsed_len, + &parsed_id) && + parsed_len == caps->len) { + cap_ptr = caps->data; + cap_size = caps->len; + /* Wrap a raw 4-byte SBC CIE into an 8-byte AVDTP Service Capability TLV */ + } else if (is_sbc && caps->len == AVDTP_SBC_CIE_LEN) { + custom_cap[0] = AVDTP_SVC_CAT_MEDIA_CODEC; + custom_cap[1] = AVDTP_SBC_LOSC; + custom_cap[2] = AVDTP_MEDIA_TYPE_AUDIO; + custom_cap[3] = A2DP_CODEC_SBC; + memcpy(&custom_cap[4], caps->data, + AVDTP_SBC_CIE_LEN); + cap_ptr = custom_cap; + cap_size = AVDTP_SBC_CAP_LEN; + /* Wrap a raw 6-byte AAC CIE into a 10-byte AVDTP Service Capability TLV */ + } else if (is_aac && caps->len == AVDTP_AAC_CIE_LEN) { + u32 br; + + custom_cap[0] = AVDTP_SVC_CAT_MEDIA_CODEC; + custom_cap[1] = AVDTP_AAC_LOSC; + custom_cap[2] = AVDTP_MEDIA_TYPE_AUDIO; + custom_cap[3] = A2DP_CODEC_MPEG24; + memcpy(&custom_cap[4], caps->data, + AVDTP_AAC_CIE_LEN); + br = ((u32)(custom_cap[7] & + AVDTP_AAC_BITRATE_HI_MASK) << 16) | + ((u32)custom_cap[8] << 8) | + custom_cap[9]; + /* Clamp AAC bitrate to supported 128..192 kbps range */ + if (br > AVDTP_AAC_MAX_BITRATE || + br < AVDTP_AAC_MIN_BITRATE) { + custom_cap[7] = (custom_cap[7] & + AVDTP_AAC_VBR_BIT) | + ((AVDTP_AAC_MAX_BITRATE >> 16) & + AVDTP_AAC_BITRATE_HI_MASK); + custom_cap[8] = (AVDTP_AAC_MAX_BITRATE >> 8) & 0xff; + custom_cap[9] = AVDTP_AAC_MAX_BITRATE & 0xff; + } + cap_ptr = custom_cap; + cap_size = AVDTP_AAC_CAP_LEN; + } + } + } + + /* Fall back to default SBC or AAC capability if no valid capability was provided */ + if (!cap_ptr) { + if (is_sbc) { + cap_ptr = default_sbc_cap; + cap_size = sizeof(default_sbc_cap); + } else if (is_aac) { + cap_ptr = default_aac_cap; + cap_size = sizeof(default_aac_cap); + } else { + bt_dev_err(hdev, + "Unsupported MSFT AVDTP codec id 0x%02x (cid 0x%04x vid 0x%04x)", + codec->id, codec->cid, codec->vid); + return -EINVAL; + } + } + + /* Use the ACL connection handle directly if the L2CAP channel is already bound */ + if (chan->conn && chan->conn->hcon) { + acl_handle = chan->conn->hcon->handle; + found_conn = true; + } else { + /* Look up an active BR/EDR ACL connection by peer address or connection list */ + struct hci_conn *hcon; + + hci_dev_lock(hdev); + if (bacmp(&chan->dst, BDADDR_ANY)) + hcon = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &chan->dst); + else + hcon = NULL; + + if (!hcon) { + struct hci_conn *c; + + list_for_each_entry_rcu(c, &hdev->conn_hash.list, list) { + if (c->type == ACL_LINK && + !HCI_CONN_HANDLE_UNSET(c->handle)) { + hcon = c; + break; + } + } + } + + if (hcon && !HCI_CONN_HANDLE_UNSET(hcon->handle)) { + acl_handle = hcon->handle; + found_conn = true; + } + hci_dev_unlock(hdev); + } + + if (!found_conn) { + bt_dev_err(hdev, + "MSFT AVDTP offload requires an active BR/EDR ACL connection"); + return -ENOTCONN; + } + + dcid = chan->dcid ? chan->dcid : L2CAP_CID_DYN_START; + mtu = chan->omtu ? chan->omtu : L2CAP_DEFAULT_MTU; + + open_len = sizeof(*open_cp) + cap_size + 1; + open_cp = kzalloc(open_len, GFP_KERNEL); + if (!open_cp) + return -ENOMEM; + + open_cp->sub_opcode = MSFT_OP_AVDTP_OPEN; + open_cp->acl_handle = cpu_to_le16(acl_handle); + open_cp->l2cap_dcid = cpu_to_le16(dcid); + open_cp->l2cap_mtu = cpu_to_le16(mtu); + memcpy(open_cp->data, cap_ptr, cap_size); + open_cp->data[cap_size] = 0x00; /* Audio_interface_parameter_count */ + + /* Send MSFT AVDTP Open command to allocate an offloaded stream handle on the controller */ + skb = hci_cmd_sync(hdev, hdev->msft_opcode, open_len, open_cp, + HCI_CMD_TIMEOUT); + kfree(open_cp); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "MSFT AVDTP Open failed (%d)", err); + return err; + } + + if (skb->len < sizeof(*open_rp)) { + kfree_skb(skb); + return -EIO; + } + + open_rp = (void *)skb->data; + if (open_rp->status || open_rp->sub_opcode != MSFT_OP_AVDTP_OPEN) { + err = -bt_to_errno(open_rp->status); + kfree_skb(skb); + return err ? err : -EIO; + } + + avdtp_handle = __le16_to_cpu(open_rp->avdtp_handle); + kfree_skb(skb); + + start_cp.sub_opcode = MSFT_OP_AVDTP_START; + start_cp.avdtp_handle = cpu_to_le16(avdtp_handle); + + /* Send MSFT AVDTP Start command to start SSP TDM audio and hardware encoding */ + skb = hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(start_cp), &start_cp, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "MSFT AVDTP Start failed (%d)", err); + msft_avdtp_close(hdev, avdtp_handle); + return err; + } + + if (skb->len < sizeof(*start_rp)) { + kfree_skb(skb); + msft_avdtp_close(hdev, avdtp_handle); + return -EIO; + } + + start_rp = (void *)skb->data; + if (start_rp->status || start_rp->sub_opcode != MSFT_OP_AVDTP_START) { + err = -bt_to_errno(start_rp->status); + kfree_skb(skb); + msft_avdtp_close(hdev, avdtp_handle); + return err ? err : -EIO; + } + + kfree_skb(skb); + chan->avdtp_handle = avdtp_handle; + + bt_dev_info(hdev, + "MSFT AVDTP offload started: acl 0x%04x dcid 0x%04x mtu %u codec 0x%02x handle 0x%04x", + acl_handle, dcid, mtu, codec->id, avdtp_handle); + return 0; +} + static bool read_supported_features(struct hci_dev *hdev, struct msft_data *msft) { @@ -669,6 +1157,9 @@ void msft_do_open(struct hci_dev *hdev) */ reregister_monitor(hdev); } + + if (msft_avdtp_supported(hdev)) + msft_read_avdtp_capabilities(hdev); } void msft_do_close(struct hci_dev *hdev) diff --git a/net/bluetooth/msft.h b/net/bluetooth/msft.h index fe538e9c91c019..f44cc850679e15 100644 --- a/net/bluetooth/msft.h +++ b/net/bluetooth/msft.h @@ -9,10 +9,17 @@ #define MSFT_FEATURE_MASK_LE_ADV_MONITOR BIT(3) #define MSFT_FEATURE_MASK_CURVE_VALIDITY BIT(4) #define MSFT_FEATURE_MASK_CONCURRENT_ADV_MONITOR BIT(5) +#define MSFT_FEATURE_MASK_AVDTP BIT(10) + +struct l2cap_chan; #if IS_ENABLED(CONFIG_BT_MSFTEXT) bool msft_monitor_supported(struct hci_dev *hdev); +bool msft_avdtp_supported(struct hci_dev *hdev); +int msft_avdtp_open(struct hci_dev *hdev, struct l2cap_chan *chan, + struct bt_codec *codec, size_t caps_len); +int msft_avdtp_close(struct hci_dev *hdev, __u16 avdtp_handle); void msft_register(struct hci_dev *hdev); void msft_release(struct hci_dev *hdev); void msft_do_open(struct hci_dev *hdev); @@ -34,6 +41,23 @@ static inline bool msft_monitor_supported(struct hci_dev *hdev) return false; } +static inline bool msft_avdtp_supported(struct hci_dev *hdev) +{ + return false; +} + +static inline int msft_avdtp_open(struct hci_dev *hdev, + struct l2cap_chan *chan, + struct bt_codec *codec, size_t caps_len) +{ + return -EOPNOTSUPP; +} + +static inline int msft_avdtp_close(struct hci_dev *hdev, __u16 avdtp_handle) +{ + return -EOPNOTSUPP; +} + static inline void msft_register(struct hci_dev *hdev) {} static inline void msft_release(struct hci_dev *hdev) {} static inline void msft_do_open(struct hci_dev *hdev) {} diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 8be48ca9f4c5b6..0d742f97c06bc7 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -645,6 +645,8 @@ static void sco_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; bt_sk(sk)->flags = bt_sk(parent)->flags; + sco_pi(sk)->setting = sco_pi(parent)->setting; + sco_pi(sk)->codec = sco_pi(parent)->codec; security_sk_clone(parent, sk); } } @@ -950,6 +952,8 @@ static void sco_conn_defer_accept(struct hci_conn *conn, u16 setting) cp.role = 0x00; /* Ignored */ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); + } else if (enhanced_sync_conn_capable(hdev) && conn->codec.data_path) { + hci_accept_sync(conn); } else { struct hci_cp_accept_sync_conn_req cp; @@ -998,6 +1002,7 @@ static int sco_sock_recvmsg(struct socket *sock, struct msghdr *msg, if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { + pi->conn->hcon->codec = pi->codec; sco_conn_defer_accept(pi->conn->hcon, pi->setting); sk->sk_state = BT_CONFIG; @@ -1082,7 +1087,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, case BT_CODEC: if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND && - sk->sk_state != BT_CONNECT2) { + sk->sk_state != BT_LISTEN && sk->sk_state != BT_CONNECT2) { err = -EINVAL; break; }