+
+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;
+}