aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/mgmt.c
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2013-09-25 06:26:10 -0400
committerGustavo Padovan <gustavo.padovan@collabora.co.uk>2013-09-25 13:30:11 -0400
commit4375f1037d52602413142e290608d0d84671ad36 (patch)
tree14fd894110e43e206b83b3146750782aa1677194 /net/bluetooth/mgmt.c
parenteeca6f891305a80378da978f803821c2a0b648b6 (diff)
Bluetooth: Add new mgmt_set_advertising command
This patch adds a new mgmt command for enabling and disabling LE advertising. The command depends on the LE setting being enabled first and will return a "rejected" response otherwise. The patch also adds safeguards so that there will ever only be one set_le or set_advertising command pending per adapter. The response handling and new_settings event sending is done in an asynchronous request callback, meaning raw HCI access from user space to enable advertising (e.g. hciconfig leadv) will not trigger the new_settings event. This is intentional since trying to support mixed raw HCI and mgmt access would mean adding extra state tracking or new helper functions, essentially negating the benefit of using the asynchronous request framework. The HCI_LE_ENABLED and HCI_LE_PERIPHERAL flags however are updated correctly even with raw HCI access so this will not completely break subsequent access over mgmt. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Acked-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r--net/bluetooth/mgmt.c97
1 files changed, 96 insertions, 1 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 9a2faa310b7c..1b5b10fab545 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -76,6 +76,7 @@ static const u16 mgmt_commands[] = {
76 MGMT_OP_BLOCK_DEVICE, 76 MGMT_OP_BLOCK_DEVICE,
77 MGMT_OP_UNBLOCK_DEVICE, 77 MGMT_OP_UNBLOCK_DEVICE,
78 MGMT_OP_SET_DEVICE_ID, 78 MGMT_OP_SET_DEVICE_ID,
79 MGMT_OP_SET_ADVERTISING,
79}; 80};
80 81
81static const u16 mgmt_events[] = { 82static const u16 mgmt_events[] = {
@@ -1431,7 +1432,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
1431 goto unlock; 1432 goto unlock;
1432 } 1433 }
1433 1434
1434 if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { 1435 if (mgmt_pending_find(MGMT_OP_SET_LE, hdev) ||
1436 mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev)) {
1435 err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, 1437 err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
1436 MGMT_STATUS_BUSY); 1438 MGMT_STATUS_BUSY);
1437 goto unlock; 1439 goto unlock;
@@ -3136,6 +3138,98 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
3136 return err; 3138 return err;
3137} 3139}
3138 3140
3141static void set_advertising_complete(struct hci_dev *hdev, u8 status)
3142{
3143 struct cmd_lookup match = { NULL, hdev };
3144
3145 if (status) {
3146 u8 mgmt_err = mgmt_status(status);
3147
3148 mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev,
3149 cmd_status_rsp, &mgmt_err);
3150 return;
3151 }
3152
3153 mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp,
3154 &match);
3155
3156 new_settings(hdev, match.sk);
3157
3158 if (match.sk)
3159 sock_put(match.sk);
3160}
3161
3162static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
3163{
3164 struct mgmt_mode *cp = data;
3165 struct pending_cmd *cmd;
3166 struct hci_request req;
3167 u8 val, enabled;
3168 int err;
3169
3170 BT_DBG("request for %s", hdev->name);
3171
3172 if (!lmp_le_capable(hdev))
3173 return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
3174 MGMT_STATUS_NOT_SUPPORTED);
3175
3176 if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
3177 return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
3178 MGMT_STATUS_REJECTED);
3179
3180 if (cp->val != 0x00 && cp->val != 0x01)
3181 return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
3182 MGMT_STATUS_INVALID_PARAMS);
3183
3184 hci_dev_lock(hdev);
3185
3186 val = !!cp->val;
3187 enabled = test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
3188
3189 if (!hdev_is_powered(hdev) || val == enabled) {
3190 bool changed = false;
3191
3192 if (val != test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
3193 change_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
3194 changed = true;
3195 }
3196
3197 err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev);
3198 if (err < 0)
3199 goto unlock;
3200
3201 if (changed)
3202 err = new_settings(hdev, sk);
3203
3204 goto unlock;
3205 }
3206
3207 if (mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev) ||
3208 mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
3209 err = cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
3210 MGMT_STATUS_BUSY);
3211 goto unlock;
3212 }
3213
3214 cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING, hdev, data, len);
3215 if (!cmd) {
3216 err = -ENOMEM;
3217 goto unlock;
3218 }
3219
3220 hci_req_init(&req, hdev);
3221
3222 hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(val), &val);
3223
3224 err = hci_req_run(&req, set_advertising_complete);
3225 if (err < 0)
3226 mgmt_pending_remove(cmd);
3227
3228unlock:
3229 hci_dev_unlock(hdev);
3230 return err;
3231}
3232
3139static void fast_connectable_complete(struct hci_dev *hdev, u8 status) 3233static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
3140{ 3234{
3141 struct pending_cmd *cmd; 3235 struct pending_cmd *cmd;
@@ -3347,6 +3441,7 @@ static const struct mgmt_handler {
3347 { block_device, false, MGMT_BLOCK_DEVICE_SIZE }, 3441 { block_device, false, MGMT_BLOCK_DEVICE_SIZE },
3348 { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, 3442 { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE },
3349 { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE }, 3443 { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
3444 { set_advertising, false, MGMT_SETTING_SIZE },
3350}; 3445};
3351 3446
3352 3447