X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=net%2Fbluetooth%2Fmgmt.c;h=f4a015840b1b885c2add47bce262f3b1ea2f9de8;hb=6c6445fb20fffc5baba8a8795d58a21dc3deaaef;hp=88563d7d505620070dbfbea397d6638c62277a73;hpb=262c03f56849f047c357d45bc25c3af6ee6c6977;p=platform%2Fkernel%2Flinux-starfive.git diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 88563d7..f4a0158 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" @@ -7028,9 +7029,14 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data, return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY, MGMT_STATUS_INVALID_PARAMS); +#ifndef TIZEN_BT + /* commenting out since set privacy command is always rejected + * if this condition is enabled. + */ if (hdev_is_powered(hdev)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY, MGMT_STATUS_REJECTED); +#endif hci_dev_lock(hdev); @@ -8823,6 +8829,92 @@ 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; +} + +static int get_adv_tx_power(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_rp_get_adv_tx_power *rp; + size_t rp_len; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + rp_len = sizeof(*rp); + rp = kmalloc(rp_len, GFP_KERNEL); + if (!rp) { + err = -ENOMEM; + goto unlock; + } + + rp->adv_tx_power = hdev->adv_tx_power; + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_TX_POWER, 0, rp, + rp_len); + + kfree(rp); + +unlock: + hci_dev_unlock(hdev); + + return err; +} + void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code) { struct mgmt_ev_hardware_error ev; @@ -8835,6 +8927,366 @@ void mgmt_tx_timeout_error(struct hci_dev *hdev) { mgmt_event(MGMT_EV_TX_TIMEOUT_ERROR, hdev, NULL, 0, NULL); } + +void mgmt_multi_adv_state_change_evt(struct hci_dev *hdev, u8 adv_instance, + u8 state_change_reason, u16 connection_handle) +{ + struct mgmt_ev_vendor_specific_multi_adv_state_changed mgmt_ev; + + BT_DBG("Multi adv state changed [%2.2X %2.2X %2.2X]", + adv_instance, state_change_reason, connection_handle); + + mgmt_ev.adv_instance = adv_instance; + mgmt_ev.state_change_reason = state_change_reason; + mgmt_ev.connection_handle = connection_handle; + + mgmt_event(MGMT_EV_MULTI_ADV_STATE_CHANGED, hdev, &mgmt_ev, + sizeof(struct mgmt_ev_vendor_specific_multi_adv_state_changed), + NULL); +} + +static int enable_bt_6lowpan(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + int err; + struct mgmt_cp_enable_6lowpan *cp = data; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ENABLE_6LOWPAN, + MGMT_STATUS_NOT_POWERED); + goto unlocked; + } + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ENABLE_6LOWPAN, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (cp->enable_6lowpan) + bt_6lowpan_enable(); + else + bt_6lowpan_disable(); + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ENABLE_6LOWPAN, + MGMT_STATUS_SUCCESS, NULL, 0); +unlocked: + hci_dev_unlock(hdev); + return err; +} + +static int connect_bt_6lowpan(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_connect_6lowpan *cp = data; + __u8 addr_type = ADDR_LE_DEV_PUBLIC; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (!hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN, + MGMT_STATUS_REJECTED); + goto unlocked; + } + + if (bdaddr_type_is_le(cp->addr.type)) { + if (cp->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + } else { + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN, + MGMT_STATUS_INVALID_PARAMS, NULL, 0); + goto unlocked; + } + + hci_dev_unlock(hdev); + + /* 6lowpan Connect */ + err = _bt_6lowpan_connect(&cp->addr.bdaddr, cp->addr.type); + + hci_dev_lock(hdev); + + if (err < 0) { + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN, + MGMT_STATUS_REJECTED, NULL, 0); + + goto unlocked; + } + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN, 0, + NULL, 0); +unlocked: + hci_dev_unlock(hdev); + return err; +} + +static int disconnect_bt_6lowpan(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_disconnect_6lowpan *cp = data; + struct hci_conn *conn = NULL; + __u8 addr_type = ADDR_LE_DEV_PUBLIC; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT_6LOWPAN, + MGMT_STATUS_NOT_SUPPORTED); + goto unlocked; + } + + if (!hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT_6LOWPAN, + MGMT_STATUS_REJECTED); + goto unlocked; + } + + if (bdaddr_type_is_le(cp->addr.type)) { + if (cp->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + } else { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_DISCONNECT_6LOWPAN, + MGMT_STATUS_INVALID_PARAMS, NULL, 0); + goto unlocked; + } + + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); + if (!conn) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_DISCONNECT_6LOWPAN, + MGMT_STATUS_NOT_CONNECTED, NULL, 0); + goto unlocked; + } + + if (conn->dst_type != addr_type) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_DISCONNECT_6LOWPAN, + MGMT_STATUS_INVALID_PARAMS, NULL, 0); + goto unlocked; + } + + if (conn->state != BT_CONNECTED) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_DISCONNECT_6LOWPAN, + MGMT_STATUS_NOT_CONNECTED, NULL, 0); + goto unlocked; + } + + /* 6lowpan Disconnect */ + err = _bt_6lowpan_disconnect(conn->l2cap_data, cp->addr.type); + if (err < 0) { + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_DISCONNECT_6LOWPAN, + MGMT_STATUS_REJECTED, NULL, 0); + goto unlocked; + } + + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN, 0, + NULL, 0); + +unlocked: + hci_dev_unlock(hdev); + return err; +} + +void mgmt_6lowpan_conn_changed(struct hci_dev *hdev, char if_name[16], + bdaddr_t *bdaddr, u8 addr_type, bool connected) +{ + char buf[512]; + struct mgmt_ev_6lowpan_conn_state_changed *ev = (void *)buf; + size_t ev_size; + + memset(buf, 0, sizeof(buf)); + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = addr_type; + ev->connected = connected; + memcpy(ev->ifname, (__u8 *)if_name, 16); + + ev_size = sizeof(*ev); + + mgmt_event(MGMT_EV_6LOWPAN_CONN_STATE_CHANGED, hdev, ev, ev_size, NULL); +} + +void mgmt_le_read_maximum_data_length_complete(struct hci_dev *hdev, u8 status) +{ + struct mgmt_pending_cmd *cmd; + struct mgmt_rp_le_read_maximum_data_length rp; + + BT_DBG("%s status %u", hdev->name, status); + + cmd = pending_find(MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, hdev); + if (!cmd) + return; + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, + mgmt_status(status)); + + memset(&rp, 0, sizeof(rp)); + + rp.max_tx_octets = cpu_to_le16(hdev->le_max_tx_len); + rp.max_tx_time = cpu_to_le16(hdev->le_max_tx_time); + rp.max_rx_octets = cpu_to_le16(hdev->le_max_rx_len); + rp.max_rx_time = cpu_to_le16(hdev->le_max_rx_time); + + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, 0, + &rp, sizeof(rp)); + + mgmt_pending_remove(cmd); +} + +static int read_maximum_le_data_length(struct sock *sk, + struct hci_dev *hdev, void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + int err; + + BT_DBG("read_maximum_le_data_length %s", hdev->name); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, + MGMT_STATUS_NOT_POWERED); + goto unlock; + } + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, + MGMT_STATUS_NOT_SUPPORTED); + goto unlock; + } + + if (pending_find(MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, + MGMT_STATUS_BUSY); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, + hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + err = hci_send_cmd(hdev, HCI_OP_LE_READ_MAX_DATA_LEN, 0, NULL); + if (err < 0) + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); + return err; +} + +void mgmt_le_write_host_suggested_data_length_complete(struct hci_dev *hdev, + u8 status) +{ + struct mgmt_pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, hdev); + if (!cmd) { + BT_ERR("cmd not found in the pending list"); + goto unlock; + } + + if (status) + mgmt_cmd_status(cmd->sk, hdev->id, + MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, hdev->id, + MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, + 0, NULL, 0); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int write_host_suggested_le_data_length(struct sock *sk, + struct hci_dev *hdev, void *data, u16 len) +{ + struct mgmt_pending_cmd *cmd; + struct mgmt_cp_le_write_host_suggested_data_length *cp = data; + struct hci_cp_le_write_def_data_len hci_data; + int err = 0; + + BT_DBG("Write host suggested data length request for %s", hdev->name); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, + MGMT_STATUS_NOT_POWERED); + goto unlock; + } + + if (!lmp_le_capable(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, + MGMT_STATUS_NOT_SUPPORTED); + goto unlock; + } + + if (pending_find(MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, + MGMT_STATUS_BUSY); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, + hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_data.tx_len = cp->def_tx_octets; + hci_data.tx_time = cp->def_tx_time; + + err = hci_send_cmd(hdev, HCI_OP_LE_WRITE_DEF_DATA_LEN, + sizeof(hci_data), &hci_data); + if (err < 0) + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); + + return err; +} #endif /* TIZEN_BT */ static bool ltk_is_valid(struct mgmt_ltk_info *key) @@ -9797,6 +10249,48 @@ int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr, return mgmt_event(MGMT_EV_CONN_UPDATED, hdev, &ev, sizeof(ev), NULL); } + +/* le device found event - Pass adv type */ +void mgmt_le_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, u8 *eir, + u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len, u8 adv_type) +{ + char buf[512]; + struct mgmt_ev_le_device_found *ev = (void *)buf; + size_t ev_size; + + if (!hci_discovery_active(hdev) && !hci_le_discovery_active(hdev)) + return; + + /* Make sure that the buffer is big enough. The 5 extra bytes + * are for the potential CoD field. + */ + if (sizeof(*ev) + eir_len + scan_rsp_len + 5 > sizeof(buf)) + return; + + memset(buf, 0, sizeof(buf)); + + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = link_to_bdaddr(link_type, addr_type); + ev->rssi = rssi; + ev->flags = cpu_to_le32(flags); + ev->adv_type = adv_type; + + if (eir_len > 0) + memcpy(ev->eir, eir, eir_len); + + if (dev_class && !eir_get_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, NULL)) + eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, + dev_class, 3); + + if (scan_rsp_len > 0) + memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len); + + ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len); + ev_size = sizeof(*ev) + eir_len + scan_rsp_len; + + mgmt_event(MGMT_EV_LE_DEVICE_FOUND, hdev, ev, ev_size, NULL); +} #endif static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data, @@ -11126,6 +11620,15 @@ 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 }, + { get_adv_tx_power, MGMT_GET_ADV_TX_POWER_SIZE }, + { enable_bt_6lowpan, MGMT_ENABLE_BT_6LOWPAN_SIZE }, + { connect_bt_6lowpan, MGMT_CONNECT_6LOWPAN_SIZE }, + { disconnect_bt_6lowpan, MGMT_DISCONNECT_6LOWPAN_SIZE }, + { read_maximum_le_data_length, + MGMT_LE_READ_MAXIMUM_DATA_LENGTH_SIZE }, + { write_host_suggested_le_data_length, + MGMT_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_SIZE }, }; #endif