diff options
-rw-r--r-- | include/net/bluetooth/mgmt.h | 2 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 97 |
2 files changed, 98 insertions, 1 deletions
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 6cc72b69e014..421d7633a91f 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h | |||
@@ -352,6 +352,8 @@ struct mgmt_cp_set_device_id { | |||
352 | } __packed; | 352 | } __packed; |
353 | #define MGMT_SET_DEVICE_ID_SIZE 8 | 353 | #define MGMT_SET_DEVICE_ID_SIZE 8 |
354 | 354 | ||
355 | #define MGMT_OP_SET_ADVERTISING 0x0029 | ||
356 | |||
355 | #define MGMT_EV_CMD_COMPLETE 0x0001 | 357 | #define MGMT_EV_CMD_COMPLETE 0x0001 |
356 | struct mgmt_ev_cmd_complete { | 358 | struct mgmt_ev_cmd_complete { |
357 | __le16 opcode; | 359 | __le16 opcode; |
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 | ||
81 | static const u16 mgmt_events[] = { | 82 | static 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 | ||
3141 | static 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 | |||
3162 | static 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 | |||
3228 | unlock: | ||
3229 | hci_dev_unlock(hdev); | ||
3230 | return err; | ||
3231 | } | ||
3232 | |||
3139 | static void fast_connectable_complete(struct hci_dev *hdev, u8 status) | 3233 | static 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 | ||