From: Sudha Bheemanna Date: Tue, 6 Sep 2016 11:08:36 +0000 (+0530) Subject: Bluetooth: Add MGMT command to set SCO settings X-Git-Tag: accepted/tizen/common/20161219.151653~155 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9eedcb2b6671f73072428184f496af2e8963f425;p=platform%2Fkernel%2Flinux-exynos.git Bluetooth: Add MGMT command to set SCO settings Added code to set sco settings. Change-Id: I37aa572436241b06e00d1e9e75964aac747eeba5 Signed-off-by: Sudha Bheemanna [remove sco link policy part] Signed-off-by: Seung-Woo Kim --- diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 9bc429e..37b4539 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1518,6 +1518,29 @@ struct hci_cc_rp_get_raw_rssi { __le16 conn_handle; __s8 rssi_dbm; } __packed; + +#define HCI_BCM_ENABLE_WBS_REQ 0xfc7e +struct hci_cp_bcm_wbs_req { + __u8 role; + __le16 pkt_type; +} __packed; + +#define HCI_BCM_I2C_PCM_REQ 0xfc6d +struct hci_cp_i2c_pcm_req { + __u8 i2c_enable; + __u8 is_master; + __u8 pcm_rate; + __u8 clock_rate; +} __packed; + +#define HCI_BCM_SCO_PCM_REQ 0xfc1c +struct hci_cp_sco_pcm_req { + __u8 sco_routing; + __u8 pcm_rate; + __u8 frame_type; + __u8 sync_mode; + __u8 clock_mode; +} __packed; #endif /* ---- HCI Events ---- */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index dfb2d91..e1aa784 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -484,6 +484,8 @@ struct hci_conn { #ifdef TIZEN_BT bool rssi_monitored; + __u8 sco_role; + __u16 voice_setting; #endif struct hci_conn *link; @@ -873,6 +875,23 @@ static inline int hci_conn_hash_lookup_rssi_count(struct hci_dev *hdev) bool hci_le_discovery_active(struct hci_dev *hdev); void hci_le_discovery_set_state(struct hci_dev *hdev, int state); + +static inline struct hci_conn *hci_conn_hash_lookup_sco(struct hci_dev *hdev) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *c; + + rcu_read_lock(); + list_for_each_entry_rcu(c, &h->list, list) { + if (c->type == SCO_LINK || c->type == ESCO_LINK) { + rcu_read_unlock(); + return c; + } + } + rcu_read_unlock(); + + return NULL; +} #endif int hci_disconnect(struct hci_conn *conn, __u8 reason); diff --git a/include/net/bluetooth/mgmt_tizen.h b/include/net/bluetooth/mgmt_tizen.h index e1eb50f..1f861a0 100644 --- a/include/net/bluetooth/mgmt_tizen.h +++ b/include/net/bluetooth/mgmt_tizen.h @@ -151,6 +151,16 @@ struct mgmt_cp_le_set_scan_params { } __packed; #define MGMT_LE_SET_SCAN_PARAMS_SIZE 5 +#define MGMT_SCO_ROLE_HANDSFREE 0x00 +#define MGMT_SCO_ROLE_AUDIO_GATEWAY 0x01 +#define MGMT_OP_SET_VOICE_SETTING (TIZEN_OP_CODE_BASE + 0x10) +struct mgmt_cp_set_voice_setting { + bdaddr_t bdaddr; + uint8_t sco_role; + uint16_t voice_setting; +} __packed; +#define MGMT_SET_VOICE_SETTING_SIZE 9 + /* EVENTS */ /* For device name update changes */ diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index f40ddb4..bca05bf 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -46,4 +46,11 @@ struct sco_conninfo { __u8 dev_class[3]; }; +#ifdef TIZEN_BT +void sco_connect_set_gw_nbc(struct hci_dev *hdev); +void sco_connect_set_gw_wbc(struct hci_dev *hdev); +void sco_connect_set_nbc(struct hci_dev *hdev); +void sco_connect_set_wbc(struct hci_dev *hdev); +#endif + #endif /* __SCO_H */ diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ee5e598..0a6ac44 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -41,6 +41,16 @@ struct sco_param { u8 retrans_effort; }; +#ifdef TIZEN_BT +static const struct sco_param esco_param_cvsd[] = { + { (EDR_ESCO_MASK & ~ESCO_2EV3) | SCO_ESCO_MASK | ESCO_EV3, + 0x000a, 0x01 }, /* S3 */ + { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007, 0x01 }, /* S2 */ + { EDR_ESCO_MASK | ESCO_EV3, 0x0007, 0x01 }, /* S1 */ + { EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0x01 }, /* D1 */ + { EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0x01 }, /* D0 */ +}; +#else static const struct sco_param esco_param_cvsd[] = { { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a, 0x01 }, /* S3 */ { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007, 0x01 }, /* S2 */ @@ -48,16 +58,25 @@ static const struct sco_param esco_param_cvsd[] = { { EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0x01 }, /* D1 */ { EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0x01 }, /* D0 */ }; +#endif static const struct sco_param sco_param_cvsd[] = { { EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0xff }, /* D1 */ { EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0xff }, /* D0 */ }; +#ifdef TIZEN_BT +static const struct sco_param esco_param_msbc[] = { + { (EDR_ESCO_MASK & ~ESCO_2EV3) | ESCO_EV3, + 0x000d, 0x02 }, /* T2 */ + { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d, 0x02 }, /* T2 */ +}; +#else static const struct sco_param esco_param_msbc[] = { { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d, 0x02 }, /* T2 */ { EDR_ESCO_MASK | ESCO_EV3, 0x0008, 0x02 }, /* T1 */ }; +#endif static void hci_le_create_connection_cancel(struct hci_conn *conn) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 37b5c4a..7e573fa 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -34,6 +34,7 @@ #include #ifdef TIZEN_BT #include +#include #endif #include "hci_request.h" @@ -7164,6 +7165,61 @@ static int le_set_scan_params(struct sock *sk, struct hci_dev *hdev, return err; } +static int set_voice_setting(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_voice_setting *cp = data; + struct hci_conn *conn; + struct hci_conn *sco_conn; + + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_bredr_capable(hdev)) { + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_VOICE_SETTING, + MGMT_STATUS_NOT_SUPPORTED); + } + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (!conn) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_SET_VOICE_SETTING, 0, NULL, 0); + goto unlock; + } + + conn->voice_setting = cp->voice_setting; + conn->sco_role = cp->sco_role; + + sco_conn = hci_conn_hash_lookup_sco(hdev); + if (sco_conn && bacmp(&sco_conn->dst, &cp->bdaddr) != 0) { + BT_ERR("There is other SCO connection."); + goto done; + } + + if (conn->sco_role == MGMT_SCO_ROLE_HANDSFREE) { + if (conn->voice_setting == 0x0063) + sco_connect_set_wbc(hdev); + else + sco_connect_set_nbc(hdev); + } else { + if (conn->voice_setting == 0x0063) + sco_connect_set_gw_wbc(hdev); + else + sco_connect_set_gw_nbc(hdev); + } + +done: + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_VOICE_SETTING, 0, + cp, sizeof(cp)); + +unlock: + hci_dev_unlock(hdev); + return err; +} + void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code) { struct mgmt_ev_hardware_error ev; @@ -9081,6 +9137,7 @@ static const struct hci_mgmt_handler tizen_mgmt_handlers[] = { { le_conn_update, MGMT_LE_CONN_UPDATE_SIZE }, { set_manufacturer_data, MGMT_SET_MANUFACTURER_DATA_SIZE }, { le_set_scan_params, MGMT_LE_SET_SCAN_PARAMS_SIZE }, + { set_voice_setting, MGMT_SET_VOICE_SETTING_SIZE }, }; #endif diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 8611bc7..805282b 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -32,6 +32,11 @@ #include #include +#ifdef TIZEN_BT +#include +#include +#endif + static bool disable_esco; static const struct proto_ops sco_sock_ops; @@ -829,8 +834,15 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char } /* Explicitly check for these values */ +#ifdef TIZEN_BT + /* Codec defer accept */ + if (voice.setting != (BT_VOICE_TRANSPARENT | + BT_VOICE_CVSD_16BIT) && + voice.setting != BT_VOICE_CVSD_16BIT) { +#else if (voice.setting != BT_VOICE_TRANSPARENT && voice.setting != BT_VOICE_CVSD_16BIT) { +#endif err = -EINVAL; break; } @@ -999,6 +1011,10 @@ static int sco_sock_release(struct socket *sock) release_sock(sk); } +#ifdef TIZEN_BT + /* SCO kernel panic fix */ + bt_sock_unlink(&sco_sk_list, sk); +#endif sock_orphan(sk); sco_sock_kill(sk); return err; @@ -1020,7 +1036,13 @@ static void sco_conn_ready(struct sco_conn *conn) } else { sco_conn_lock(conn); +#ifdef TIZEN_BT + /* Multiple SCO accept feature */ + parent = sco_get_sock_listen(&conn->hcon->dst); +#else parent = sco_get_sock_listen(&conn->hcon->src); +#endif + if (!parent) { sco_conn_unlock(conn); return; @@ -1058,6 +1080,131 @@ static void sco_conn_ready(struct sco_conn *conn) } } +#ifdef TIZEN_BT +/* WBC/NBC feature */ +void sco_connect_set_gw_nbc(struct hci_dev *hdev) +{ + struct hci_cp_write_voice_setting cp1; + struct hci_cp_bcm_wbs_req cp2; + struct hci_cp_i2c_pcm_req cp3; + struct hci_cp_sco_pcm_req cp4; + + BT_DBG("Setting the NBC params, hdev = %p", hdev); + + cp1.voice_setting = cpu_to_le16(0x0060); + hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1); + hdev->voice_setting = cpu_to_le16(0x0060); + + cp2.role = 0x00; /* WbDisable */ + cp2.pkt_type = cpu_to_le16(0x0002); + hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2); + + cp3.i2c_enable = 0x01; + cp3.is_master = 0x00; + cp3.pcm_rate = 0x00; + cp3.clock_rate = 0x01; + hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3); + + cp4.sco_routing = 0x00; + cp4.pcm_rate = 0x01; + cp4.frame_type = 0x00; + cp4.sync_mode = 0x00; + cp4.clock_mode = 0x00; + hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4); +} + +void sco_connect_set_gw_wbc(struct hci_dev *hdev) +{ + struct hci_cp_write_voice_setting cp1; + struct hci_cp_bcm_wbs_req cp2; + struct hci_cp_i2c_pcm_req cp3; + struct hci_cp_sco_pcm_req cp4; + + BT_DBG("Setting the WBC params, hdev = %p", hdev); + cp1.voice_setting = cpu_to_le16(0x0003 | 0x0060); + hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1); + hdev->voice_setting = cpu_to_le16(0x0003 | 0x0060); + + cp2.role = 0x01; /* Enable */ + cp2.pkt_type = cpu_to_le16(0x0002); + hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2); + + cp3.i2c_enable = 0x00; + cp3.is_master = 0x00; + cp3.pcm_rate = 0x01; + cp3.clock_rate = 0x02; + hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3); + + cp4.sco_routing = 0x00; + cp4.pcm_rate = 0x00; + cp4.frame_type = 0x00; + cp4.sync_mode = 0x00; + cp4.clock_mode = 0x00; + hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4); +} + +void sco_connect_set_nbc(struct hci_dev *hdev) +{ + struct hci_cp_write_voice_setting cp1; + struct hci_cp_bcm_wbs_req cp2; + struct hci_cp_i2c_pcm_req cp3; + struct hci_cp_sco_pcm_req cp4; + + BT_DBG("Setting the NBC params, hdev = %p", hdev); + + cp1.voice_setting = cpu_to_le16(0x0060); + hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1); + hdev->voice_setting = cpu_to_le16(0x0060); + + cp2.role = 0x00; /* WbDisable */ + cp2.pkt_type = cpu_to_le16(0x0002); + hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2); + + cp3.i2c_enable = 0x00; + cp3.is_master = 0x00; + cp3.pcm_rate = 0x00; + cp3.clock_rate = 0x04; + hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3); + + cp4.sco_routing = 0x00; + cp4.pcm_rate = 0x04; + cp4.frame_type = 0x00; + cp4.sync_mode = 0x00; + cp4.clock_mode = 0x00; + hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4); +} + +void sco_connect_set_wbc(struct hci_dev *hdev) +{ + struct hci_cp_write_voice_setting cp1; + struct hci_cp_bcm_wbs_req cp2; + struct hci_cp_i2c_pcm_req cp3; + struct hci_cp_sco_pcm_req cp4; + + BT_DBG("Setting the WBC params, hdev = %p", hdev); + cp1.voice_setting = cpu_to_le16(0x0003 | 0x0060); + hci_send_cmd(hdev, HCI_OP_WRITE_VOICE_SETTING, sizeof(cp1), &cp1); + hdev->voice_setting = cpu_to_le16(0x0003 | 0x0060); + + cp2.role = 0x01; /* Enable */ + cp2.pkt_type = cpu_to_le16(0x0002); + hci_send_cmd(hdev, HCI_BCM_ENABLE_WBS_REQ, sizeof(cp2), &cp2); + + cp3.i2c_enable = 0x00; + cp3.is_master = 0x00; + cp3.pcm_rate = 0x01; + cp3.clock_rate = 0x04; + hci_send_cmd(hdev, HCI_BCM_I2C_PCM_REQ, sizeof(cp3), &cp3); + + cp4.sco_routing = 0x00; + cp4.pcm_rate = 0x04; + cp4.frame_type = 0x00; + cp4.sync_mode = 0x00; + cp4.clock_mode = 0x00; + hci_send_cmd(hdev, HCI_BCM_SCO_PCM_REQ, sizeof(cp4), &cp4); +} +#endif + /* ----- SCO interface with lower layer (HCI) ----- */ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) { @@ -1072,8 +1219,14 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) if (sk->sk_state != BT_LISTEN) continue; +#ifdef TIZEN_BT + /* Multiple SCO accept feature */ + if (!bacmp(&sco_pi(sk)->src, bdaddr) || + !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) { +#else if (!bacmp(&sco_pi(sk)->src, &hdev->bdaddr) || !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) { +#endif lm |= HCI_LM_ACCEPT; if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) @@ -1083,6 +1236,36 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) } read_unlock(&sco_sk_list.lock); +#ifdef TIZEN_BT + /* WBC/NBC feature */ + if ((lm & HCI_LM_ACCEPT) && !hci_conn_hash_lookup_sco(hdev)) { + struct hci_conn *hcon_acl; + + hcon_acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); + if (!hcon_acl) { + BT_ERR("ACL doesn't alive. Use 0x%X", + hdev->voice_setting); + + if (hdev->voice_setting == 0x0063) + sco_connect_set_wbc(hdev); + else + sco_connect_set_nbc(hdev); + } else if (hcon_acl->sco_role == MGMT_SCO_ROLE_HANDSFREE) { + BT_DBG("Handsfree SCO 0x%X", hcon_acl->voice_setting); + if (hcon_acl->voice_setting == 0x0063) + sco_connect_set_wbc(hdev); + else + sco_connect_set_nbc(hdev); + } else { + BT_DBG("Gateway SCO 0x%X", hcon_acl->voice_setting); + if (hcon_acl->voice_setting == 0x0063) + sco_connect_set_gw_wbc(hdev); + else + sco_connect_set_gw_nbc(hdev); + } + } +#endif + return lm; }