2021-05-19 17:52:26

by Kiran K

[permalink] [raw]
Subject: [PATCH v8 1/9] Bluetooth: enumerate local supported codec and cache details

Move reading of supported local codecs into a separate init function,
query codecs capabilities and cache the data

Signed-off-by: Kiran K <[email protected]>
Signed-off-by: Chethan T N <[email protected]>
Signed-off-by: Srivatsa Ravishankar <[email protected]>
Reported-by: kernel test robot <[email protected]>
---
* changes in v8:
- add comments
- split __u8 codec_id[5] into {__u8 id; __le16 cid, vid }
- address review comment related codec caps structure

* changes in v7:
- keep codec enumeration call in hci_init instead of having a separate
function
- Remove unused bitmasks defined for LE transports

* changes in v6:
- fix compiler warning reported for ARCH=arc

* changes in v5:
- fix review comments
- move code used to read standard/vendor codecs caps into single function

* changes in v4:
- convert reading of codecs and codecs caps calls from async to sync

* changes in v3
move codec enumeration into a new init function

* changes in v2
add skb length check before accessing data

include/net/bluetooth/hci.h | 41 +++++++
include/net/bluetooth/hci_core.h | 17 +++
net/bluetooth/hci_core.c | 199 ++++++++++++++++++++++++++++++-
3 files changed, 253 insertions(+), 4 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index c4b0650fb9ae..6cb9340a2d51 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -1307,6 +1307,28 @@ struct hci_rp_read_data_block_size {
} __packed;

#define HCI_OP_READ_LOCAL_CODECS 0x100b
+struct hci_std_codecs {
+ __u8 num;
+ __u8 codec[];
+} __packed;
+
+struct hci_ven_codec {
+ /* company id */
+ __le16 cid;
+ /* vendor codec id */
+ __le16 vid;
+} __packed;
+
+struct hci_ven_codecs {
+ __u8 num;
+ struct hci_ven_codec codec[];
+} __packed;
+
+struct hci_rp_read_local_supported_codecs {
+ __u8 status;
+ struct hci_std_codecs std_codecs;
+ struct hci_ven_codecs ven_codecs;
+} __packed;

#define HCI_OP_READ_LOCAL_PAIRING_OPTS 0x100c
struct hci_rp_read_local_pairing_opts {
@@ -1315,6 +1337,25 @@ struct hci_rp_read_local_pairing_opts {
__u8 max_key_size;
} __packed;

+#define HCI_OP_READ_LOCAL_CODEC_CAPS 0x100e
+struct hci_op_read_local_codec_caps {
+ __u8 id;
+ __le16 cid;
+ __le16 vid;
+ __u8 transport;
+ __u8 direction;
+} __packed;
+
+struct hci_codec_caps {
+ __u8 len;
+ __u8 data[];
+} __packed;
+
+struct hci_rp_read_local_codec_caps {
+ __u8 status;
+ __u8 num_caps;
+} __packed;
+
#define HCI_OP_READ_PAGE_SCAN_ACTIVITY 0x0c1b
struct hci_rp_read_page_scan_activity {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 43b08bebae74..cdc9580ff264 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -131,6 +131,17 @@ struct bdaddr_list {
u8 bdaddr_type;
};

+struct codec_list {
+ struct list_head list;
+ u8 id;
+ __le16 cid;
+ __le16 vid;
+ u8 transport;
+ u8 num_caps;
+ u32 len;
+ struct hci_codec_caps caps[];
+};
+
struct bdaddr_list_with_irk {
struct list_head list;
bdaddr_t bdaddr;
@@ -535,6 +546,7 @@ struct hci_dev {
struct list_head pend_le_conns;
struct list_head pend_le_reports;
struct list_head blocked_keys;
+ struct list_head local_codecs;

struct hci_dev_stats stat;

@@ -1849,4 +1861,9 @@ void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
#define SCO_AIRMODE_CVSD 0x0000
#define SCO_AIRMODE_TRANSP 0x0003

+#define LOCAL_CODEC_ACL_MASK BIT(0)
+#define LOCAL_CODEC_SCO_MASK BIT(1)
+
+#define TRANSPORT_TYPE_MAX 0x04
+
#endif /* __HCI_CORE_H */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 6eedf334f943..b74de5996a27 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -838,10 +838,6 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
if (hdev->commands[22] & 0x04)
hci_set_event_mask_page_2(req);

- /* Read local codec list if the HCI command is supported */
- if (hdev->commands[29] & 0x20)
- hci_req_add(req, HCI_OP_READ_LOCAL_CODECS, 0, NULL);
-
/* Read local pairing options if the HCI command is supported */
if (hdev->commands[41] & 0x08)
hci_req_add(req, HCI_OP_READ_LOCAL_PAIRING_OPTS, 0, NULL);
@@ -907,6 +903,195 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
return 0;
}

+static int hci_codec_list_add(struct list_head *list,
+ struct hci_op_read_local_codec_caps *sent,
+ struct hci_rp_read_local_codec_caps *rp,
+ void *caps,
+ __u32 len)
+{
+ struct codec_list *entry;
+
+ entry = kzalloc(sizeof(*entry) + len, GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->id = sent->id;
+ if (sent->id == 0xFF) {
+ entry->cid = __le16_to_cpu(sent->cid);
+ entry->vid = __le16_to_cpu(sent->vid);
+ }
+ entry->transport = sent->transport;
+ entry->len = len;
+ entry->num_caps = rp->num_caps;
+ if (rp->num_caps)
+ memcpy(entry->caps, caps, len);
+ list_add(&entry->list, list);
+
+ return 0;
+}
+
+static void hci_codec_list_clear(struct list_head *codec_list)
+{
+ struct codec_list *c, *n;
+
+ list_for_each_entry_safe(c, n, codec_list, list) {
+ list_del(&c->list);
+ kfree(c);
+ }
+}
+
+static void hci_read_codec_capabilities(struct hci_dev *hdev, void *codec_id,
+ __u8 transport, bool is_vendor_codec)
+{
+ struct hci_op_read_local_codec_caps cmd;
+ __u8 i;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ if (is_vendor_codec) {
+ struct hci_ven_codec *ven_codec;
+
+ ven_codec = codec_id;
+ cmd.id = 0xFF;
+ cmd.cid = ven_codec->cid;
+ cmd.vid = ven_codec->vid;
+ } else {
+ cmd.id = *(__u8 *)codec_id;
+ }
+
+ cmd.direction = 0x00;
+
+ for (i = 0; i < TRANSPORT_TYPE_MAX; i++) {
+ if (transport & BIT(i)) {
+ struct hci_rp_read_local_codec_caps *rp;
+ struct hci_codec_caps *caps;
+ struct sk_buff *skb;
+ __u8 j;
+ __u32 len;
+
+ cmd.transport = i;
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_CODEC_CAPS,
+ sizeof(cmd), &cmd,
+ HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Failed to read codec capabilities (%ld)",
+ PTR_ERR(skb));
+ continue;
+ }
+
+ if (skb->len < sizeof(*rp))
+ goto error;
+
+ rp = (void *)skb->data;
+
+ if (rp->status)
+ goto error;
+
+ if (!rp->num_caps) {
+ len = 0;
+ /* this codec doesn't have capabilities */
+ goto skip_caps_parse;
+ }
+
+ skb_pull(skb, sizeof(*rp));
+
+ for (j = 0, len = 0; j < rp->num_caps; j++) {
+ caps = (void *)skb->data;
+ if (skb->len < sizeof(*caps))
+ goto error;
+ if (skb->len < caps->len)
+ goto error;
+ len += sizeof(caps->len) + caps->len;
+ skb_pull(skb, sizeof(caps->len) + caps->len);
+ }
+
+skip_caps_parse:
+ hci_dev_lock(hdev);
+ hci_codec_list_add(&hdev->local_codecs, &cmd, rp,
+ (__u8 *)rp + sizeof(*rp), len);
+ hci_dev_unlock(hdev);
+error:
+ kfree_skb(skb);
+ }
+ }
+}
+
+static void hci_codec_list_parse(struct hci_dev *hdev, __u8 num_codecs,
+ void *codec_list, bool is_vendor_codec)
+{
+ __u8 i;
+
+ for (i = 0; i < num_codecs; i++) {
+ if (!is_vendor_codec) {
+ struct hci_std_codecs *codecs = codec_list;
+
+ hci_read_codec_capabilities(hdev, &codecs->codec[i],
+ LOCAL_CODEC_ACL_MASK,
+ is_vendor_codec);
+ } else {
+ struct hci_ven_codecs *codecs = codec_list;
+
+ hci_read_codec_capabilities(hdev, &codecs->codec[i],
+ LOCAL_CODEC_ACL_MASK,
+ is_vendor_codec);
+ }
+ }
+}
+
+static void hci_read_supported_codecs(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ struct hci_rp_read_local_supported_codecs *rp;
+ struct hci_std_codecs *std_codecs;
+ struct hci_ven_codecs *ven_codecs;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_CODECS, 0, NULL,
+ HCI_CMD_TIMEOUT);
+
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Failed to read local supported codecs (%ld)",
+ PTR_ERR(skb));
+ return;
+ }
+
+ if (skb->len < sizeof(*rp))
+ goto error;
+
+ rp = (void *)skb->data;
+
+ if (rp->status)
+ goto error;
+
+ skb_pull(skb, sizeof(rp->status));
+
+ std_codecs = (void *)skb->data;
+
+ /* validate codecs length before accessing */
+ if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num)
+ + sizeof(std_codecs->num))
+ goto error;
+
+ /* enumerate codec capabilities of standard codecs */
+ hci_codec_list_parse(hdev, std_codecs->num, std_codecs, false);
+
+ skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num)
+ + sizeof(std_codecs->num));
+
+ ven_codecs = (void *)skb->data;
+
+ /* validate vendor codecs length before accessing */
+ if (skb->len <
+ flex_array_size(ven_codecs, codec, ven_codecs->num)
+ + sizeof(ven_codecs->num))
+ goto error;
+
+ /* enumerate vendor codec capabilities */
+ hci_codec_list_parse(hdev, ven_codecs->num, ven_codecs, true);
+
+error:
+ kfree_skb(skb);
+}
+
static int __hci_init(struct hci_dev *hdev)
{
int err;
@@ -937,6 +1122,10 @@ static int __hci_init(struct hci_dev *hdev)
if (err < 0)
return err;

+ /* Read local codec list if the HCI command is supported */
+ if (hdev->commands[29] & 0x20)
+ hci_read_supported_codecs(hdev);
+
/* This function is only called when the controller is actually in
* configured state. When the controller is marked as unconfigured,
* this initialization procedure is not run.
@@ -1836,6 +2025,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
bacpy(&hdev->random_addr, BDADDR_ANY);
+ hci_codec_list_clear(&hdev->local_codecs);

hci_req_sync_unlock(hdev);

@@ -3837,6 +4027,7 @@ struct hci_dev *hci_alloc_dev(void)
INIT_LIST_HEAD(&hdev->conn_hash.list);
INIT_LIST_HEAD(&hdev->adv_instances);
INIT_LIST_HEAD(&hdev->blocked_keys);
+ INIT_LIST_HEAD(&hdev->local_codecs);

INIT_WORK(&hdev->rx_work, hci_rx_work);
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
--
2.17.1



2021-05-19 17:52:45

by Kiran K

[permalink] [raw]
Subject: [PATCH v8 9/9] Bluetooth: Add support for msbc coding format

In Enhanced_Setup_Synchronous_Command, add support for msbc
coding format

Signed-off-by: Kiran K <[email protected]>
Reviewed-by: Chethan T N <[email protected]>
Reviewed-by: Srivatsa Ravishankar <[email protected]>
---
include/net/bluetooth/bluetooth.h | 1 +
net/bluetooth/hci_conn.c | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+)

diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 4e58d275c72c..817245d1046f 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -175,6 +175,7 @@ struct bt_codecs {

#define CODING_FORMAT_CVSD 0x02
#define CODING_FORMAT_TRANS 0x03
+#define CODING_FORMAT_MSBC 0x05

__printf(1, 2)
void bt_info(const char *fmt, ...);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index e2435b5abeca..edacf791d663 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -383,6 +383,30 @@ bool hci_enhanced_setup_sync(struct hci_conn *conn, __u16 handle)
cp.out_trasnport_unit_size = 1;
break;

+ case CODING_FORMAT_MSBC:
+ if (conn->attempt > ARRAY_SIZE(esco_param_msbc))
+ return false;
+
+ param = &esco_param_msbc[conn->attempt - 1];
+ 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_trasnport_unit_size = 1;
+ cp.out_trasnport_unit_size = 1;
+ break;
default:
return false;
}
--
2.17.1


2021-05-19 17:52:46

by Kiran K

[permalink] [raw]
Subject: [PATCH v8 5/9] Bluetooth: Add BT_CODEC option for getsockopt for SCO socket

Add BT_CODEC option for getsockopt systemcall over SCO socket
to expose the codecs supported by controller

Signed-off-by: Kiran K <[email protected]>
Reviewed-by: Chethan T N <[email protected]>
Reviewed-by: Srivatsa Ravishankar <[email protected]>
---
include/net/bluetooth/bluetooth.h | 20 ++++++
include/net/bluetooth/hci.h | 4 ++
net/bluetooth/sco.c | 109 ++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+)

diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 9125effbf448..1840756958ce 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -153,6 +153,26 @@ struct bt_voice {

#define BT_SCM_PKT_STATUS 0x03

+#define BT_CODEC 19
+
+struct bt_codec_caps {
+ __u8 len;
+ __u8 data[];
+} __packed;
+
+struct bt_codec {
+ __u8 id;
+ __le16 cid;
+ __le16 vid;
+ __u8 data_path;
+ __u8 num_caps;
+} __packed;
+
+struct bt_codecs {
+ __u8 num_codecs;
+ struct bt_codec codecs[];
+} __packed;
+
__printf(1, 2)
void bt_info(const char *fmt, ...);
__printf(1, 2)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 731d48ca873a..9658600ae5de 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -2626,6 +2626,10 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
#define hci_iso_data_len(h) ((h) & 0x3fff)
#define hci_iso_data_flags(h) ((h) >> 14)

+/* codec transport types */
+#define TRANSPORT_BREDR 0x00
+#define TRANSPORT_SCO_ESCO 0x01
+
/* le24 support */
static inline void hci_cpu_to_le24(__u32 val, __u8 dst[3])
{
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 3bd41563f118..d66293d1cba4 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -948,6 +948,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
struct bt_voice voice;
u32 phys;
int pkt_status;
+ struct codec_list *c;
+ u8 num_codecs, i, __user *ptr;
+ struct hci_dev *hdev;
+ struct hci_codec_caps *caps;
+ __u8 data_path;

BT_DBG("sk %p", sk);

@@ -1012,6 +1017,110 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
err = -EFAULT;
break;

+ case BT_CODEC:
+ num_codecs = 0;
+ len = 0;
+
+ hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
+
+ if (!hdev) {
+ err = -EBADFD;
+ break;
+ }
+
+ if (!test_bit(HCI_QUIRK_HFP_OFFLOAD_CODECS_SUPPORTED, &hdev->quirks)) {
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ if (!hdev->get_data_path_id) {
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ hci_dev_lock(hdev);
+ list_for_each_entry(c, &hdev->local_codecs, list) {
+ if (c->transport != TRANSPORT_SCO_ESCO)
+ continue;
+ num_codecs++;
+ for (i = 0, caps = c->caps; i < c->num_caps; i++) {
+ len += 1 + caps->len;
+ caps = (void *)&caps->data[caps->len];
+ }
+ len += sizeof(struct bt_codec);
+ }
+ hci_dev_unlock(hdev);
+
+ if (len > 255) {
+ err = -ENOMEM;
+ break;
+ }
+
+ ptr = optval;
+ if (put_user(num_codecs, ptr)) {
+ err = -EFAULT;
+ break;
+ }
+ ptr += sizeof(num_codecs);
+
+ hci_dev_lock(hdev);
+ list_for_each_entry(c, &hdev->local_codecs, list) {
+ if (c->transport != TRANSPORT_SCO_ESCO)
+ continue;
+
+ if (put_user(c->id, ptr)) {
+ err = -EFAULT;
+ goto unlock;
+ }
+ ptr += sizeof(c->id);
+
+ if (put_user(c->cid, ptr)) {
+ err = -EFAULT;
+ goto unlock;
+ }
+ ptr += sizeof(c->cid);
+
+ if (put_user(c->vid, ptr)) {
+ err = -EFAULT;
+ goto unlock;
+ }
+ ptr += sizeof(c->vid);
+
+ err = hdev->get_data_path_id(hdev);
+ if (err < 0) {
+ err = -EFAULT;
+ goto unlock;
+ }
+ data_path = (__u8)err;
+ if (put_user(data_path, ptr)) {
+ err = -EFAULT;
+ goto unlock;
+ }
+ ptr += sizeof(data_path);
+
+ if (put_user(c->num_caps, ptr)) {
+ err = -EFAULT;
+ goto unlock;
+ }
+ ptr += sizeof(c->num_caps);
+
+ 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_user(ptr, c->caps, len)) {
+ err = -EFAULT;
+ goto unlock;
+ }
+ ptr += len;
+ }
+unlock:
+ hci_dev_unlock(hdev);
+
+ break;
+
default:
err = -ENOPROTOOPT;
break;
--
2.17.1


2021-05-19 17:52:46

by Kiran K

[permalink] [raw]
Subject: [PATCH v8 7/9] Bluetooth: Add BT_CODEC option for setsockopt over SCO

Add BT_CODEC option on setsockopt system call to allow user space
audio modules to set codec. Driver also configures codec if non-HCI
data is selected.

Signed-off-by: Kiran K <[email protected]>
Reviewed-by: Chethan T N <[email protected]>
Reviewed-by: Srivatsa Ravishankar <[email protected]>
---
include/net/bluetooth/bluetooth.h | 2 +
net/bluetooth/sco.c | 63 +++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+)

diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 1840756958ce..0e8802d09068 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -173,6 +173,8 @@ struct bt_codecs {
struct bt_codec codecs[];
} __packed;

+#define CODING_FORMAT_CVSD 0x02
+
__printf(1, 2)
void bt_info(const char *fmt, ...);
__printf(1, 2)
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index d66293d1cba4..d59f30fc4b9f 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -67,6 +67,7 @@ struct sco_pinfo {
__u32 flags;
__u16 setting;
__u8 cmsg_mask;
+ struct bt_codec codec;
struct sco_conn *conn;
};

@@ -438,6 +439,7 @@ static void __sco_sock_close(struct sock *sk)
sock_set_flag(sk, SOCK_ZAPPED);
break;
}
+
}

/* Must be called on unlocked socket. */
@@ -499,6 +501,10 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock,
sk->sk_state = BT_OPEN;

sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
+ sco_pi(sk)->codec.id = CODING_FORMAT_CVSD;
+ sco_pi(sk)->codec.cid = 0xffff;
+ sco_pi(sk)->codec.vid = 0xffff;
+ sco_pi(sk)->codec.data_path = 0x00;

timer_setup(&sk->sk_timer, sco_sock_timeout, 0);

@@ -808,6 +814,9 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
struct sock *sk = sock->sk;
int len, err = 0;
struct bt_voice voice;
+ struct bt_codecs *codecs;
+ struct hci_dev *hdev;
+ __u8 buffer[255];
u32 opt;

BT_DBG("sk %p", sk);
@@ -870,6 +879,60 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
sco_pi(sk)->cmsg_mask &= SCO_CMSG_PKT_STATUS;
break;

+ case BT_CODEC:
+ if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND &&
+ sk->sk_state != BT_CONNECT2) {
+ err = -EINVAL;
+ break;
+ }
+
+ hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
+ if (!hdev) {
+ err = -EBADFD;
+ break;
+ }
+
+ if (!test_bit(HCI_QUIRK_HFP_OFFLOAD_CODECS_SUPPORTED, &hdev->quirks)) {
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ if (optlen < sizeof(struct bt_codecs) || optlen > 255) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (copy_from_sockptr(buffer, optval, optlen)) {
+ err = -EFAULT;
+ break;
+ }
+
+ codecs = (void *)buffer;
+
+ if (codecs->num_codecs > 1) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (codecs->codecs[0].data_path) {
+ if (!hdev->configure_data_path) {
+ err = -EOPNOTSUPP;
+ break;
+ }
+ err = hdev->configure_data_path(hdev, SCO_LINK,
+ codecs->codecs);
+ if (err < 0)
+ break;
+
+ if (codecs->codecs[0].id == 0xff) {
+ sco_pi(sk)->codec.cid = codecs->codecs[0].cid;
+ sco_pi(sk)->codec.vid = codecs->codecs[0].vid;
+ }
+ }
+ sco_pi(sk)->codec.id = codecs->codecs[0].id;
+ sco_pi(sk)->codec.data_path = codecs->codecs[0].data_path;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
--
2.17.1


2021-06-03 14:27:13

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH v8 1/9] Bluetooth: enumerate local supported codec and cache details

Hi Kiran,

> Move reading of supported local codecs into a separate init function,
> query codecs capabilities and cache the data
>
> Signed-off-by: Kiran K <[email protected]>
> Signed-off-by: Chethan T N <[email protected]>
> Signed-off-by: Srivatsa Ravishankar <[email protected]>
> Reported-by: kernel test robot <[email protected]>
> ---
> * changes in v8:
> - add comments
> - split __u8 codec_id[5] into {__u8 id; __le16 cid, vid }
> - address review comment related codec caps structure
>
> * changes in v7:
> - keep codec enumeration call in hci_init instead of having a separate
> function
> - Remove unused bitmasks defined for LE transports
>
> * changes in v6:
> - fix compiler warning reported for ARCH=arc
>
> * changes in v5:
> - fix review comments
> - move code used to read standard/vendor codecs caps into single function
>
> * changes in v4:
> - convert reading of codecs and codecs caps calls from async to sync
>
> * changes in v3
> move codec enumeration into a new init function
>
> * changes in v2
> add skb length check before accessing data
>
> include/net/bluetooth/hci.h | 41 +++++++
> include/net/bluetooth/hci_core.h | 17 +++
> net/bluetooth/hci_core.c | 199 ++++++++++++++++++++++++++++++-
> 3 files changed, 253 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index c4b0650fb9ae..6cb9340a2d51 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -1307,6 +1307,28 @@ struct hci_rp_read_data_block_size {
> } __packed;
>
> #define HCI_OP_READ_LOCAL_CODECS 0x100b
> +struct hci_std_codecs {
> + __u8 num;
> + __u8 codec[];
> +} __packed;
> +
> +struct hci_ven_codec {
> + /* company id */
> + __le16 cid;
> + /* vendor codec id */
> + __le16 vid;
> +} __packed;

I am pretty sure that I said to use vnd and not ven. The shortcut ven for vendor is not something we used at all.

Regards

Marcel

2021-06-03 14:38:14

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH v8 5/9] Bluetooth: Add BT_CODEC option for getsockopt for SCO socket

Hi Kiran,

> Add BT_CODEC option for getsockopt systemcall over SCO socket
> to expose the codecs supported by controller
>
> Signed-off-by: Kiran K <[email protected]>
> Reviewed-by: Chethan T N <[email protected]>
> Reviewed-by: Srivatsa Ravishankar <[email protected]>
> ---
> include/net/bluetooth/bluetooth.h | 20 ++++++
> include/net/bluetooth/hci.h | 4 ++
> net/bluetooth/sco.c | 109 ++++++++++++++++++++++++++++++
> 3 files changed, 133 insertions(+)
>
> diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
> index 9125effbf448..1840756958ce 100644
> --- a/include/net/bluetooth/bluetooth.h
> +++ b/include/net/bluetooth/bluetooth.h
> @@ -153,6 +153,26 @@ struct bt_voice {
>
> #define BT_SCM_PKT_STATUS 0x03
>
> +#define BT_CODEC 19
> +
> +struct bt_codec_caps {
> + __u8 len;
> + __u8 data[];
> +} __packed;
> +
> +struct bt_codec {
> + __u8 id;
> + __le16 cid;
> + __le16 vid;
> + __u8 data_path;
> + __u8 num_caps;
> +} __packed;
> +
> +struct bt_codecs {
> + __u8 num_codecs;
> + struct bt_codec codecs[];
> +} __packed;
> +

what is encapsulating what here?

> __printf(1, 2)
> void bt_info(const char *fmt, ...);
> __printf(1, 2)
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index 731d48ca873a..9658600ae5de 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -2626,6 +2626,10 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
> #define hci_iso_data_len(h) ((h) & 0x3fff)
> #define hci_iso_data_flags(h) ((h) >> 14)
>
> +/* codec transport types */
> +#define TRANSPORT_BREDR 0x00
> +#define TRANSPORT_SCO_ESCO 0x01
> +

This is confusing. SCO_ESCO is also BREDR.

> /* le24 support */
> static inline void hci_cpu_to_le24(__u32 val, __u8 dst[3])
> {
> diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> index 3bd41563f118..d66293d1cba4 100644
> --- a/net/bluetooth/sco.c
> +++ b/net/bluetooth/sco.c
> @@ -948,6 +948,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
> struct bt_voice voice;
> u32 phys;
> int pkt_status;
> + struct codec_list *c;
> + u8 num_codecs, i, __user *ptr;
> + struct hci_dev *hdev;
> + struct hci_codec_caps *caps;
> + __u8 data_path;
>
> BT_DBG("sk %p", sk);
>
> @@ -1012,6 +1017,110 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
> err = -EFAULT;
> break;
>
> + case BT_CODEC:
> + num_codecs = 0;
> + len = 0;
> +
> + hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
> +

Remove extra empty line.

> + if (!hdev) {
> + err = -EBADFD;
> + break;
> + }
> +
> + if (!test_bit(HCI_QUIRK_HFP_OFFLOAD_CODECS_SUPPORTED, &hdev->quirks)) {
> + err = -EOPNOTSUPP;
> + break;
> + }
> +
> + if (!hdev->get_data_path_id) {
> + err = -EOPNOTSUPP;
> + break;
> + }

See it is pointless to check a quirk, you can use the callback for it.

> +
> + hci_dev_lock(hdev);
> + list_for_each_entry(c, &hdev->local_codecs, list) {
> + if (c->transport != TRANSPORT_SCO_ESCO)
> + continue;
> + num_codecs++;
> + for (i = 0, caps = c->caps; i < c->num_caps; i++) {
> + len += 1 + caps->len;
> + caps = (void *)&caps->data[caps->len];
> + }
> + len += sizeof(struct bt_codec);
> + }
> + hci_dev_unlock(hdev);
> +
> + if (len > 255) {
> + err = -ENOMEM;
> + break;

I think this is the wrong error code. You need to indicate that size is wrong. ENOMEM really means that memory allocation failed.

> + }
> +
> + ptr = optval;
> + if (put_user(num_codecs, ptr)) {
> + err = -EFAULT;
> + break;
> + }
> + ptr += sizeof(num_codecs);
> +
> + hci_dev_lock(hdev);
> + list_for_each_entry(c, &hdev->local_codecs, list) {
> + if (c->transport != TRANSPORT_SCO_ESCO)
> + continue;
> +
> + if (put_user(c->id, ptr)) {
> + err = -EFAULT;
> + goto unlock;
> + }
> + ptr += sizeof(c->id);
> +
> + if (put_user(c->cid, ptr)) {
> + err = -EFAULT;
> + goto unlock;
> + }
> + ptr += sizeof(c->cid);
> +
> + if (put_user(c->vid, ptr)) {
> + err = -EFAULT;
> + goto unlock;
> + }
> + ptr += sizeof(c->vid);
> +
> + err = hdev->get_data_path_id(hdev);
> + if (err < 0) {
> + err = -EFAULT;
> + goto unlock;
> + }
> + data_path = (__u8)err;
> + if (put_user(data_path, ptr)) {
> + err = -EFAULT;
> + goto unlock;
> + }
> + ptr += sizeof(data_path);
> +
> + if (put_user(c->num_caps, ptr)) {
> + err = -EFAULT;
> + goto unlock;
> + }
> + ptr += sizeof(c->num_caps);
> +
> + 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_user(ptr, c->caps, len)) {
> + err = -EFAULT;
> + goto unlock;
> + }
> + ptr += len;
> + }
> +unlock:
> + hci_dev_unlock(hdev);
> +
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;

Regards

Marcel

2021-06-03 14:38:33

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH v8 7/9] Bluetooth: Add BT_CODEC option for setsockopt over SCO

Hi Kiran,

> Add BT_CODEC option on setsockopt system call to allow user space
> audio modules to set codec. Driver also configures codec if non-HCI
> data is selected.
>
> Signed-off-by: Kiran K <[email protected]>
> Reviewed-by: Chethan T N <[email protected]>
> Reviewed-by: Srivatsa Ravishankar <[email protected]>
> ---
> include/net/bluetooth/bluetooth.h | 2 +
> net/bluetooth/sco.c | 63 +++++++++++++++++++++++++++++++
> 2 files changed, 65 insertions(+)
>
> diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
> index 1840756958ce..0e8802d09068 100644
> --- a/include/net/bluetooth/bluetooth.h
> +++ b/include/net/bluetooth/bluetooth.h
> @@ -173,6 +173,8 @@ struct bt_codecs {
> struct bt_codec codecs[];
> } __packed;
>
> +#define CODING_FORMAT_CVSD 0x02
> +
> __printf(1, 2)
> void bt_info(const char *fmt, ...);
> __printf(1, 2)
> diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> index d66293d1cba4..d59f30fc4b9f 100644
> --- a/net/bluetooth/sco.c
> +++ b/net/bluetooth/sco.c
> @@ -67,6 +67,7 @@ struct sco_pinfo {
> __u32 flags;
> __u16 setting;
> __u8 cmsg_mask;
> + struct bt_codec codec;
> struct sco_conn *conn;
> };
>
> @@ -438,6 +439,7 @@ static void __sco_sock_close(struct sock *sk)
> sock_set_flag(sk, SOCK_ZAPPED);
> break;
> }
> +
> }
>
> /* Must be called on unlocked socket. */
> @@ -499,6 +501,10 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock,
> sk->sk_state = BT_OPEN;
>
> sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
> + sco_pi(sk)->codec.id = CODING_FORMAT_CVSD;
> + sco_pi(sk)->codec.cid = 0xffff;
> + sco_pi(sk)->codec.vid = 0xffff;
> + sco_pi(sk)->codec.data_path = 0x00;
>
> timer_setup(&sk->sk_timer, sco_sock_timeout, 0);
>
> @@ -808,6 +814,9 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
> struct sock *sk = sock->sk;
> int len, err = 0;
> struct bt_voice voice;
> + struct bt_codecs *codecs;
> + struct hci_dev *hdev;
> + __u8 buffer[255];
> u32 opt;
>
> BT_DBG("sk %p", sk);
> @@ -870,6 +879,60 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
> sco_pi(sk)->cmsg_mask &= SCO_CMSG_PKT_STATUS;
> break;
>
> + case BT_CODEC:
> + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND &&
> + sk->sk_state != BT_CONNECT2) {
> + err = -EINVAL;
> + break;
> + }
> +
> + hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
> + if (!hdev) {
> + err = -EBADFD;
> + break;
> + }
> +
> + if (!test_bit(HCI_QUIRK_HFP_OFFLOAD_CODECS_SUPPORTED, &hdev->quirks)) {
> + err = -EOPNOTSUPP;
> + break;
> + }

Same here. Just check the configure_data_path (and I think set_data_path is better and shorter) is available, if not bail our here right away instead checking this over and over again later.

> +
> + if (optlen < sizeof(struct bt_codecs) || optlen > 255) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (copy_from_sockptr(buffer, optval, optlen)) {
> + err = -EFAULT;
> + break;
> + }
> +
> + codecs = (void *)buffer;
> +
> + if (codecs->num_codecs > 1) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (codecs->codecs[0].data_path) {
> + if (!hdev->configure_data_path) {
> + err = -EOPNOTSUPP;
> + break;
> + }
> + err = hdev->configure_data_path(hdev, SCO_LINK,
> + codecs->codecs);
> + if (err < 0)
> + break;
> +
> + if (codecs->codecs[0].id == 0xff) {
> + sco_pi(sk)->codec.cid = codecs->codecs[0].cid;
> + sco_pi(sk)->codec.vid = codecs->codecs[0].vid;
> + }
> + }
> + sco_pi(sk)->codec.id = codecs->codecs[0].id;
> + sco_pi(sk)->codec.data_path = codecs->codecs[0].data_path;
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;

Regards

Marcel

2021-06-08 12:00:28

by Kiran K

[permalink] [raw]
Subject: RE: [PATCH v8 1/9] Bluetooth: enumerate local supported codec and cache details

Hi Marcel,

> > #define HCI_OP_READ_LOCAL_CODECS 0x100b
> > +struct hci_std_codecs {
> > + __u8 num;
> > + __u8 codec[];
> > +} __packed;
> > +
> > +struct hci_ven_codec {
> > + /* company id */
> > + __le16 cid;
> > + /* vendor codec id */
> > + __le16 vid;
> > +} __packed;
>
> I am pretty sure that I said to use vnd and not ven. The shortcut ven for
> vendor is not something we used at all.
>
Ack. I overlooked at it. I will fix this in the next patchset.

Regards,
Kiran

2021-06-08 12:06:04

by Kiran K

[permalink] [raw]
Subject: RE: [PATCH v8 5/9] Bluetooth: Add BT_CODEC option for getsockopt for SCO socket

Hi Marcel,

> >
> > #define BT_SCM_PKT_STATUS 0x03
> >
> > +#define BT_CODEC 19
> > +
> > +struct bt_codec_caps {
> > + __u8 len;
> > + __u8 data[];
> > +} __packed;
> > +
> > +struct bt_codec {
> > + __u8 id;
> > + __le16 cid;
> > + __le16 vid;
> > + __u8 data_path;
> > + __u8 num_caps;
> > +} __packed;
> > +
> > +struct bt_codecs {
> > + __u8 num_codecs;
> > + struct bt_codec codecs[];
> > +} __packed;
> > +
>
> what is encapsulating what here?

bt_codec encapsulates caps.
bt_codecs is aggregation of bt_codec.

>
> > __printf(1, 2)
> > void bt_info(const char *fmt, ...);
> > __printf(1, 2)
> > diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> > index 731d48ca873a..9658600ae5de 100644
> > --- a/include/net/bluetooth/hci.h
> > +++ b/include/net/bluetooth/hci.h
> > @@ -2626,6 +2626,10 @@ static inline struct hci_sco_hdr
> *hci_sco_hdr(const struct sk_buff *skb)
> > #define hci_iso_data_len(h) ((h) & 0x3fff)
> > #define hci_iso_data_flags(h) ((h) >> 14)
> >
> > +/* codec transport types */
> > +#define TRANSPORT_BREDR 0x00
> > +#define TRANSPORT_SCO_ESCO 0x01
> > +
>
> This is confusing. SCO_ESCO is also BREDR.

Ack. I will remove the suffix BREDR.

>
> > /* le24 support */
> > static inline void hci_cpu_to_le24(__u32 val, __u8 dst[3]) { diff
> > --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index
> > 3bd41563f118..d66293d1cba4 100644
> > --- a/net/bluetooth/sco.c
> > +++ b/net/bluetooth/sco.c
> > @@ -948,6 +948,11 @@ static int sco_sock_getsockopt(struct socket *sock,
> int level, int optname,
> > struct bt_voice voice;
> > u32 phys;
> > int pkt_status;
> > + struct codec_list *c;
> > + u8 num_codecs, i, __user *ptr;
> > + struct hci_dev *hdev;
> > + struct hci_codec_caps *caps;
> > + __u8 data_path;
> >
> > BT_DBG("sk %p", sk);
> >
> > @@ -1012,6 +1017,110 @@ static int sco_sock_getsockopt(struct socket
> *sock, int level, int optname,
> > err = -EFAULT;
> > break;
> >
> > + case BT_CODEC:
> > + num_codecs = 0;
> > + len = 0;
> > +
> > + hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src,
> > +BDADDR_BREDR);
> > +
>
> Remove extra empty line.

Ack

>
> > + if (!hdev) {
> > + err = -EBADFD;
> > + break;
> > + }
> > +
> > + if
> (!test_bit(HCI_QUIRK_HFP_OFFLOAD_CODECS_SUPPORTED, &hdev->quirks))
> {
> > + err = -EOPNOTSUPP;
> > + break;
> > + }
> > +
> > + if (!hdev->get_data_path_id) {
> > + err = -EOPNOTSUPP;
> > + break;
> > + }
>
> See it is pointless to check a quirk, you can use the callback for it.
>

Ack

> > +
> > + hci_dev_lock(hdev);
> > + list_for_each_entry(c, &hdev->local_codecs, list) {
> > + if (c->transport != TRANSPORT_SCO_ESCO)
> > + continue;
> > + num_codecs++;
> > + for (i = 0, caps = c->caps; i < c->num_caps; i++) {
> > + len += 1 + caps->len;
> > + caps = (void *)&caps->data[caps->len];
> > + }
> > + len += sizeof(struct bt_codec);
> > + }
> > + hci_dev_unlock(hdev);
> > +
> > + if (len > 255) {
> > + err = -ENOMEM;
> > + break;
>
> I think this is the wrong error code. You need to indicate that size is wrong.
> ENOMEM really means that memory allocation failed.

I will change error code to ENOBUFS
>

Thanks,
Kiran