diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2012-02-22 09:37:11 -0500 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@intel.com> | 2012-02-23 06:06:59 -0500 |
commit | 06199cf86a84206cfdc96b8dc02d5c27efa8c60f (patch) | |
tree | 165507d2dfa0707b4e506d811f6466a37aecb0b5 /net/bluetooth | |
parent | 6c8f12c143fe83485afa530320e6f70dfc1aad54 (diff) |
Bluetooth: mgmt: Implement Set LE command
This patch implements support for the Set LE mgmt command. Now, in
addition to the enable_le module parameter user space needs to send an
explicit Enable LE command to enable LE support.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/hci_event.c | 7 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 119 |
2 files changed, 124 insertions, 2 deletions
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3476d5c7b02d..498b71a0579a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c | |||
@@ -539,7 +539,7 @@ static void hci_set_le_support(struct hci_dev *hdev) | |||
539 | 539 | ||
540 | memset(&cp, 0, sizeof(cp)); | 540 | memset(&cp, 0, sizeof(cp)); |
541 | 541 | ||
542 | if (enable_le) { | 542 | if (enable_le && test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { |
543 | cp.le = 1; | 543 | cp.le = 1; |
544 | cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); | 544 | cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); |
545 | } | 545 | } |
@@ -1130,10 +1130,15 @@ static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev, | |||
1130 | struct sk_buff *skb) | 1130 | struct sk_buff *skb) |
1131 | { | 1131 | { |
1132 | struct hci_cp_read_local_ext_features cp; | 1132 | struct hci_cp_read_local_ext_features cp; |
1133 | struct hci_cp_write_le_host_supported *sent; | ||
1133 | __u8 status = *((__u8 *) skb->data); | 1134 | __u8 status = *((__u8 *) skb->data); |
1134 | 1135 | ||
1135 | BT_DBG("%s status 0x%x", hdev->name, status); | 1136 | BT_DBG("%s status 0x%x", hdev->name, status); |
1136 | 1137 | ||
1138 | sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED); | ||
1139 | if (sent && test_bit(HCI_MGMT, &hdev->dev_flags)) | ||
1140 | mgmt_le_enable_complete(hdev, sent->le, status); | ||
1141 | |||
1137 | if (status) | 1142 | if (status) |
1138 | return; | 1143 | return; |
1139 | 1144 | ||
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ac8ba839a78b..8bc6a7a48732 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -407,7 +407,7 @@ static u32 get_current_settings(struct hci_dev *hdev) | |||
407 | if (!(hdev->features[4] & LMP_NO_BREDR)) | 407 | if (!(hdev->features[4] & LMP_NO_BREDR)) |
408 | settings |= MGMT_SETTING_BREDR; | 408 | settings |= MGMT_SETTING_BREDR; |
409 | 409 | ||
410 | if (hdev->host_features[0] & LMP_HOST_LE) | 410 | if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) |
411 | settings |= MGMT_SETTING_LE; | 411 | settings |= MGMT_SETTING_LE; |
412 | 412 | ||
413 | if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) | 413 | if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) |
@@ -1231,6 +1231,82 @@ failed: | |||
1231 | return err; | 1231 | return err; |
1232 | } | 1232 | } |
1233 | 1233 | ||
1234 | static int set_le(struct sock *sk, u16 index, void *data, u16 len) | ||
1235 | { | ||
1236 | struct mgmt_mode *cp = data; | ||
1237 | struct hci_cp_write_le_host_supported hci_cp; | ||
1238 | struct pending_cmd *cmd; | ||
1239 | struct hci_dev *hdev; | ||
1240 | int err; | ||
1241 | u8 val; | ||
1242 | |||
1243 | BT_DBG("request for hci%u", index); | ||
1244 | |||
1245 | if (len != sizeof(*cp)) | ||
1246 | return cmd_status(sk, index, MGMT_OP_SET_LE, | ||
1247 | MGMT_STATUS_INVALID_PARAMS); | ||
1248 | |||
1249 | hdev = hci_dev_get(index); | ||
1250 | if (!hdev) | ||
1251 | return cmd_status(sk, index, MGMT_OP_SET_LE, | ||
1252 | MGMT_STATUS_INVALID_PARAMS); | ||
1253 | |||
1254 | if (!enable_le || !(hdev->features[4] & LMP_LE)) { | ||
1255 | err = cmd_status(sk, index, MGMT_OP_SET_LE, | ||
1256 | MGMT_STATUS_NOT_SUPPORTED); | ||
1257 | goto failed; | ||
1258 | } | ||
1259 | |||
1260 | val = !!cp->val; | ||
1261 | |||
1262 | if (!hdev_is_powered(hdev)) { | ||
1263 | bool changed = false; | ||
1264 | |||
1265 | if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { | ||
1266 | change_bit(HCI_LE_ENABLED, &hdev->dev_flags); | ||
1267 | changed = true; | ||
1268 | } | ||
1269 | |||
1270 | err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); | ||
1271 | if (err < 0) | ||
1272 | goto failed; | ||
1273 | |||
1274 | if (changed) | ||
1275 | err = new_settings(hdev, sk); | ||
1276 | |||
1277 | goto failed; | ||
1278 | } | ||
1279 | |||
1280 | if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { | ||
1281 | err = cmd_status(sk, index, MGMT_OP_SET_LE, MGMT_STATUS_BUSY); | ||
1282 | goto failed; | ||
1283 | } | ||
1284 | |||
1285 | cmd = mgmt_pending_add(sk, MGMT_OP_SET_LE, hdev, data, len); | ||
1286 | if (!cmd) { | ||
1287 | err = -ENOMEM; | ||
1288 | goto failed; | ||
1289 | } | ||
1290 | |||
1291 | memset(&hci_cp, 0, sizeof(hci_cp)); | ||
1292 | |||
1293 | if (val) { | ||
1294 | hci_cp.le = val; | ||
1295 | hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); | ||
1296 | } | ||
1297 | |||
1298 | err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, | ||
1299 | sizeof(hci_cp), &hci_cp); | ||
1300 | if (err < 0) { | ||
1301 | mgmt_pending_remove(cmd); | ||
1302 | goto failed; | ||
1303 | } | ||
1304 | |||
1305 | failed: | ||
1306 | hci_dev_put(hdev); | ||
1307 | return err; | ||
1308 | } | ||
1309 | |||
1234 | static int add_uuid(struct sock *sk, u16 index, void *data, u16 len) | 1310 | static int add_uuid(struct sock *sk, u16 index, void *data, u16 len) |
1235 | { | 1311 | { |
1236 | struct mgmt_cp_add_uuid *cp = data; | 1312 | struct mgmt_cp_add_uuid *cp = data; |
@@ -2816,6 +2892,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | |||
2816 | case MGMT_OP_SET_HS: | 2892 | case MGMT_OP_SET_HS: |
2817 | err = set_hs(sk, index, cp, len); | 2893 | err = set_hs(sk, index, cp, len); |
2818 | break; | 2894 | break; |
2895 | case MGMT_OP_SET_LE: | ||
2896 | err = set_le(sk, index, cp, len); | ||
2897 | break; | ||
2819 | case MGMT_OP_ADD_UUID: | 2898 | case MGMT_OP_ADD_UUID: |
2820 | err = add_uuid(sk, index, cp, len); | 2899 | err = add_uuid(sk, index, cp, len); |
2821 | break; | 2900 | break; |
@@ -3521,6 +3600,44 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, | |||
3521 | return err; | 3600 | return err; |
3522 | } | 3601 | } |
3523 | 3602 | ||
3603 | int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) | ||
3604 | { | ||
3605 | struct cmd_lookup match = { NULL, hdev }; | ||
3606 | bool changed = false; | ||
3607 | int err = 0; | ||
3608 | |||
3609 | if (status) { | ||
3610 | u8 mgmt_err = mgmt_status(status); | ||
3611 | |||
3612 | if (enable && test_and_clear_bit(HCI_LE_ENABLED, | ||
3613 | &hdev->dev_flags)) | ||
3614 | err = new_settings(hdev, NULL); | ||
3615 | |||
3616 | mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, | ||
3617 | cmd_status_rsp, &mgmt_err); | ||
3618 | |||
3619 | return err; | ||
3620 | } | ||
3621 | |||
3622 | if (enable) { | ||
3623 | if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags)) | ||
3624 | changed = true; | ||
3625 | } else { | ||
3626 | if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags)) | ||
3627 | changed = true; | ||
3628 | } | ||
3629 | |||
3630 | mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); | ||
3631 | |||
3632 | if (changed) | ||
3633 | err = new_settings(hdev, match.sk); | ||
3634 | |||
3635 | if (match.sk) | ||
3636 | sock_put(match.sk); | ||
3637 | |||
3638 | return err; | ||
3639 | } | ||
3640 | |||
3524 | int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, | 3641 | int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, |
3525 | u8 addr_type, u8 *dev_class, s8 rssi, | 3642 | u8 addr_type, u8 *dev_class, s8 rssi, |
3526 | u8 cfm_name, u8 *eir, u16 eir_len) | 3643 | u8 cfm_name, u8 *eir, u16 eir_len) |