Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Bluetooth: Add framework for Extended Controller Information

This command is used to retrieve the current state and basic
information of a controller. It is typically used right after
getting the response to the Read Controller Index List command
or an Index Added event (or its extended counterparts).

When any of the values in the EIR_Data field changes, the event
Extended Controller Information Changed will be used to inform
clients about the updated information.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Michał Narajowski <michal.narajowski@codecoup.pl>

+79 -2
+1
include/net/bluetooth/hci.h
··· 208 208 HCI_MGMT_INDEX_EVENTS, 209 209 HCI_MGMT_UNCONF_INDEX_EVENTS, 210 210 HCI_MGMT_EXT_INDEX_EVENTS, 211 + HCI_MGMT_EXT_INFO_EVENTS, 211 212 HCI_MGMT_OPTION_EVENTS, 212 213 HCI_MGMT_SETTING_EVENTS, 213 214 HCI_MGMT_DEV_CLASS_EVENTS,
+18
include/net/bluetooth/mgmt.h
··· 586 586 587 587 #define MGMT_OP_START_LIMITED_DISCOVERY 0x0041 588 588 589 + #define MGMT_OP_READ_EXT_INFO 0x0042 590 + #define MGMT_READ_EXT_INFO_SIZE 0 591 + struct mgmt_rp_read_ext_info { 592 + bdaddr_t bdaddr; 593 + __u8 version; 594 + __le16 manufacturer; 595 + __le32 supported_settings; 596 + __le32 current_settings; 597 + __le16 eir_len; 598 + __u8 eir[0]; 599 + } __packed; 600 + 589 601 #define MGMT_EV_CMD_COMPLETE 0x0001 590 602 struct mgmt_ev_cmd_complete { 591 603 __le16 opcode; ··· 811 799 #define MGMT_EV_ADVERTISING_REMOVED 0x0024 812 800 struct mgmt_ev_advertising_removed { 813 801 __u8 instance; 802 + } __packed; 803 + 804 + #define MGMT_EV_EXT_INFO_CHANGED 0x0025 805 + struct mgmt_ev_ext_info_changed { 806 + __le16 eir_len; 807 + __u8 eir[0]; 814 808 } __packed;
+60 -2
net/bluetooth/mgmt.c
··· 104 104 MGMT_OP_REMOVE_ADVERTISING, 105 105 MGMT_OP_GET_ADV_SIZE_INFO, 106 106 MGMT_OP_START_LIMITED_DISCOVERY, 107 + MGMT_OP_READ_EXT_INFO, 107 108 }; 108 109 109 110 static const u16 mgmt_events[] = { ··· 142 141 MGMT_EV_LOCAL_OOB_DATA_UPDATED, 143 142 MGMT_EV_ADVERTISING_ADDED, 144 143 MGMT_EV_ADVERTISING_REMOVED, 144 + MGMT_EV_EXT_INFO_CHANGED, 145 145 }; 146 146 147 147 static const u16 mgmt_untrusted_commands[] = { ··· 151 149 MGMT_OP_READ_UNCONF_INDEX_LIST, 152 150 MGMT_OP_READ_CONFIG_INFO, 153 151 MGMT_OP_READ_EXT_INDEX_LIST, 152 + MGMT_OP_READ_EXT_INFO, 154 153 }; 155 154 156 155 static const u16 mgmt_untrusted_events[] = { ··· 165 162 MGMT_EV_NEW_CONFIG_OPTIONS, 166 163 MGMT_EV_EXT_INDEX_ADDED, 167 164 MGMT_EV_EXT_INDEX_REMOVED, 165 + MGMT_EV_EXT_INFO_CHANGED, 168 166 }; 169 167 170 168 #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) ··· 864 860 865 861 return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp, 866 862 sizeof(rp)); 863 + } 864 + 865 + static int read_ext_controller_info(struct sock *sk, struct hci_dev *hdev, 866 + void *data, u16 data_len) 867 + { 868 + struct mgmt_rp_read_ext_info rp; 869 + 870 + BT_DBG("sock %p %s", sk, hdev->name); 871 + 872 + hci_dev_lock(hdev); 873 + 874 + memset(&rp, 0, sizeof(rp)); 875 + 876 + bacpy(&rp.bdaddr, &hdev->bdaddr); 877 + 878 + rp.version = hdev->hci_ver; 879 + rp.manufacturer = cpu_to_le16(hdev->manufacturer); 880 + 881 + rp.supported_settings = cpu_to_le32(get_supported_settings(hdev)); 882 + rp.current_settings = cpu_to_le32(get_current_settings(hdev)); 883 + 884 + rp.eir_len = cpu_to_le16(0); 885 + 886 + hci_dev_unlock(hdev); 887 + 888 + /* If this command is called at least once, then the events 889 + * for class of device and local name changes are disabled 890 + * and only the new extended controller information event 891 + * is used. 892 + */ 893 + hci_sock_set_flag(sk, HCI_MGMT_EXT_INFO_EVENTS); 894 + hci_sock_clear_flag(sk, HCI_MGMT_DEV_CLASS_EVENTS); 895 + hci_sock_clear_flag(sk, HCI_MGMT_LOCAL_NAME_EVENTS); 896 + 897 + return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_EXT_INFO, 0, &rp, 898 + sizeof(rp)); 899 + } 900 + 901 + static int ext_info_changed(struct hci_dev *hdev, struct sock *skip) 902 + { 903 + struct mgmt_ev_ext_info_changed ev; 904 + 905 + ev.eir_len = cpu_to_le16(0); 906 + 907 + return mgmt_limited_event(MGMT_EV_EXT_INFO_CHANGED, hdev, &ev, 908 + sizeof(ev), HCI_MGMT_EXT_INFO_EVENTS, skip); 867 909 } 868 910 869 911 static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) ··· 3045 2995 3046 2996 err = mgmt_limited_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, 3047 2997 len, HCI_MGMT_LOCAL_NAME_EVENTS, sk); 2998 + ext_info_changed(hdev, sk); 3048 2999 3049 3000 goto failed; 3050 3001 } ··· 6407 6356 { remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE }, 6408 6357 { get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE }, 6409 6358 { start_limited_discovery, MGMT_START_DISCOVERY_SIZE }, 6359 + { read_ext_controller_info,MGMT_READ_EXT_INFO_SIZE, 6360 + HCI_MGMT_UNTRUSTED }, 6410 6361 }; 6411 6362 6412 6363 void mgmt_index_added(struct hci_dev *hdev) ··· 6547 6494 6548 6495 mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); 6549 6496 6550 - if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) 6497 + if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) { 6551 6498 mgmt_limited_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, 6552 6499 zero_cod, sizeof(zero_cod), 6553 6500 HCI_MGMT_DEV_CLASS_EVENTS, NULL); 6501 + ext_info_changed(hdev, NULL); 6502 + } 6554 6503 6555 6504 new_settings(hdev, match.sk); 6556 6505 ··· 7148 7093 mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match); 7149 7094 mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match); 7150 7095 7151 - if (!status) 7096 + if (!status) { 7152 7097 mgmt_limited_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 7153 7098 3, HCI_MGMT_DEV_CLASS_EVENTS, NULL); 7099 + ext_info_changed(hdev, NULL); 7100 + } 7154 7101 7155 7102 if (match.sk) 7156 7103 sock_put(match.sk); ··· 7183 7126 7184 7127 mgmt_limited_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), 7185 7128 HCI_MGMT_LOCAL_NAME_EVENTS, cmd ? cmd->sk : NULL); 7129 + ext_info_changed(hdev, cmd ? cmd->sk : NULL); 7186 7130 } 7187 7131 7188 7132 static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])