forked from mirrors/linux
		
	Bluetooth: Add initial implementation of BIS connections
This adds initial support for BIS/BIG which includes:
== Broadcaster role: Setup a periodic advertising and create a BIG ==
> tools/isotest -s 00:00:00:00:00:00
isotest[63]: Connected [00:00:00:00:00:00]
isotest[63]: QoS BIG 0x00 BIS 0x00 Packing 0x00 Framing 0x00]
isotest[63]: Output QoS [Interval 10000 us Latency 10 ms SDU 40 PHY 0x02
RTN 2]
isotest[63]: Sending ...
isotest[63]: Number of packets: 1
isotest[63]: Socket jitter buffer: 80 buffer
< HCI Command: LE Set Perio.. (0x08|0x003e) plen 7
...
> HCI Event: Command Complete (0x0e) plen 4
      LE Set Periodic Advertising Parameters (0x08|0x003e) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Perio.. (0x08|0x003f) plen 7
...
> HCI Event: Command Complete (0x0e) plen 4
      LE Set Periodic Advertising Data (0x08|0x003f) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Perio.. (0x08|0x0040) plen 2
...
> HCI Event: Command Complete (0x0e) plen 4
      LE Set Periodic Advertising Enable (0x08|0x0040) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Create B.. (0x08|0x0068) plen 31
...
> HCI Event: Command Status (0x0f) plen 4
      LE Create Broadcast Isochronous Group (0x08|0x0068) ncmd 1
        Status: Success (0x00)
> HCI Event: LE Meta Event (0x3e) plen 21
      LE Broadcast Isochronous Group Complete (0x1b)
      ...
== Broadcast Receiver role: Create a PA Sync and BIG Sync ==
> tools/isotest -i hci1 -d 00:AA:01:00:00:00
isotest[66]: Waiting for connection 00:AA:01:00:00:00...
< HCI Command: LE Periodic Advert.. (0x08|0x0044) plen 14
...
> HCI Event: Command Status (0x0f) plen 4
      LE Periodic Advertising Create Sync (0x08|0x0044) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Extended Sca.. (0x08|0x0041) plen 8
...
> HCI Event: Command Complete (0x0e) plen 4
      LE Set Extended Scan Parameters (0x08|0x0041) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Extended Sca.. (0x08|0x0042) plen 6
...
> HCI Event: Command Complete (0x0e) plen 4
      LE Set Extended Scan Enable (0x08|0x0042) ncmd 1
        Status: Success (0x00)
> HCI Event: LE Meta Event (0x3e) plen 29
      LE Extended Advertising Report (0x0d)
      ...
> HCI Event: LE Meta Event (0x3e) plen 16
      LE Periodic Advertising Sync Established (0x0e)
      ...
< HCI Command: LE Broadcast Isoch.. (0x08|0x006b) plen 25
...
> HCI Event: Command Status (0x0f) plen 4
      LE Broadcast Isochronous Group Create Sync (0x08|0x006b) ncmd 1
        Status: Success (0x00)
> HCI Event: LE Meta Event (0x3e) plen 17
      LE Broadcast Isochronous Group Sync Estabilished (0x1d)
      ...
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
			
			
This commit is contained in:
		
							parent
							
								
									ccf74f2390
								
							
						
					
					
						commit
						eca0ae4aea
					
				
					 13 changed files with 1333 additions and 126 deletions
				
			
		|  | @ -160,6 +160,9 @@ struct bt_voice { | ||||||
| #define BT_ISO_QOS_CIG_UNSET	0xff | #define BT_ISO_QOS_CIG_UNSET	0xff | ||||||
| #define BT_ISO_QOS_CIS_UNSET	0xff | #define BT_ISO_QOS_CIS_UNSET	0xff | ||||||
| 
 | 
 | ||||||
|  | #define BT_ISO_QOS_BIG_UNSET	0xff | ||||||
|  | #define BT_ISO_QOS_BIS_UNSET	0xff | ||||||
|  | 
 | ||||||
| struct bt_iso_io_qos { | struct bt_iso_io_qos { | ||||||
| 	__u32 interval; | 	__u32 interval; | ||||||
| 	__u16 latency; | 	__u16 latency; | ||||||
|  | @ -169,9 +172,18 @@ struct bt_iso_io_qos { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct bt_iso_qos { | struct bt_iso_qos { | ||||||
| 	__u8  cig; | 	union { | ||||||
| 	__u8  cis; | 		__u8  cig; | ||||||
| 	__u8  sca; | 		__u8  big; | ||||||
|  | 	}; | ||||||
|  | 	union { | ||||||
|  | 		__u8  cis; | ||||||
|  | 		__u8  bis; | ||||||
|  | 	}; | ||||||
|  | 	union { | ||||||
|  | 		__u8  sca; | ||||||
|  | 		__u8  sync_interval; | ||||||
|  | 	}; | ||||||
| 	__u8  packing; | 	__u8  packing; | ||||||
| 	__u8  framing; | 	__u8  framing; | ||||||
| 	struct bt_iso_io_qos in; | 	struct bt_iso_io_qos in; | ||||||
|  |  | ||||||
|  | @ -316,6 +316,7 @@ enum { | ||||||
| 	HCI_USER_CHANNEL, | 	HCI_USER_CHANNEL, | ||||||
| 	HCI_EXT_CONFIGURED, | 	HCI_EXT_CONFIGURED, | ||||||
| 	HCI_LE_ADV, | 	HCI_LE_ADV, | ||||||
|  | 	HCI_LE_PER_ADV, | ||||||
| 	HCI_LE_SCAN, | 	HCI_LE_SCAN, | ||||||
| 	HCI_SSP_ENABLED, | 	HCI_SSP_ENABLED, | ||||||
| 	HCI_SC_ENABLED, | 	HCI_SC_ENABLED, | ||||||
|  | @ -338,6 +339,7 @@ enum { | ||||||
| 	HCI_LE_SCAN_INTERRUPTED, | 	HCI_LE_SCAN_INTERRUPTED, | ||||||
| 	HCI_WIDEBAND_SPEECH_ENABLED, | 	HCI_WIDEBAND_SPEECH_ENABLED, | ||||||
| 	HCI_EVENT_FILTER_CONFIGURED, | 	HCI_EVENT_FILTER_CONFIGURED, | ||||||
|  | 	HCI_PA_SYNC, | ||||||
| 
 | 
 | ||||||
| 	HCI_DUT_MODE, | 	HCI_DUT_MODE, | ||||||
| 	HCI_VENDOR_DIAG, | 	HCI_VENDOR_DIAG, | ||||||
|  | @ -519,9 +521,11 @@ enum { | ||||||
| #define HCI_LE_PHY_2M			0x01 | #define HCI_LE_PHY_2M			0x01 | ||||||
| #define HCI_LE_PHY_CODED		0x08 | #define HCI_LE_PHY_CODED		0x08 | ||||||
| #define HCI_LE_EXT_ADV			0x10 | #define HCI_LE_EXT_ADV			0x10 | ||||||
|  | #define HCI_LE_PERIODIC_ADV		0x20 | ||||||
| #define HCI_LE_CHAN_SEL_ALG2		0x40 | #define HCI_LE_CHAN_SEL_ALG2		0x40 | ||||||
| #define HCI_LE_CIS_CENTRAL		0x10 | #define HCI_LE_CIS_CENTRAL		0x10 | ||||||
| #define HCI_LE_CIS_PERIPHERAL		0x20 | #define HCI_LE_CIS_PERIPHERAL		0x20 | ||||||
|  | #define HCI_LE_ISO_BROADCASTER		0x40 | ||||||
| 
 | 
 | ||||||
| /* Connection modes */ | /* Connection modes */ | ||||||
| #define HCI_CM_ACTIVE	0x0000 | #define HCI_CM_ACTIVE	0x0000 | ||||||
|  | @ -1865,6 +1869,22 @@ struct hci_cp_le_ext_conn_param { | ||||||
| 	__le16 max_ce_len; | 	__le16 max_ce_len; | ||||||
| } __packed; | } __packed; | ||||||
| 
 | 
 | ||||||
|  | #define HCI_OP_LE_PA_CREATE_SYNC	0x2044 | ||||||
|  | struct hci_cp_le_pa_create_sync { | ||||||
|  | 	__u8      options; | ||||||
|  | 	__u8      sid; | ||||||
|  | 	__u8      addr_type; | ||||||
|  | 	bdaddr_t  addr; | ||||||
|  | 	__le16    skip; | ||||||
|  | 	__le16    sync_timeout; | ||||||
|  | 	__u8      sync_cte_type; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_OP_LE_PA_TERM_SYNC		0x2046 | ||||||
|  | struct hci_cp_le_pa_term_sync { | ||||||
|  | 	__le16    handle; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
| #define HCI_OP_LE_READ_NUM_SUPPORTED_ADV_SETS	0x203b | #define HCI_OP_LE_READ_NUM_SUPPORTED_ADV_SETS	0x203b | ||||||
| struct hci_rp_le_read_num_supported_adv_sets { | struct hci_rp_le_read_num_supported_adv_sets { | ||||||
| 	__u8  status; | 	__u8  status; | ||||||
|  | @ -1899,13 +1919,6 @@ struct hci_rp_le_set_ext_adv_params { | ||||||
| 	__u8  tx_power; | 	__u8  tx_power; | ||||||
| } __packed; | } __packed; | ||||||
| 
 | 
 | ||||||
| #define HCI_OP_LE_SET_EXT_ADV_ENABLE		0x2039 |  | ||||||
| struct hci_cp_le_set_ext_adv_enable { |  | ||||||
| 	__u8  enable; |  | ||||||
| 	__u8  num_of_sets; |  | ||||||
| 	__u8  data[]; |  | ||||||
| } __packed; |  | ||||||
| 
 |  | ||||||
| struct hci_cp_ext_adv_set { | struct hci_cp_ext_adv_set { | ||||||
| 	__u8  handle; | 	__u8  handle; | ||||||
| 	__le16 duration; | 	__le16 duration; | ||||||
|  | @ -1932,6 +1945,37 @@ struct hci_cp_le_set_ext_scan_rsp_data { | ||||||
| 	__u8  data[]; | 	__u8  data[]; | ||||||
| } __packed; | } __packed; | ||||||
| 
 | 
 | ||||||
|  | #define HCI_OP_LE_SET_EXT_ADV_ENABLE		0x2039 | ||||||
|  | struct hci_cp_le_set_ext_adv_enable { | ||||||
|  | 	__u8  enable; | ||||||
|  | 	__u8  num_of_sets; | ||||||
|  | 	__u8  data[]; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_OP_LE_SET_PER_ADV_PARAMS		0x203e | ||||||
|  | struct hci_cp_le_set_per_adv_params { | ||||||
|  | 	__u8      handle; | ||||||
|  | 	__le16    min_interval; | ||||||
|  | 	__le16    max_interval; | ||||||
|  | 	__le16    periodic_properties; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_MAX_PER_AD_LENGTH	252 | ||||||
|  | 
 | ||||||
|  | #define HCI_OP_LE_SET_PER_ADV_DATA		0x203f | ||||||
|  | struct hci_cp_le_set_per_adv_data { | ||||||
|  | 	__u8  handle; | ||||||
|  | 	__u8  operation; | ||||||
|  | 	__u8  length; | ||||||
|  | 	__u8  data[]; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_OP_LE_SET_PER_ADV_ENABLE		0x2040 | ||||||
|  | struct hci_cp_le_set_per_adv_enable { | ||||||
|  | 	__u8  enable; | ||||||
|  | 	__u8  handle; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
| #define LE_SET_ADV_DATA_OP_COMPLETE	0x03 | #define LE_SET_ADV_DATA_OP_COMPLETE	0x03 | ||||||
| 
 | 
 | ||||||
| #define LE_SET_ADV_DATA_NO_FRAG		0x01 | #define LE_SET_ADV_DATA_NO_FRAG		0x01 | ||||||
|  | @ -2043,6 +2087,49 @@ struct hci_cp_le_reject_cis { | ||||||
| 	__u8    reason; | 	__u8    reason; | ||||||
| } __packed; | } __packed; | ||||||
| 
 | 
 | ||||||
|  | #define HCI_OP_LE_CREATE_BIG			0x2068 | ||||||
|  | struct hci_bis { | ||||||
|  | 	__u8    sdu_interval[3]; | ||||||
|  | 	__le16  sdu; | ||||||
|  | 	__le16  latency; | ||||||
|  | 	__u8    rtn; | ||||||
|  | 	__u8    phy; | ||||||
|  | 	__u8    packing; | ||||||
|  | 	__u8    framing; | ||||||
|  | 	__u8    encryption; | ||||||
|  | 	__u8    bcode[16]; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct hci_cp_le_create_big { | ||||||
|  | 	__u8    handle; | ||||||
|  | 	__u8    adv_handle; | ||||||
|  | 	__u8    num_bis; | ||||||
|  | 	struct hci_bis bis; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_OP_LE_TERM_BIG			0x206a | ||||||
|  | struct hci_cp_le_term_big { | ||||||
|  | 	__u8    handle; | ||||||
|  | 	__u8    reason; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_OP_LE_BIG_CREATE_SYNC		0x206b | ||||||
|  | struct hci_cp_le_big_create_sync { | ||||||
|  | 	__u8    handle; | ||||||
|  | 	__le16  sync_handle; | ||||||
|  | 	__u8    encryption; | ||||||
|  | 	__u8    bcode[16]; | ||||||
|  | 	__u8    mse; | ||||||
|  | 	__le16  timeout; | ||||||
|  | 	__u8    num_bis; | ||||||
|  | 	__u8    bis[0]; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_OP_LE_BIG_TERM_SYNC			0x206c | ||||||
|  | struct hci_cp_le_big_term_sync { | ||||||
|  | 	__u8    handle; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
| #define HCI_OP_LE_SETUP_ISO_PATH		0x206e | #define HCI_OP_LE_SETUP_ISO_PATH		0x206e | ||||||
| struct hci_cp_le_setup_iso_path { | struct hci_cp_le_setup_iso_path { | ||||||
| 	__le16  handle; | 	__le16  handle; | ||||||
|  | @ -2595,6 +2682,18 @@ struct hci_ev_le_ext_adv_report { | ||||||
| 	struct hci_ev_le_ext_adv_info info[]; | 	struct hci_ev_le_ext_adv_info info[]; | ||||||
| } __packed; | } __packed; | ||||||
| 
 | 
 | ||||||
|  | #define HCI_EV_LE_PA_SYNC_ESTABLISHED	0x0e | ||||||
|  | struct hci_ev_le_pa_sync_established { | ||||||
|  | 	__u8      status; | ||||||
|  | 	__le16    handle; | ||||||
|  | 	__u8      sid; | ||||||
|  | 	__u8      bdaddr_type; | ||||||
|  | 	bdaddr_t  bdaddr; | ||||||
|  | 	__u8      phy; | ||||||
|  | 	__le16    interval; | ||||||
|  | 	__u8      clock_accuracy; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
| #define HCI_EV_LE_ENHANCED_CONN_COMPLETE    0x0a | #define HCI_EV_LE_ENHANCED_CONN_COMPLETE    0x0a | ||||||
| struct hci_ev_le_enh_conn_complete { | struct hci_ev_le_enh_conn_complete { | ||||||
| 	__u8      status; | 	__u8      status; | ||||||
|  | @ -2646,6 +2745,55 @@ struct hci_evt_le_cis_req { | ||||||
| 	__u8  cis_id; | 	__u8  cis_id; | ||||||
| } __packed; | } __packed; | ||||||
| 
 | 
 | ||||||
|  | #define HCI_EVT_LE_CREATE_BIG_COMPLETE	0x1b | ||||||
|  | struct hci_evt_le_create_big_complete { | ||||||
|  | 	__u8    status; | ||||||
|  | 	__u8    handle; | ||||||
|  | 	__u8    sync_delay[3]; | ||||||
|  | 	__u8    transport_delay[3]; | ||||||
|  | 	__u8    phy; | ||||||
|  | 	__u8    nse; | ||||||
|  | 	__u8    bn; | ||||||
|  | 	__u8    pto; | ||||||
|  | 	__u8    irc; | ||||||
|  | 	__le16  max_pdu; | ||||||
|  | 	__le16  interval; | ||||||
|  | 	__u8    num_bis; | ||||||
|  | 	__le16  bis_handle[]; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_EVT_LE_BIG_SYNC_ESTABILISHED 0x1d | ||||||
|  | struct hci_evt_le_big_sync_estabilished { | ||||||
|  | 	__u8    status; | ||||||
|  | 	__u8    handle; | ||||||
|  | 	__u8    latency[3]; | ||||||
|  | 	__u8    nse; | ||||||
|  | 	__u8    bn; | ||||||
|  | 	__u8    pto; | ||||||
|  | 	__u8    irc; | ||||||
|  | 	__le16  max_pdu; | ||||||
|  | 	__le16  interval; | ||||||
|  | 	__u8    num_bis; | ||||||
|  | 	__le16  bis[]; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define HCI_EVT_LE_BIG_INFO_ADV_REPORT	0x22 | ||||||
|  | struct hci_evt_le_big_info_adv_report { | ||||||
|  | 	__le16  sync_handle; | ||||||
|  | 	__u8    num_bis; | ||||||
|  | 	__u8    nse; | ||||||
|  | 	__le16  iso_interval; | ||||||
|  | 	__u8    bn; | ||||||
|  | 	__u8    pto; | ||||||
|  | 	__u8    irc; | ||||||
|  | 	__le16  max_pdu; | ||||||
|  | 	__u8    sdu_interval[3]; | ||||||
|  | 	__le16  max_sdu; | ||||||
|  | 	__u8    phy; | ||||||
|  | 	__u8    framing; | ||||||
|  | 	__u8    encryption; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
| #define HCI_EV_VENDOR			0xff | #define HCI_EV_VENDOR			0xff | ||||||
| 
 | 
 | ||||||
| /* Internal events generated by Bluetooth stack */ | /* Internal events generated by Bluetooth stack */ | ||||||
|  |  | ||||||
|  | @ -235,8 +235,9 @@ struct oob_data { | ||||||
| 
 | 
 | ||||||
| struct adv_info { | struct adv_info { | ||||||
| 	struct list_head list; | 	struct list_head list; | ||||||
| 	bool enabled; | 	bool	enabled; | ||||||
| 	bool pending; | 	bool	pending; | ||||||
|  | 	bool	periodic; | ||||||
| 	__u8	instance; | 	__u8	instance; | ||||||
| 	__u32	flags; | 	__u32	flags; | ||||||
| 	__u16	timeout; | 	__u16	timeout; | ||||||
|  | @ -248,6 +249,8 @@ struct adv_info { | ||||||
| 	__u16	scan_rsp_len; | 	__u16	scan_rsp_len; | ||||||
| 	__u8	scan_rsp_data[HCI_MAX_EXT_AD_LENGTH]; | 	__u8	scan_rsp_data[HCI_MAX_EXT_AD_LENGTH]; | ||||||
| 	bool	scan_rsp_changed; | 	bool	scan_rsp_changed; | ||||||
|  | 	__u16	per_adv_data_len; | ||||||
|  | 	__u8	per_adv_data[HCI_MAX_PER_AD_LENGTH]; | ||||||
| 	__s8	tx_power; | 	__s8	tx_power; | ||||||
| 	__u32   min_interval; | 	__u32   min_interval; | ||||||
| 	__u32   max_interval; | 	__u32   max_interval; | ||||||
|  | @ -594,6 +597,8 @@ struct hci_dev { | ||||||
| 	__u8			adv_data_len; | 	__u8			adv_data_len; | ||||||
| 	__u8			scan_rsp_data[HCI_MAX_EXT_AD_LENGTH]; | 	__u8			scan_rsp_data[HCI_MAX_EXT_AD_LENGTH]; | ||||||
| 	__u8			scan_rsp_data_len; | 	__u8			scan_rsp_data_len; | ||||||
|  | 	__u8			per_adv_data[HCI_MAX_PER_AD_LENGTH]; | ||||||
|  | 	__u8			per_adv_data_len; | ||||||
| 
 | 
 | ||||||
| 	struct list_head	adv_instances; | 	struct list_head	adv_instances; | ||||||
| 	unsigned int		adv_instance_cnt; | 	unsigned int		adv_instance_cnt; | ||||||
|  | @ -679,6 +684,7 @@ struct hci_conn { | ||||||
| 	__u8		resp_addr_type; | 	__u8		resp_addr_type; | ||||||
| 	__u8		adv_instance; | 	__u8		adv_instance; | ||||||
| 	__u16		handle; | 	__u16		handle; | ||||||
|  | 	__u16		sync_handle; | ||||||
| 	__u16		state; | 	__u16		state; | ||||||
| 	__u8		mode; | 	__u8		mode; | ||||||
| 	__u8		type; | 	__u8		type; | ||||||
|  | @ -709,6 +715,8 @@ struct hci_conn { | ||||||
| 	__u16		le_supv_timeout; | 	__u16		le_supv_timeout; | ||||||
| 	__u8		le_adv_data[HCI_MAX_AD_LENGTH]; | 	__u8		le_adv_data[HCI_MAX_AD_LENGTH]; | ||||||
| 	__u8		le_adv_data_len; | 	__u8		le_adv_data_len; | ||||||
|  | 	__u8		le_per_adv_data[HCI_MAX_PER_AD_LENGTH]; | ||||||
|  | 	__u8		le_per_adv_data_len; | ||||||
| 	__u8		le_tx_phy; | 	__u8		le_tx_phy; | ||||||
| 	__u8		le_rx_phy; | 	__u8		le_rx_phy; | ||||||
| 	__s8		rssi; | 	__s8		rssi; | ||||||
|  | @ -942,6 +950,7 @@ enum { | ||||||
| 	HCI_CONN_NEW_LINK_KEY, | 	HCI_CONN_NEW_LINK_KEY, | ||||||
| 	HCI_CONN_SCANNING, | 	HCI_CONN_SCANNING, | ||||||
| 	HCI_CONN_AUTH_FAILURE, | 	HCI_CONN_AUTH_FAILURE, | ||||||
|  | 	HCI_CONN_PER_ADV, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) | static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) | ||||||
|  | @ -1060,6 +1069,29 @@ static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle) | ||||||
| 	return type; | 	return type; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev, | ||||||
|  | 							bdaddr_t *ba, | ||||||
|  | 							__u8 big, __u8 bis) | ||||||
|  | { | ||||||
|  | 	struct hci_conn_hash *h = &hdev->conn_hash; | ||||||
|  | 	struct hci_conn  *c; | ||||||
|  | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry_rcu(c, &h->list, list) { | ||||||
|  | 		if (bacmp(&c->dst, ba) || c->type != ISO_LINK) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		if (c->iso_qos.big == big && c->iso_qos.bis == bis) { | ||||||
|  | 			rcu_read_unlock(); | ||||||
|  | 			return c; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	rcu_read_unlock(); | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, | static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, | ||||||
| 								__u16 handle) | 								__u16 handle) | ||||||
| { | { | ||||||
|  | @ -1170,6 +1202,29 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev, | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev, | ||||||
|  | 							__u8 handle) | ||||||
|  | { | ||||||
|  | 	struct hci_conn_hash *h = &hdev->conn_hash; | ||||||
|  | 	struct hci_conn  *c; | ||||||
|  | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry_rcu(c, &h->list, list) { | ||||||
|  | 		if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		if (handle == c->iso_qos.big) { | ||||||
|  | 			rcu_read_unlock(); | ||||||
|  | 			return c; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rcu_read_unlock(); | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, | static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, | ||||||
| 							__u8 type, __u16 state) | 							__u8 type, __u16 state) | ||||||
| { | { | ||||||
|  | @ -1264,6 +1319,13 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, | ||||||
| 			      __u8 dst_type, struct bt_iso_qos *qos); | 			      __u8 dst_type, struct bt_iso_qos *qos); | ||||||
| struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, | struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, | ||||||
| 				 __u8 dst_type, struct bt_iso_qos *qos); | 				 __u8 dst_type, struct bt_iso_qos *qos); | ||||||
|  | struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, | ||||||
|  | 				 __u8 dst_type, struct bt_iso_qos *qos, | ||||||
|  | 				 __u8 data_len, __u8 *data); | ||||||
|  | int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, | ||||||
|  | 		       __u8 sid); | ||||||
|  | int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos, | ||||||
|  | 			   __u16 sync_handle, __u8 num_bis, __u8 bis[]); | ||||||
| int hci_conn_check_link_mode(struct hci_conn *conn); | int hci_conn_check_link_mode(struct hci_conn *conn); | ||||||
| int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); | int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); | ||||||
| int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type, | int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type, | ||||||
|  | @ -1510,11 +1572,14 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, | ||||||
| void hci_adv_instances_clear(struct hci_dev *hdev); | void hci_adv_instances_clear(struct hci_dev *hdev); | ||||||
| struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance); | struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance); | ||||||
| struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance); | struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance); | ||||||
| int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags, | struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance, | ||||||
| 			 u16 adv_data_len, u8 *adv_data, | 				      u32 flags, u16 adv_data_len, u8 *adv_data, | ||||||
| 			 u16 scan_rsp_len, u8 *scan_rsp_data, | 				      u16 scan_rsp_len, u8 *scan_rsp_data, | ||||||
| 			 u16 timeout, u16 duration, s8 tx_power, | 				      u16 timeout, u16 duration, s8 tx_power, | ||||||
| 			 u32 min_interval, u32 max_interval); | 				      u32 min_interval, u32 max_interval); | ||||||
|  | struct adv_info *hci_add_per_instance(struct hci_dev *hdev, u8 instance, | ||||||
|  | 				      u32 flags, u8 data_len, u8 *data, | ||||||
|  | 				      u32 min_interval, u32 max_interval); | ||||||
| int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance, | int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance, | ||||||
| 			 u16 adv_data_len, u8 *adv_data, | 			 u16 adv_data_len, u8 *adv_data, | ||||||
| 			 u16 scan_rsp_len, u8 *scan_rsp_data); | 			 u16 scan_rsp_len, u8 *scan_rsp_data); | ||||||
|  | @ -1631,14 +1696,18 @@ void hci_conn_del_sysfs(struct hci_conn *conn); | ||||||
| #define use_enhanced_conn_complete(dev) (ll_privacy_capable(dev) || \ | #define use_enhanced_conn_complete(dev) (ll_privacy_capable(dev) || \ | ||||||
| 					 ext_adv_capable(dev)) | 					 ext_adv_capable(dev)) | ||||||
| 
 | 
 | ||||||
| /* CIS Master/Slave support */ | /* Periodic advertising support */ | ||||||
| #define iso_capable(dev) (cis_capable(dev)) | #define per_adv_capable(dev) (((dev)->le_features[1] & HCI_LE_PERIODIC_ADV)) | ||||||
|  | 
 | ||||||
|  | /* CIS Master/Slave and BIS support */ | ||||||
|  | #define iso_capable(dev) (cis_capable(dev) || bis_capable(dev)) | ||||||
| #define cis_capable(dev) \ | #define cis_capable(dev) \ | ||||||
| 	(cis_central_capable(dev) || cis_peripheral_capable(dev)) | 	(cis_central_capable(dev) || cis_peripheral_capable(dev)) | ||||||
| #define cis_central_capable(dev) \ | #define cis_central_capable(dev) \ | ||||||
| 	((dev)->le_features[3] & HCI_LE_CIS_CENTRAL) | 	((dev)->le_features[3] & HCI_LE_CIS_CENTRAL) | ||||||
| #define cis_peripheral_capable(dev) \ | #define cis_peripheral_capable(dev) \ | ||||||
| 	((dev)->le_features[3] & HCI_LE_CIS_PERIPHERAL) | 	((dev)->le_features[3] & HCI_LE_CIS_PERIPHERAL) | ||||||
|  | #define bis_capable(dev) ((dev)->le_features[3] & HCI_LE_ISO_BROADCASTER) | ||||||
| 
 | 
 | ||||||
| /* ----- HCI protocols ----- */ | /* ----- HCI protocols ----- */ | ||||||
| #define HCI_PROTO_DEFER             0x01 | #define HCI_PROTO_DEFER             0x01 | ||||||
|  | @ -1926,6 +1995,8 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c); | ||||||
| #define DISCOV_LE_RESTART_DELAY		msecs_to_jiffies(200)	/* msec */ | #define DISCOV_LE_RESTART_DELAY		msecs_to_jiffies(200)	/* msec */ | ||||||
| #define DISCOV_LE_FAST_ADV_INT_MIN	0x00A0	/* 100 msec */ | #define DISCOV_LE_FAST_ADV_INT_MIN	0x00A0	/* 100 msec */ | ||||||
| #define DISCOV_LE_FAST_ADV_INT_MAX	0x00F0	/* 150 msec */ | #define DISCOV_LE_FAST_ADV_INT_MAX	0x00F0	/* 150 msec */ | ||||||
|  | #define DISCOV_LE_PER_ADV_INT_MIN	0x00A0	/* 200 msec */ | ||||||
|  | #define DISCOV_LE_PER_ADV_INT_MAX	0x00A0	/* 200 msec */ | ||||||
| 
 | 
 | ||||||
| #define NAME_RESOLVE_DURATION		msecs_to_jiffies(10240)	/* 10.24 sec */ | #define NAME_RESOLVE_DURATION		msecs_to_jiffies(10240)	/* 10.24 sec */ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -65,6 +65,10 @@ int hci_enable_ext_advertising_sync(struct hci_dev *hdev, u8 instance); | ||||||
| int hci_enable_advertising_sync(struct hci_dev *hdev); | int hci_enable_advertising_sync(struct hci_dev *hdev); | ||||||
| int hci_enable_advertising(struct hci_dev *hdev); | int hci_enable_advertising(struct hci_dev *hdev); | ||||||
| 
 | 
 | ||||||
|  | int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 data_len, | ||||||
|  | 			   u8 *data, u32 flags, u16 min_interval, | ||||||
|  | 			   u16 max_interval, u16 sync_interval); | ||||||
|  | 
 | ||||||
| int hci_remove_advertising_sync(struct hci_dev *hdev, struct sock *sk, | int hci_remove_advertising_sync(struct hci_dev *hdev, struct sock *sk, | ||||||
| 				u8 instance, bool force); | 				u8 instance, bool force); | ||||||
| int hci_disable_advertising_sync(struct hci_dev *hdev); | int hci_disable_advertising_sync(struct hci_dev *hdev); | ||||||
|  | @ -83,6 +87,7 @@ int hci_update_scan(struct hci_dev *hdev); | ||||||
| int hci_write_le_host_supported_sync(struct hci_dev *hdev, u8 le, u8 simul); | int hci_write_le_host_supported_sync(struct hci_dev *hdev, u8 le, u8 simul); | ||||||
| int hci_remove_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance, | int hci_remove_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance, | ||||||
| 				     struct sock *sk); | 				     struct sock *sk); | ||||||
|  | int hci_remove_ext_adv_instance(struct hci_dev *hdev, u8 instance); | ||||||
| struct sk_buff *hci_read_local_oob_data_sync(struct hci_dev *hdev, bool ext, | struct sk_buff *hci_read_local_oob_data_sync(struct hci_dev *hdev, bool ext, | ||||||
| 					     struct sock *sk); | 					     struct sock *sk); | ||||||
| 
 | 
 | ||||||
|  | @ -111,4 +116,9 @@ int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason); | ||||||
| int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn); | int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn); | ||||||
| 
 | 
 | ||||||
| int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle); | int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle); | ||||||
| int hci_le_remove_cig(struct hci_dev *hdev, u8 handle); | 
 | ||||||
|  | int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason); | ||||||
|  | 
 | ||||||
|  | int hci_le_big_terminate_sync(struct hci_dev *hdev, u8 handle); | ||||||
|  | 
 | ||||||
|  | int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle); | ||||||
|  |  | ||||||
|  | @ -236,6 +236,27 @@ void eir_create(struct hci_dev *hdev, u8 *data) | ||||||
| 	ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); | 	ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | u8 eir_create_per_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr) | ||||||
|  | { | ||||||
|  | 	struct adv_info *adv = NULL; | ||||||
|  | 	u8 ad_len = 0; | ||||||
|  | 
 | ||||||
|  | 	/* Return 0 when the current instance identifier is invalid. */ | ||||||
|  | 	if (instance) { | ||||||
|  | 		adv = hci_find_adv_instance(hdev, instance); | ||||||
|  | 		if (!adv) | ||||||
|  | 			return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (adv) { | ||||||
|  | 		memcpy(ptr, adv->per_adv_data, adv->per_adv_data_len); | ||||||
|  | 		ad_len += adv->per_adv_data_len; | ||||||
|  | 		ptr += adv->per_adv_data_len; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ad_len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr) | u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr) | ||||||
| { | { | ||||||
| 	struct adv_info *adv = NULL; | 	struct adv_info *adv = NULL; | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ void eir_create(struct hci_dev *hdev, u8 *data); | ||||||
| 
 | 
 | ||||||
| u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr); | u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr); | ||||||
| u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr); | u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr); | ||||||
|  | u8 eir_create_per_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr); | ||||||
| 
 | 
 | ||||||
| u8 eir_append_local_name(struct hci_dev *hdev, u8 *eir, u8 ad_len); | u8 eir_append_local_name(struct hci_dev *hdev, u8 *eir, u8 ad_len); | ||||||
| u8 eir_append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len); | u8 eir_append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len); | ||||||
|  |  | ||||||
|  | @ -30,10 +30,13 @@ | ||||||
| #include <net/bluetooth/bluetooth.h> | #include <net/bluetooth/bluetooth.h> | ||||||
| #include <net/bluetooth/hci_core.h> | #include <net/bluetooth/hci_core.h> | ||||||
| #include <net/bluetooth/l2cap.h> | #include <net/bluetooth/l2cap.h> | ||||||
|  | #include <net/bluetooth/iso.h> | ||||||
|  | #include <net/bluetooth/mgmt.h> | ||||||
| 
 | 
 | ||||||
| #include "hci_request.h" | #include "hci_request.h" | ||||||
| #include "smp.h" | #include "smp.h" | ||||||
| #include "a2mp.h" | #include "a2mp.h" | ||||||
|  | #include "eir.h" | ||||||
| 
 | 
 | ||||||
| struct sco_param { | struct sco_param { | ||||||
| 	u16 pkt_type; | 	u16 pkt_type; | ||||||
|  | @ -684,6 +687,199 @@ static void le_conn_timeout(struct work_struct *work) | ||||||
| 	hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); | 	hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | struct iso_list_data { | ||||||
|  | 	union { | ||||||
|  | 		u8  cig; | ||||||
|  | 		u8  big; | ||||||
|  | 	}; | ||||||
|  | 	union { | ||||||
|  | 		u8  cis; | ||||||
|  | 		u8  bis; | ||||||
|  | 		u16 sync_handle; | ||||||
|  | 	}; | ||||||
|  | 	int count; | ||||||
|  | 	struct { | ||||||
|  | 		struct hci_cp_le_set_cig_params cp; | ||||||
|  | 		struct hci_cis_params cis[0x11]; | ||||||
|  | 	} pdu; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void bis_list(struct hci_conn *conn, void *data) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data *d = data; | ||||||
|  | 
 | ||||||
|  | 	/* Skip if not broadcast/ANY address */ | ||||||
|  | 	if (bacmp(&conn->dst, BDADDR_ANY)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (d->big != conn->iso_qos.big || d->bis == BT_ISO_QOS_BIS_UNSET || | ||||||
|  | 	    d->bis != conn->iso_qos.bis) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	d->count++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void find_bis(struct hci_conn *conn, void *data) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data *d = data; | ||||||
|  | 
 | ||||||
|  | 	/* Ignore unicast */ | ||||||
|  | 	if (bacmp(&conn->dst, BDADDR_ANY)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	d->count++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int terminate_big_sync(struct hci_dev *hdev, void *data) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data *d = data; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", d->big, d->bis); | ||||||
|  | 
 | ||||||
|  | 	hci_remove_ext_adv_instance_sync(hdev, d->bis, NULL); | ||||||
|  | 
 | ||||||
|  | 	/* Check if ISO connection is a BIS and terminate BIG if there are
 | ||||||
|  | 	 * no other connections using it. | ||||||
|  | 	 */ | ||||||
|  | 	hci_conn_hash_list_state(hdev, find_bis, ISO_LINK, BT_CONNECTED, d); | ||||||
|  | 	if (d->count) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	return hci_le_terminate_big_sync(hdev, d->big, | ||||||
|  | 					 HCI_ERROR_LOCAL_HOST_TERM); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void terminate_big_destroy(struct hci_dev *hdev, void *data, int err) | ||||||
|  | { | ||||||
|  | 	kfree(data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hci_le_terminate_big(struct hci_dev *hdev, u8 big, u8 bis) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data *d; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", big, bis); | ||||||
|  | 
 | ||||||
|  | 	d = kmalloc(sizeof(*d), GFP_KERNEL); | ||||||
|  | 	if (!d) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	memset(d, 0, sizeof(*d)); | ||||||
|  | 	d->big = big; | ||||||
|  | 	d->bis = bis; | ||||||
|  | 
 | ||||||
|  | 	return hci_cmd_sync_queue(hdev, terminate_big_sync, d, | ||||||
|  | 				  terminate_big_destroy); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int big_terminate_sync(struct hci_dev *hdev, void *data) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data *d = data; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", d->big, | ||||||
|  | 		   d->sync_handle); | ||||||
|  | 
 | ||||||
|  | 	/* Check if ISO connection is a BIS and terminate BIG if there are
 | ||||||
|  | 	 * no other connections using it. | ||||||
|  | 	 */ | ||||||
|  | 	hci_conn_hash_list_state(hdev, find_bis, ISO_LINK, BT_CONNECTED, d); | ||||||
|  | 	if (d->count) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	hci_le_big_terminate_sync(hdev, d->big); | ||||||
|  | 
 | ||||||
|  | 	return hci_le_pa_terminate_sync(hdev, d->sync_handle); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data *d; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, sync_handle); | ||||||
|  | 
 | ||||||
|  | 	d = kmalloc(sizeof(*d), GFP_KERNEL); | ||||||
|  | 	if (!d) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	memset(d, 0, sizeof(*d)); | ||||||
|  | 	d->big = big; | ||||||
|  | 	d->sync_handle = sync_handle; | ||||||
|  | 
 | ||||||
|  | 	return hci_cmd_sync_queue(hdev, big_terminate_sync, d, | ||||||
|  | 				  terminate_big_destroy); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Cleanup BIS connection
 | ||||||
|  |  * | ||||||
|  |  * Detects if there any BIS left connected in a BIG | ||||||
|  |  * broadcaster: Remove advertising instance and terminate BIG. | ||||||
|  |  * broadcaster receiver: Teminate BIG sync and terminate PA sync. | ||||||
|  |  */ | ||||||
|  | static void bis_cleanup(struct hci_conn *conn) | ||||||
|  | { | ||||||
|  | 	struct hci_dev *hdev = conn->hdev; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "conn %p", conn); | ||||||
|  | 
 | ||||||
|  | 	if (conn->role == HCI_ROLE_MASTER) { | ||||||
|  | 		if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags)) | ||||||
|  | 			return; | ||||||
|  | 
 | ||||||
|  | 		hci_le_terminate_big(hdev, conn->iso_qos.big, | ||||||
|  | 				     conn->iso_qos.bis); | ||||||
|  | 	} else { | ||||||
|  | 		hci_le_big_terminate(hdev, conn->iso_qos.big, | ||||||
|  | 				     conn->sync_handle); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int remove_cig_sync(struct hci_dev *hdev, void *data) | ||||||
|  | { | ||||||
|  | 	u8 handle = PTR_ERR(data); | ||||||
|  | 
 | ||||||
|  | 	return hci_le_remove_cig_sync(hdev, handle); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hci_le_remove_cig(struct hci_dev *hdev, u8 handle) | ||||||
|  | { | ||||||
|  | 	bt_dev_dbg(hdev, "handle 0x%2.2x", handle); | ||||||
|  | 
 | ||||||
|  | 	return hci_cmd_sync_queue(hdev, remove_cig_sync, ERR_PTR(handle), NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void find_cis(struct hci_conn *conn, void *data) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data *d = data; | ||||||
|  | 
 | ||||||
|  | 	/* Ignore broadcast */ | ||||||
|  | 	if (!bacmp(&conn->dst, BDADDR_ANY)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	d->count++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Cleanup CIS connection:
 | ||||||
|  |  * | ||||||
|  |  * Detects if there any CIS left connected in a CIG and remove it. | ||||||
|  |  */ | ||||||
|  | static void cis_cleanup(struct hci_conn *conn) | ||||||
|  | { | ||||||
|  | 	struct hci_dev *hdev = conn->hdev; | ||||||
|  | 	struct iso_list_data d; | ||||||
|  | 
 | ||||||
|  | 	memset(&d, 0, sizeof(d)); | ||||||
|  | 	d.cig = conn->iso_qos.cig; | ||||||
|  | 
 | ||||||
|  | 	/* Check if ISO connection is a CIS and remove CIG if there are
 | ||||||
|  | 	 * no other connections using it. | ||||||
|  | 	 */ | ||||||
|  | 	hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d); | ||||||
|  | 	if (d.count) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	hci_le_remove_cig(hdev, conn->iso_qos.cig); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, | struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, | ||||||
| 			      u8 role) | 			      u8 role) | ||||||
| { | { | ||||||
|  | @ -725,9 +921,19 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, | ||||||
| 		conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; | 		conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; | ||||||
| 		break; | 		break; | ||||||
| 	case LE_LINK: | 	case LE_LINK: | ||||||
|  | 		/* conn->src should reflect the local identity address */ | ||||||
|  | 		hci_copy_identity_address(hdev, &conn->src, &conn->src_type); | ||||||
|  | 		break; | ||||||
| 	case ISO_LINK: | 	case ISO_LINK: | ||||||
| 		/* conn->src should reflect the local identity address */ | 		/* conn->src should reflect the local identity address */ | ||||||
| 		hci_copy_identity_address(hdev, &conn->src, &conn->src_type); | 		hci_copy_identity_address(hdev, &conn->src, &conn->src_type); | ||||||
|  | 
 | ||||||
|  | 		/* set proper cleanup function */ | ||||||
|  | 		if (!bacmp(dst, BDADDR_ANY)) | ||||||
|  | 			conn->cleanup = bis_cleanup; | ||||||
|  | 		else if (conn->role == HCI_ROLE_MASTER) | ||||||
|  | 			conn->cleanup = cis_cleanup; | ||||||
|  | 
 | ||||||
| 		break; | 		break; | ||||||
| 	case SCO_LINK: | 	case SCO_LINK: | ||||||
| 		if (lmp_esco_capable(hdev)) | 		if (lmp_esco_capable(hdev)) | ||||||
|  | @ -1100,6 +1306,108 @@ static int hci_explicit_conn_params_set(struct hci_dev *hdev, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data data; | ||||||
|  | 
 | ||||||
|  | 	/* Allocate a BIG if not set */ | ||||||
|  | 	if (qos->big == BT_ISO_QOS_BIG_UNSET) { | ||||||
|  | 		for (data.big = 0x00; data.big < 0xef; data.big++) { | ||||||
|  | 			data.count = 0; | ||||||
|  | 			data.bis = 0xff; | ||||||
|  | 
 | ||||||
|  | 			hci_conn_hash_list_state(hdev, bis_list, ISO_LINK, | ||||||
|  | 						 BT_BOUND, &data); | ||||||
|  | 			if (!data.count) | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (data.big == 0xef) | ||||||
|  | 			return -EADDRNOTAVAIL; | ||||||
|  | 
 | ||||||
|  | 		/* Update BIG */ | ||||||
|  | 		qos->big = data.big; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos) | ||||||
|  | { | ||||||
|  | 	struct iso_list_data data; | ||||||
|  | 
 | ||||||
|  | 	/* Allocate BIS if not set */ | ||||||
|  | 	if (qos->bis == BT_ISO_QOS_BIS_UNSET) { | ||||||
|  | 		/* Find an unused adv set to advertise BIS, skip instance 0x00
 | ||||||
|  | 		 * since it is reserved as general purpose set. | ||||||
|  | 		 */ | ||||||
|  | 		for (data.bis = 0x01; data.bis < hdev->le_num_of_adv_sets; | ||||||
|  | 		     data.bis++) { | ||||||
|  | 			data.count = 0; | ||||||
|  | 
 | ||||||
|  | 			hci_conn_hash_list_state(hdev, bis_list, ISO_LINK, | ||||||
|  | 						 BT_BOUND, &data); | ||||||
|  | 			if (!data.count) | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (data.bis == hdev->le_num_of_adv_sets) | ||||||
|  | 			return -EADDRNOTAVAIL; | ||||||
|  | 
 | ||||||
|  | 		/* Update BIS */ | ||||||
|  | 		qos->bis = data.bis; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* This function requires the caller holds hdev->lock */ | ||||||
|  | static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst, | ||||||
|  | 				    struct bt_iso_qos *qos) | ||||||
|  | { | ||||||
|  | 	struct hci_conn *conn; | ||||||
|  | 	struct iso_list_data data; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	/* Let's make sure that le is enabled.*/ | ||||||
|  | 	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { | ||||||
|  | 		if (lmp_le_capable(hdev)) | ||||||
|  | 			return ERR_PTR(-ECONNREFUSED); | ||||||
|  | 		return ERR_PTR(-EOPNOTSUPP); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = qos_set_big(hdev, qos); | ||||||
|  | 	if (err) | ||||||
|  | 		return ERR_PTR(err); | ||||||
|  | 
 | ||||||
|  | 	err = qos_set_bis(hdev, qos); | ||||||
|  | 	if (err) | ||||||
|  | 		return ERR_PTR(err); | ||||||
|  | 
 | ||||||
|  | 	data.big = qos->big; | ||||||
|  | 	data.bis = qos->bis; | ||||||
|  | 	data.count = 0; | ||||||
|  | 
 | ||||||
|  | 	/* Check if there is already a matching BIG/BIS */ | ||||||
|  | 	hci_conn_hash_list_state(hdev, bis_list, ISO_LINK, BT_BOUND, &data); | ||||||
|  | 	if (data.count) | ||||||
|  | 		return ERR_PTR(-EADDRINUSE); | ||||||
|  | 
 | ||||||
|  | 	conn = hci_conn_hash_lookup_bis(hdev, dst, qos->big, qos->bis); | ||||||
|  | 	if (conn) | ||||||
|  | 		return ERR_PTR(-EADDRINUSE); | ||||||
|  | 
 | ||||||
|  | 	conn = hci_conn_add(hdev, ISO_LINK, dst, HCI_ROLE_MASTER); | ||||||
|  | 	if (!conn) | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 
 | ||||||
|  | 	set_bit(HCI_CONN_PER_ADV, &conn->flags); | ||||||
|  | 	conn->state = BT_CONNECT; | ||||||
|  | 
 | ||||||
|  | 	hci_conn_hold(conn); | ||||||
|  | 	return conn; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* This function requires the caller holds hdev->lock */ | /* This function requires the caller holds hdev->lock */ | ||||||
| struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst, | struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst, | ||||||
| 				     u8 dst_type, u8 sec_level, | 				     u8 dst_type, u8 sec_level, | ||||||
|  | @ -1236,16 +1544,6 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, | ||||||
| 	return sco; | 	return sco; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct iso_list_data { |  | ||||||
| 	u8  cig; |  | ||||||
| 	u8  cis; |  | ||||||
| 	int count; |  | ||||||
| 	struct { |  | ||||||
| 		struct hci_cp_le_set_cig_params cp; |  | ||||||
| 		struct hci_cis_params cis[0x11]; |  | ||||||
| 	} pdu; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos) | static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos) | ||||||
| { | { | ||||||
| 	struct hci_cis_params *cis = &d->pdu.cis[d->pdu.cp.num_cis]; | 	struct hci_cis_params *cis = &d->pdu.cis[d->pdu.cp.num_cis]; | ||||||
|  | @ -1265,6 +1563,10 @@ static void cis_list(struct hci_conn *conn, void *data) | ||||||
| { | { | ||||||
| 	struct iso_list_data *d = data; | 	struct iso_list_data *d = data; | ||||||
| 
 | 
 | ||||||
|  | 	/* Skip if broadcast/ANY address */ | ||||||
|  | 	if (!bacmp(&conn->dst, BDADDR_ANY)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
| 	if (d->cig != conn->iso_qos.cig || d->cis == BT_ISO_QOS_CIS_UNSET || | 	if (d->cig != conn->iso_qos.cig || d->cis == BT_ISO_QOS_CIS_UNSET || | ||||||
| 	    d->cis != conn->iso_qos.cis) | 	    d->cis != conn->iso_qos.cis) | ||||||
| 		return; | 		return; | ||||||
|  | @ -1278,6 +1580,29 @@ static void cis_list(struct hci_conn *conn, void *data) | ||||||
| 	cis_add(d, &conn->iso_qos); | 	cis_add(d, &conn->iso_qos); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) | ||||||
|  | { | ||||||
|  | 	struct hci_dev *hdev = conn->hdev; | ||||||
|  | 	struct hci_cp_le_create_big cp; | ||||||
|  | 
 | ||||||
|  | 	memset(&cp, 0, sizeof(cp)); | ||||||
|  | 
 | ||||||
|  | 	cp.handle = qos->big; | ||||||
|  | 	cp.adv_handle = qos->bis; | ||||||
|  | 	cp.num_bis  = 0x01; | ||||||
|  | 	hci_cpu_to_le24(qos->out.interval, cp.bis.sdu_interval); | ||||||
|  | 	cp.bis.sdu = cpu_to_le16(qos->out.sdu); | ||||||
|  | 	cp.bis.latency =  cpu_to_le16(qos->out.latency); | ||||||
|  | 	cp.bis.rtn  = qos->out.rtn; | ||||||
|  | 	cp.bis.phy  = qos->out.phy; | ||||||
|  | 	cp.bis.packing = qos->packing; | ||||||
|  | 	cp.bis.framing = qos->framing; | ||||||
|  | 	cp.bis.encryption = 0x00; | ||||||
|  | 	memset(&cp.bis.bcode, 0, sizeof(cp.bis.bcode)); | ||||||
|  | 
 | ||||||
|  | 	return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) | static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) | ||||||
| { | { | ||||||
| 	struct hci_dev *hdev = conn->hdev; | 	struct hci_dev *hdev = conn->hdev; | ||||||
|  | @ -1361,49 +1686,6 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void find_cis(struct hci_conn *conn, void *data) |  | ||||||
| { |  | ||||||
| 	struct iso_list_data *d = data; |  | ||||||
| 
 |  | ||||||
| 	/* Ignore broadcast */ |  | ||||||
| 	if (!bacmp(&conn->dst, BDADDR_ANY)) |  | ||||||
| 		return; |  | ||||||
| 
 |  | ||||||
| 	d->count++; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int remove_cig_sync(struct hci_dev *hdev, void *data) |  | ||||||
| { |  | ||||||
| 	u8 handle = PTR_ERR(data); |  | ||||||
| 
 |  | ||||||
| 	return hci_le_remove_cig_sync(hdev, handle); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int hci_le_remove_cig(struct hci_dev *hdev, u8 handle) |  | ||||||
| { |  | ||||||
| 	bt_dev_dbg(hdev, "handle 0x%2.2x", handle); |  | ||||||
| 
 |  | ||||||
| 	return hci_cmd_sync_queue(hdev, remove_cig_sync, ERR_PTR(handle), NULL); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void cis_cleanup(struct hci_conn *conn) |  | ||||||
| { |  | ||||||
| 	struct hci_dev *hdev = conn->hdev; |  | ||||||
| 	struct iso_list_data d; |  | ||||||
| 
 |  | ||||||
| 	memset(&d, 0, sizeof(d)); |  | ||||||
| 	d.cig = conn->iso_qos.cig; |  | ||||||
| 
 |  | ||||||
| 	/* Check if ISO connection is a CIS and remove CIG if there are
 |  | ||||||
| 	 * no other connections using it. |  | ||||||
| 	 */ |  | ||||||
| 	hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d); |  | ||||||
| 	if (d.count) |  | ||||||
| 		return; |  | ||||||
| 
 |  | ||||||
| 	hci_le_remove_cig(hdev, conn->iso_qos.cig); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, | struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, | ||||||
| 			      __u8 dst_type, struct bt_iso_qos *qos) | 			      __u8 dst_type, struct bt_iso_qos *qos) | ||||||
| { | { | ||||||
|  | @ -1622,6 +1904,179 @@ static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn, | ||||||
| 		qos->latency = conn->le_conn_latency; | 		qos->latency = conn->le_conn_latency; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static struct hci_conn *hci_bind_bis(struct hci_conn *conn, | ||||||
|  | 				     struct bt_iso_qos *qos) | ||||||
|  | { | ||||||
|  | 	/* Update LINK PHYs according to QoS preference */ | ||||||
|  | 	conn->le_tx_phy = qos->out.phy; | ||||||
|  | 	conn->le_tx_phy = qos->out.phy; | ||||||
|  | 	conn->iso_qos = *qos; | ||||||
|  | 	conn->state = BT_BOUND; | ||||||
|  | 
 | ||||||
|  | 	return conn; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int create_big_sync(struct hci_dev *hdev, void *data) | ||||||
|  | { | ||||||
|  | 	struct hci_conn *conn = data; | ||||||
|  | 	struct bt_iso_qos *qos = &conn->iso_qos; | ||||||
|  | 	u16 interval, sync_interval = 0; | ||||||
|  | 	u32 flags = 0; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	if (qos->out.phy == 0x02) | ||||||
|  | 		flags |= MGMT_ADV_FLAG_SEC_2M; | ||||||
|  | 
 | ||||||
|  | 	/* Align intervals */ | ||||||
|  | 	interval = qos->out.interval / 1250; | ||||||
|  | 
 | ||||||
|  | 	if (qos->bis) | ||||||
|  | 		sync_interval = qos->sync_interval * 1600; | ||||||
|  | 
 | ||||||
|  | 	err = hci_start_per_adv_sync(hdev, qos->bis, conn->le_per_adv_data_len, | ||||||
|  | 				     conn->le_per_adv_data, flags, interval, | ||||||
|  | 				     interval, sync_interval); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	return hci_le_create_big(conn, &conn->iso_qos); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void create_pa_complete(struct hci_dev *hdev, void *data, int err) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_pa_create_sync *cp = data; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, ""); | ||||||
|  | 
 | ||||||
|  | 	if (err) | ||||||
|  | 		bt_dev_err(hdev, "Unable to create PA: %d", err); | ||||||
|  | 
 | ||||||
|  | 	kfree(cp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int create_pa_sync(struct hci_dev *hdev, void *data) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_pa_create_sync *cp = data; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	err = __hci_cmd_sync_status(hdev, HCI_OP_LE_PA_CREATE_SYNC, | ||||||
|  | 				    sizeof(*cp), cp, HCI_CMD_TIMEOUT); | ||||||
|  | 	if (err) { | ||||||
|  | 		hci_dev_clear_flag(hdev, HCI_PA_SYNC); | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return hci_update_passive_scan_sync(hdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, | ||||||
|  | 		       __u8 sid) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_pa_create_sync *cp; | ||||||
|  | 
 | ||||||
|  | 	if (hci_dev_test_and_set_flag(hdev, HCI_PA_SYNC)) | ||||||
|  | 		return -EBUSY; | ||||||
|  | 
 | ||||||
|  | 	cp = kmalloc(sizeof(*cp), GFP_KERNEL); | ||||||
|  | 	if (!cp) { | ||||||
|  | 		hci_dev_clear_flag(hdev, HCI_PA_SYNC); | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Convert from ISO socket address type to HCI address type  */ | ||||||
|  | 	if (dst_type == BDADDR_LE_PUBLIC) | ||||||
|  | 		dst_type = ADDR_LE_DEV_PUBLIC; | ||||||
|  | 	else | ||||||
|  | 		dst_type = ADDR_LE_DEV_RANDOM; | ||||||
|  | 
 | ||||||
|  | 	memset(cp, 0, sizeof(*cp)); | ||||||
|  | 	cp->sid = sid; | ||||||
|  | 	cp->addr_type = dst_type; | ||||||
|  | 	bacpy(&cp->addr, dst); | ||||||
|  | 
 | ||||||
|  | 	/* Queue start pa_create_sync and scan */ | ||||||
|  | 	return hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos, | ||||||
|  | 			   __u16 sync_handle, __u8 num_bis, __u8 bis[]) | ||||||
|  | { | ||||||
|  | 	struct _packed { | ||||||
|  | 		struct hci_cp_le_big_create_sync cp; | ||||||
|  | 		__u8  bis[0x11]; | ||||||
|  | 	} pdu; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	if (num_bis > sizeof(pdu.bis)) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	err = qos_set_big(hdev, qos); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	memset(&pdu, 0, sizeof(pdu)); | ||||||
|  | 	pdu.cp.handle = qos->big; | ||||||
|  | 	pdu.cp.sync_handle = cpu_to_le16(sync_handle); | ||||||
|  | 	pdu.cp.num_bis = num_bis; | ||||||
|  | 	memcpy(pdu.bis, bis, num_bis); | ||||||
|  | 
 | ||||||
|  | 	return hci_send_cmd(hdev, HCI_OP_LE_BIG_CREATE_SYNC, | ||||||
|  | 			    sizeof(pdu.cp) + num_bis, &pdu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void create_big_complete(struct hci_dev *hdev, void *data, int err) | ||||||
|  | { | ||||||
|  | 	struct hci_conn *conn = data; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "conn %p", conn); | ||||||
|  | 
 | ||||||
|  | 	if (err) { | ||||||
|  | 		bt_dev_err(hdev, "Unable to create BIG: %d", err); | ||||||
|  | 		hci_connect_cfm(conn, err); | ||||||
|  | 		hci_conn_del(conn); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, | ||||||
|  | 				 __u8 dst_type, struct bt_iso_qos *qos, | ||||||
|  | 				 __u8 base_len, __u8 *base) | ||||||
|  | { | ||||||
|  | 	struct hci_conn *conn; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	/* We need hci_conn object using the BDADDR_ANY as dst */ | ||||||
|  | 	conn = hci_add_bis(hdev, dst, qos); | ||||||
|  | 	if (IS_ERR(conn)) | ||||||
|  | 		return conn; | ||||||
|  | 
 | ||||||
|  | 	conn = hci_bind_bis(conn, qos); | ||||||
|  | 	if (!conn) { | ||||||
|  | 		hci_conn_drop(conn); | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Add Basic Announcement into Peridic Adv Data if BASE is set */ | ||||||
|  | 	if (base_len && base) { | ||||||
|  | 		base_len = eir_append_service_data(conn->le_per_adv_data, 0, | ||||||
|  | 						   0x1851, base, base_len); | ||||||
|  | 		conn->le_per_adv_data_len = base_len; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Queue start periodic advertising and create BIG */ | ||||||
|  | 	err = hci_cmd_sync_queue(hdev, create_big_sync, conn, | ||||||
|  | 				 create_big_complete); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		hci_conn_drop(conn); | ||||||
|  | 		return ERR_PTR(err); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hci_iso_qos_setup(hdev, conn, &qos->out, | ||||||
|  | 			  conn->le_tx_phy ? conn->le_tx_phy : | ||||||
|  | 			  hdev->le_tx_def_phys); | ||||||
|  | 
 | ||||||
|  | 	return conn; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, | struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, | ||||||
| 				 __u8 dst_type, struct bt_iso_qos *qos) | 				 __u8 dst_type, struct bt_iso_qos *qos) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -1702,57 +1702,77 @@ static void adv_instance_rpa_expired(struct work_struct *work) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* This function requires the caller holds hdev->lock */ | /* This function requires the caller holds hdev->lock */ | ||||||
| int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags, | struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance, | ||||||
| 			 u16 adv_data_len, u8 *adv_data, | 				      u32 flags, u16 adv_data_len, u8 *adv_data, | ||||||
| 			 u16 scan_rsp_len, u8 *scan_rsp_data, | 				      u16 scan_rsp_len, u8 *scan_rsp_data, | ||||||
| 			 u16 timeout, u16 duration, s8 tx_power, | 				      u16 timeout, u16 duration, s8 tx_power, | ||||||
| 			 u32 min_interval, u32 max_interval) | 				      u32 min_interval, u32 max_interval) | ||||||
| { | { | ||||||
| 	struct adv_info *adv_instance; | 	struct adv_info *adv; | ||||||
| 
 | 
 | ||||||
| 	adv_instance = hci_find_adv_instance(hdev, instance); | 	adv = hci_find_adv_instance(hdev, instance); | ||||||
| 	if (adv_instance) { | 	if (adv) { | ||||||
| 		memset(adv_instance->adv_data, 0, | 		memset(adv->adv_data, 0, sizeof(adv->adv_data)); | ||||||
| 		       sizeof(adv_instance->adv_data)); | 		memset(adv->scan_rsp_data, 0, sizeof(adv->scan_rsp_data)); | ||||||
| 		memset(adv_instance->scan_rsp_data, 0, | 		memset(adv->per_adv_data, 0, sizeof(adv->per_adv_data)); | ||||||
| 		       sizeof(adv_instance->scan_rsp_data)); |  | ||||||
| 	} else { | 	} else { | ||||||
| 		if (hdev->adv_instance_cnt >= hdev->le_num_of_adv_sets || | 		if (hdev->adv_instance_cnt >= hdev->le_num_of_adv_sets || | ||||||
| 		    instance < 1 || instance > hdev->le_num_of_adv_sets) | 		    instance < 1 || instance > hdev->le_num_of_adv_sets) | ||||||
| 			return -EOVERFLOW; | 			return ERR_PTR(-EOVERFLOW); | ||||||
| 
 | 
 | ||||||
| 		adv_instance = kzalloc(sizeof(*adv_instance), GFP_KERNEL); | 		adv = kzalloc(sizeof(*adv), GFP_KERNEL); | ||||||
| 		if (!adv_instance) | 		if (!adv) | ||||||
| 			return -ENOMEM; | 			return ERR_PTR(-ENOMEM); | ||||||
| 
 | 
 | ||||||
| 		adv_instance->pending = true; | 		adv->pending = true; | ||||||
| 		adv_instance->instance = instance; | 		adv->instance = instance; | ||||||
| 		list_add(&adv_instance->list, &hdev->adv_instances); | 		list_add(&adv->list, &hdev->adv_instances); | ||||||
| 		hdev->adv_instance_cnt++; | 		hdev->adv_instance_cnt++; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	adv_instance->flags = flags; | 	adv->flags = flags; | ||||||
| 	adv_instance->min_interval = min_interval; | 	adv->min_interval = min_interval; | ||||||
| 	adv_instance->max_interval = max_interval; | 	adv->max_interval = max_interval; | ||||||
| 	adv_instance->tx_power = tx_power; | 	adv->tx_power = tx_power; | ||||||
| 
 | 
 | ||||||
| 	hci_set_adv_instance_data(hdev, instance, adv_data_len, adv_data, | 	hci_set_adv_instance_data(hdev, instance, adv_data_len, adv_data, | ||||||
| 				  scan_rsp_len, scan_rsp_data); | 				  scan_rsp_len, scan_rsp_data); | ||||||
| 
 | 
 | ||||||
| 	adv_instance->timeout = timeout; | 	adv->timeout = timeout; | ||||||
| 	adv_instance->remaining_time = timeout; | 	adv->remaining_time = timeout; | ||||||
| 
 | 
 | ||||||
| 	if (duration == 0) | 	if (duration == 0) | ||||||
| 		adv_instance->duration = hdev->def_multi_adv_rotation_duration; | 		adv->duration = hdev->def_multi_adv_rotation_duration; | ||||||
| 	else | 	else | ||||||
| 		adv_instance->duration = duration; | 		adv->duration = duration; | ||||||
| 
 | 
 | ||||||
| 	INIT_DELAYED_WORK(&adv_instance->rpa_expired_cb, | 	INIT_DELAYED_WORK(&adv->rpa_expired_cb, adv_instance_rpa_expired); | ||||||
| 			  adv_instance_rpa_expired); |  | ||||||
| 
 | 
 | ||||||
| 	BT_DBG("%s for %dMR", hdev->name, instance); | 	BT_DBG("%s for %dMR", hdev->name, instance); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return adv; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* This function requires the caller holds hdev->lock */ | ||||||
|  | struct adv_info *hci_add_per_instance(struct hci_dev *hdev, u8 instance, | ||||||
|  | 				      u32 flags, u8 data_len, u8 *data, | ||||||
|  | 				      u32 min_interval, u32 max_interval) | ||||||
|  | { | ||||||
|  | 	struct adv_info *adv; | ||||||
|  | 
 | ||||||
|  | 	adv = hci_add_adv_instance(hdev, instance, flags, 0, NULL, 0, NULL, | ||||||
|  | 				   0, 0, HCI_ADV_TX_POWER_NO_PREFERENCE, | ||||||
|  | 				   min_interval, max_interval); | ||||||
|  | 	if (IS_ERR(adv)) | ||||||
|  | 		return adv; | ||||||
|  | 
 | ||||||
|  | 	adv->periodic = true; | ||||||
|  | 	adv->per_adv_data_len = data_len; | ||||||
|  | 
 | ||||||
|  | 	if (data) | ||||||
|  | 		memcpy(adv->per_adv_data, data, data_len); | ||||||
|  | 
 | ||||||
|  | 	return adv; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* This function requires the caller holds hdev->lock */ | /* This function requires the caller holds hdev->lock */ | ||||||
|  |  | ||||||
|  | @ -3893,6 +3893,57 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data, | ||||||
| 	return rp->status; | 	return rp->status; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void hci_cs_le_create_big(struct hci_dev *hdev, u8 status) | ||||||
|  | { | ||||||
|  | 	bt_dev_dbg(hdev, "status 0x%2.2x", status); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u8 hci_cc_set_per_adv_param(struct hci_dev *hdev, void *data, | ||||||
|  | 				   struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct hci_ev_status *rp = data; | ||||||
|  | 	struct hci_cp_le_set_per_adv_params *cp; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); | ||||||
|  | 
 | ||||||
|  | 	if (rp->status) | ||||||
|  | 		return rp->status; | ||||||
|  | 
 | ||||||
|  | 	cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PER_ADV_PARAMS); | ||||||
|  | 	if (!cp) | ||||||
|  | 		return rp->status; | ||||||
|  | 
 | ||||||
|  | 	/* TODO: set the conn state */ | ||||||
|  | 	return rp->status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u8 hci_cc_le_set_per_adv_enable(struct hci_dev *hdev, void *data, | ||||||
|  | 				       struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct hci_ev_status *rp = data; | ||||||
|  | 	__u8 *sent; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); | ||||||
|  | 
 | ||||||
|  | 	if (rp->status) | ||||||
|  | 		return rp->status; | ||||||
|  | 
 | ||||||
|  | 	sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE); | ||||||
|  | 	if (!sent) | ||||||
|  | 		return rp->status; | ||||||
|  | 
 | ||||||
|  | 	hci_dev_lock(hdev); | ||||||
|  | 
 | ||||||
|  | 	if (*sent) | ||||||
|  | 		hci_dev_set_flag(hdev, HCI_LE_PER_ADV); | ||||||
|  | 	else | ||||||
|  | 		hci_dev_clear_flag(hdev, HCI_LE_PER_ADV); | ||||||
|  | 
 | ||||||
|  | 	hci_dev_unlock(hdev); | ||||||
|  | 
 | ||||||
|  | 	return rp->status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #define HCI_CC_VL(_op, _func, _min, _max) \ | #define HCI_CC_VL(_op, _func, _min, _max) \ | ||||||
| { \ | { \ | ||||||
| 	.op = _op, \ | 	.op = _op, \ | ||||||
|  | @ -4066,6 +4117,9 @@ static const struct hci_cc { | ||||||
| 		      hci_cc_le_set_adv_set_random_addr), | 		      hci_cc_le_set_adv_set_random_addr), | ||||||
| 	HCI_CC_STATUS(HCI_OP_LE_REMOVE_ADV_SET, hci_cc_le_remove_adv_set), | 	HCI_CC_STATUS(HCI_OP_LE_REMOVE_ADV_SET, hci_cc_le_remove_adv_set), | ||||||
| 	HCI_CC_STATUS(HCI_OP_LE_CLEAR_ADV_SETS, hci_cc_le_clear_adv_sets), | 	HCI_CC_STATUS(HCI_OP_LE_CLEAR_ADV_SETS, hci_cc_le_clear_adv_sets), | ||||||
|  | 	HCI_CC_STATUS(HCI_OP_LE_SET_PER_ADV_PARAMS, hci_cc_set_per_adv_param), | ||||||
|  | 	HCI_CC_STATUS(HCI_OP_LE_SET_PER_ADV_ENABLE, | ||||||
|  | 		      hci_cc_le_set_per_adv_enable), | ||||||
| 	HCI_CC(HCI_OP_LE_READ_TRANSMIT_POWER, hci_cc_le_read_transmit_power, | 	HCI_CC(HCI_OP_LE_READ_TRANSMIT_POWER, hci_cc_le_read_transmit_power, | ||||||
| 	       sizeof(struct hci_rp_le_read_transmit_power)), | 	       sizeof(struct hci_rp_le_read_transmit_power)), | ||||||
| 	HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE, hci_cc_le_set_privacy_mode), | 	HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE, hci_cc_le_set_privacy_mode), | ||||||
|  | @ -4202,6 +4256,7 @@ static const struct hci_cs { | ||||||
| 	HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc), | 	HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc), | ||||||
| 	HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn), | 	HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn), | ||||||
| 	HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis), | 	HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis), | ||||||
|  | 	HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void hci_cmd_status_evt(struct hci_dev *hdev, void *data, | static void hci_cmd_status_evt(struct hci_dev *hdev, void *data, | ||||||
|  | @ -6425,6 +6480,39 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data, | ||||||
| 	hci_dev_unlock(hdev); | 	hci_dev_unlock(hdev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_pa_term_sync cp; | ||||||
|  | 
 | ||||||
|  | 	memset(&cp, 0, sizeof(cp)); | ||||||
|  | 	cp.handle = handle; | ||||||
|  | 
 | ||||||
|  | 	return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data, | ||||||
|  | 					    struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct hci_ev_le_pa_sync_established *ev = data; | ||||||
|  | 	int mask = hdev->link_mode; | ||||||
|  | 	__u8 flags = 0; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); | ||||||
|  | 
 | ||||||
|  | 	if (ev->status) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	hci_dev_lock(hdev); | ||||||
|  | 
 | ||||||
|  | 	hci_dev_clear_flag(hdev, HCI_PA_SYNC); | ||||||
|  | 
 | ||||||
|  | 	mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ISO_LINK, &flags); | ||||||
|  | 	if (!(mask & HCI_LM_ACCEPT)) | ||||||
|  | 		hci_le_pa_term_sync(hdev, ev->handle); | ||||||
|  | 
 | ||||||
|  | 	hci_dev_unlock(hdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data, | static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data, | ||||||
| 					    struct sk_buff *skb) | 					    struct sk_buff *skb) | ||||||
| { | { | ||||||
|  | @ -6776,6 +6864,105 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data, | ||||||
| 	hci_dev_unlock(hdev); | 	hci_dev_unlock(hdev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data, | ||||||
|  | 					   struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct hci_evt_le_create_big_complete *ev = data; | ||||||
|  | 	struct hci_conn *conn; | ||||||
|  | 
 | ||||||
|  | 	BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); | ||||||
|  | 
 | ||||||
|  | 	if (!hci_le_ev_skb_pull(hdev, skb, HCI_EVT_LE_CREATE_BIG_COMPLETE, | ||||||
|  | 				flex_array_size(ev, bis_handle, ev->num_bis))) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	hci_dev_lock(hdev); | ||||||
|  | 
 | ||||||
|  | 	conn = hci_conn_hash_lookup_big(hdev, ev->handle); | ||||||
|  | 	if (!conn) | ||||||
|  | 		goto unlock; | ||||||
|  | 
 | ||||||
|  | 	if (ev->num_bis) | ||||||
|  | 		conn->handle = __le16_to_cpu(ev->bis_handle[0]); | ||||||
|  | 
 | ||||||
|  | 	if (!ev->status) { | ||||||
|  | 		conn->state = BT_CONNECTED; | ||||||
|  | 		hci_debugfs_create_conn(conn); | ||||||
|  | 		hci_conn_add_sysfs(conn); | ||||||
|  | 		hci_iso_setup_path(conn); | ||||||
|  | 		goto unlock; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hci_connect_cfm(conn, ev->status); | ||||||
|  | 	hci_conn_del(conn); | ||||||
|  | 
 | ||||||
|  | unlock: | ||||||
|  | 	hci_dev_unlock(hdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data, | ||||||
|  | 					    struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct hci_evt_le_big_sync_estabilished *ev = data; | ||||||
|  | 	struct hci_conn *bis; | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); | ||||||
|  | 
 | ||||||
|  | 	if (!hci_le_ev_skb_pull(hdev, skb, HCI_EVT_LE_BIG_SYNC_ESTABILISHED, | ||||||
|  | 				flex_array_size(ev, bis, ev->num_bis))) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (ev->status) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	hci_dev_lock(hdev); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ev->num_bis; i++) { | ||||||
|  | 		u16 handle = le16_to_cpu(ev->bis[i]); | ||||||
|  | 		__le32 interval; | ||||||
|  | 
 | ||||||
|  | 		bis = hci_conn_hash_lookup_handle(hdev, handle); | ||||||
|  | 		if (!bis) { | ||||||
|  | 			bis = hci_conn_add(hdev, ISO_LINK, BDADDR_ANY, | ||||||
|  | 					   HCI_ROLE_SLAVE); | ||||||
|  | 			if (!bis) | ||||||
|  | 				continue; | ||||||
|  | 			bis->handle = handle; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		bis->iso_qos.big = ev->handle; | ||||||
|  | 		memset(&interval, 0, sizeof(interval)); | ||||||
|  | 		memcpy(&interval, ev->latency, sizeof(ev->latency)); | ||||||
|  | 		bis->iso_qos.in.interval = le32_to_cpu(interval); | ||||||
|  | 		/* Convert ISO Interval (1.25 ms slots) to latency (ms) */ | ||||||
|  | 		bis->iso_qos.in.latency = le16_to_cpu(ev->interval) * 125 / 100; | ||||||
|  | 		bis->iso_qos.in.sdu = le16_to_cpu(ev->max_pdu); | ||||||
|  | 
 | ||||||
|  | 		hci_connect_cfm(bis, ev->status); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hci_dev_unlock(hdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data, | ||||||
|  | 					   struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct hci_evt_le_big_info_adv_report *ev = data; | ||||||
|  | 	int mask = hdev->link_mode; | ||||||
|  | 	__u8 flags = 0; | ||||||
|  | 
 | ||||||
|  | 	bt_dev_dbg(hdev, "sync_handle 0x%4.4x", le16_to_cpu(ev->sync_handle)); | ||||||
|  | 
 | ||||||
|  | 	hci_dev_lock(hdev); | ||||||
|  | 
 | ||||||
|  | 	mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags); | ||||||
|  | 	if (!(mask & HCI_LM_ACCEPT)) | ||||||
|  | 		hci_le_pa_term_sync(hdev, ev->sync_handle); | ||||||
|  | 
 | ||||||
|  | 	hci_dev_unlock(hdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \ | #define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \ | ||||||
| [_op] = { \ | [_op] = { \ | ||||||
| 	.func = _func, \ | 	.func = _func, \ | ||||||
|  | @ -6836,6 +7023,10 @@ static const struct hci_le_ev { | ||||||
| 	HCI_LE_EV_VL(HCI_EV_LE_EXT_ADV_REPORT, hci_le_ext_adv_report_evt, | 	HCI_LE_EV_VL(HCI_EV_LE_EXT_ADV_REPORT, hci_le_ext_adv_report_evt, | ||||||
| 		     sizeof(struct hci_ev_le_ext_adv_report), | 		     sizeof(struct hci_ev_le_ext_adv_report), | ||||||
| 		     HCI_MAX_EVENT_SIZE), | 		     HCI_MAX_EVENT_SIZE), | ||||||
|  | 	/* [0x0e = HCI_EV_LE_PA_SYNC_ESTABLISHED] */ | ||||||
|  | 	HCI_LE_EV(HCI_EV_LE_PA_SYNC_ESTABLISHED, | ||||||
|  | 		  hci_le_pa_sync_estabilished_evt, | ||||||
|  | 		  sizeof(struct hci_ev_le_pa_sync_established)), | ||||||
| 	/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */ | 	/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */ | ||||||
| 	HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt, | 	HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt, | ||||||
| 		  sizeof(struct hci_evt_le_ext_adv_set_term)), | 		  sizeof(struct hci_evt_le_ext_adv_set_term)), | ||||||
|  | @ -6845,6 +7036,21 @@ static const struct hci_le_ev { | ||||||
| 	/* [0x1a = HCI_EVT_LE_CIS_REQ] */ | 	/* [0x1a = HCI_EVT_LE_CIS_REQ] */ | ||||||
| 	HCI_LE_EV(HCI_EVT_LE_CIS_REQ, hci_le_cis_req_evt, | 	HCI_LE_EV(HCI_EVT_LE_CIS_REQ, hci_le_cis_req_evt, | ||||||
| 		  sizeof(struct hci_evt_le_cis_req)), | 		  sizeof(struct hci_evt_le_cis_req)), | ||||||
|  | 	/* [0x1b = HCI_EVT_LE_CREATE_BIG_COMPLETE] */ | ||||||
|  | 	HCI_LE_EV_VL(HCI_EVT_LE_CREATE_BIG_COMPLETE, | ||||||
|  | 		     hci_le_create_big_complete_evt, | ||||||
|  | 		     sizeof(struct hci_evt_le_create_big_complete), | ||||||
|  | 		     HCI_MAX_EVENT_SIZE), | ||||||
|  | 	/* [0x1d = HCI_EV_LE_BIG_SYNC_ESTABILISHED] */ | ||||||
|  | 	HCI_LE_EV_VL(HCI_EVT_LE_BIG_SYNC_ESTABILISHED, | ||||||
|  | 		     hci_le_big_sync_established_evt, | ||||||
|  | 		     sizeof(struct hci_evt_le_big_sync_estabilished), | ||||||
|  | 		     HCI_MAX_EVENT_SIZE), | ||||||
|  | 	/* [0x22 = HCI_EVT_LE_BIG_INFO_ADV_REPORT] */ | ||||||
|  | 	HCI_LE_EV_VL(HCI_EVT_LE_BIG_INFO_ADV_REPORT, | ||||||
|  | 		     hci_le_big_info_adv_report_evt, | ||||||
|  | 		     sizeof(struct hci_evt_le_big_info_adv_report), | ||||||
|  | 		     HCI_MAX_EVENT_SIZE), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void hci_le_meta_evt(struct hci_dev *hdev, void *data, | static void hci_le_meta_evt(struct hci_dev *hdev, void *data, | ||||||
|  |  | ||||||
|  | @ -827,7 +827,6 @@ void __hci_req_disable_advertising(struct hci_request *req) | ||||||
| { | { | ||||||
| 	if (ext_adv_capable(req->hdev)) { | 	if (ext_adv_capable(req->hdev)) { | ||||||
| 		__hci_req_disable_ext_adv_instance(req, 0x00); | 		__hci_req_disable_ext_adv_instance(req, 0x00); | ||||||
| 
 |  | ||||||
| 	} else { | 	} else { | ||||||
| 		u8 enable = 0x00; | 		u8 enable = 0x00; | ||||||
| 
 | 
 | ||||||
|  | @ -1338,15 +1337,15 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance) | ||||||
| 	bdaddr_t random_addr; | 	bdaddr_t random_addr; | ||||||
| 	u8 own_addr_type; | 	u8 own_addr_type; | ||||||
| 	int err; | 	int err; | ||||||
| 	struct adv_info *adv_instance; | 	struct adv_info *adv; | ||||||
| 	bool secondary_adv; | 	bool secondary_adv, require_privacy; | ||||||
| 
 | 
 | ||||||
| 	if (instance > 0) { | 	if (instance > 0) { | ||||||
| 		adv_instance = hci_find_adv_instance(hdev, instance); | 		adv = hci_find_adv_instance(hdev, instance); | ||||||
| 		if (!adv_instance) | 		if (!adv) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 	} else { | 	} else { | ||||||
| 		adv_instance = NULL; | 		adv = NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	flags = hci_adv_instance_flags(hdev, instance); | 	flags = hci_adv_instance_flags(hdev, instance); | ||||||
|  | @ -1364,18 +1363,24 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance) | ||||||
| 	 * advertising is used. In that case it is fine to use a | 	 * advertising is used. In that case it is fine to use a | ||||||
| 	 * non-resolvable private address. | 	 * non-resolvable private address. | ||||||
| 	 */ | 	 */ | ||||||
| 	err = hci_get_random_address(hdev, !connectable, | 	require_privacy = !connectable; | ||||||
| 				     adv_use_rpa(hdev, flags), adv_instance, | 
 | ||||||
|  | 	/* Don't require privacy for periodic adv? */ | ||||||
|  | 	if (adv && adv->periodic) | ||||||
|  | 		require_privacy = false; | ||||||
|  | 
 | ||||||
|  | 	err = hci_get_random_address(hdev, require_privacy, | ||||||
|  | 				     adv_use_rpa(hdev, flags), adv, | ||||||
| 				     &own_addr_type, &random_addr); | 				     &own_addr_type, &random_addr); | ||||||
| 	if (err < 0) | 	if (err < 0) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 
 | ||||||
| 	memset(&cp, 0, sizeof(cp)); | 	memset(&cp, 0, sizeof(cp)); | ||||||
| 
 | 
 | ||||||
| 	if (adv_instance) { | 	if (adv) { | ||||||
| 		hci_cpu_to_le24(adv_instance->min_interval, cp.min_interval); | 		hci_cpu_to_le24(adv->min_interval, cp.min_interval); | ||||||
| 		hci_cpu_to_le24(adv_instance->max_interval, cp.max_interval); | 		hci_cpu_to_le24(adv->max_interval, cp.max_interval); | ||||||
| 		cp.tx_power = adv_instance->tx_power; | 		cp.tx_power = adv->tx_power; | ||||||
| 	} else { | 	} else { | ||||||
| 		hci_cpu_to_le24(hdev->le_adv_min_interval, cp.min_interval); | 		hci_cpu_to_le24(hdev->le_adv_min_interval, cp.min_interval); | ||||||
| 		hci_cpu_to_le24(hdev->le_adv_max_interval, cp.max_interval); | 		hci_cpu_to_le24(hdev->le_adv_max_interval, cp.max_interval); | ||||||
|  | @ -1396,7 +1401,8 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance) | ||||||
| 		else | 		else | ||||||
| 			cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_SCAN_IND); | 			cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_SCAN_IND); | ||||||
| 	} else { | 	} else { | ||||||
| 		if (secondary_adv) | 		/* Secondary and periodic cannot use legacy PDUs */ | ||||||
|  | 		if (secondary_adv || (adv && adv->periodic)) | ||||||
| 			cp.evt_properties = cpu_to_le16(LE_EXT_ADV_NON_CONN_IND); | 			cp.evt_properties = cpu_to_le16(LE_EXT_ADV_NON_CONN_IND); | ||||||
| 		else | 		else | ||||||
| 			cp.evt_properties = cpu_to_le16(LE_LEGACY_NONCONN_IND); | 			cp.evt_properties = cpu_to_le16(LE_LEGACY_NONCONN_IND); | ||||||
|  | @ -1426,8 +1432,8 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance) | ||||||
| 		struct hci_cp_le_set_adv_set_rand_addr cp; | 		struct hci_cp_le_set_adv_set_rand_addr cp; | ||||||
| 
 | 
 | ||||||
| 		/* Check if random address need to be updated */ | 		/* Check if random address need to be updated */ | ||||||
| 		if (adv_instance) { | 		if (adv) { | ||||||
| 			if (!bacmp(&random_addr, &adv_instance->random_addr)) | 			if (!bacmp(&random_addr, &adv->random_addr)) | ||||||
| 				return 0; | 				return 0; | ||||||
| 		} else { | 		} else { | ||||||
| 			if (!bacmp(&random_addr, &hdev->random_addr)) | 			if (!bacmp(&random_addr, &hdev->random_addr)) | ||||||
|  |  | ||||||
|  | @ -83,6 +83,9 @@ void __hci_req_enable_advertising(struct hci_request *req); | ||||||
| void __hci_req_disable_advertising(struct hci_request *req); | void __hci_req_disable_advertising(struct hci_request *req); | ||||||
| void __hci_req_update_adv_data(struct hci_request *req, u8 instance); | void __hci_req_update_adv_data(struct hci_request *req, u8 instance); | ||||||
| int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance); | int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance); | ||||||
|  | int hci_req_start_per_adv(struct hci_dev *hdev, u8 instance, u32 flags, | ||||||
|  | 			  u16 min_interval, u16 max_interval, | ||||||
|  | 			  u16 sync_interval); | ||||||
| void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance); | void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance); | ||||||
| 
 | 
 | ||||||
| int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance, | int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance, | ||||||
|  | @ -92,8 +95,14 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk, | ||||||
| 				bool force); | 				bool force); | ||||||
| 
 | 
 | ||||||
| int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance); | int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance); | ||||||
|  | int __hci_req_setup_per_adv_instance(struct hci_request *req, u8 instance, | ||||||
|  | 				     u16 min_interval, u16 max_interval); | ||||||
| int __hci_req_start_ext_adv(struct hci_request *req, u8 instance); | int __hci_req_start_ext_adv(struct hci_request *req, u8 instance); | ||||||
|  | int __hci_req_start_per_adv(struct hci_request *req, u8 instance, u32 flags, | ||||||
|  | 			    u16 min_interval, u16 max_interval, | ||||||
|  | 			    u16 sync_interval); | ||||||
| int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance); | int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance); | ||||||
|  | int __hci_req_enable_per_advertising(struct hci_request *req, u8 instance); | ||||||
| int __hci_req_disable_ext_adv_instance(struct hci_request *req, u8 instance); | int __hci_req_disable_ext_adv_instance(struct hci_request *req, u8 instance); | ||||||
| int __hci_req_remove_ext_adv_instance(struct hci_request *req, u8 instance); | int __hci_req_remove_ext_adv_instance(struct hci_request *req, u8 instance); | ||||||
| void __hci_req_clear_ext_adv_sets(struct hci_request *req); | void __hci_req_clear_ext_adv_sets(struct hci_request *req); | ||||||
|  |  | ||||||
|  | @ -977,6 +977,187 @@ int hci_start_ext_adv_sync(struct hci_dev *hdev, u8 instance) | ||||||
| 	return hci_enable_ext_advertising_sync(hdev, instance); | 	return hci_enable_ext_advertising_sync(hdev, instance); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int hci_disable_per_advertising_sync(struct hci_dev *hdev, u8 instance) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_set_per_adv_enable cp; | ||||||
|  | 
 | ||||||
|  | 	/* If periodic advertising already disabled there is nothing to do. */ | ||||||
|  | 	if (!hci_dev_test_flag(hdev, HCI_LE_PER_ADV)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	memset(&cp, 0, sizeof(cp)); | ||||||
|  | 
 | ||||||
|  | 	cp.enable = 0x00; | ||||||
|  | 	cp.handle = instance; | ||||||
|  | 
 | ||||||
|  | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE, | ||||||
|  | 				     sizeof(cp), &cp, HCI_CMD_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hci_set_per_adv_params_sync(struct hci_dev *hdev, u8 instance, | ||||||
|  | 				       u16 min_interval, u16 max_interval) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_set_per_adv_params cp; | ||||||
|  | 
 | ||||||
|  | 	memset(&cp, 0, sizeof(cp)); | ||||||
|  | 
 | ||||||
|  | 	if (!min_interval) | ||||||
|  | 		min_interval = DISCOV_LE_PER_ADV_INT_MIN; | ||||||
|  | 
 | ||||||
|  | 	if (!max_interval) | ||||||
|  | 		max_interval = DISCOV_LE_PER_ADV_INT_MAX; | ||||||
|  | 
 | ||||||
|  | 	cp.handle = instance; | ||||||
|  | 	cp.min_interval = cpu_to_le16(min_interval); | ||||||
|  | 	cp.max_interval = cpu_to_le16(max_interval); | ||||||
|  | 	cp.periodic_properties = 0x0000; | ||||||
|  | 
 | ||||||
|  | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_PARAMS, | ||||||
|  | 				     sizeof(cp), &cp, HCI_CMD_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) | ||||||
|  | { | ||||||
|  | 	struct { | ||||||
|  | 		struct hci_cp_le_set_per_adv_data cp; | ||||||
|  | 		u8 data[HCI_MAX_PER_AD_LENGTH]; | ||||||
|  | 	} pdu; | ||||||
|  | 	u8 len; | ||||||
|  | 
 | ||||||
|  | 	memset(&pdu, 0, sizeof(pdu)); | ||||||
|  | 
 | ||||||
|  | 	if (instance) { | ||||||
|  | 		struct adv_info *adv = hci_find_adv_instance(hdev, instance); | ||||||
|  | 
 | ||||||
|  | 		if (!adv || !adv->periodic) | ||||||
|  | 			return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	len = eir_create_per_adv_data(hdev, instance, pdu.data); | ||||||
|  | 
 | ||||||
|  | 	pdu.cp.length = len; | ||||||
|  | 	pdu.cp.handle = instance; | ||||||
|  | 	pdu.cp.operation = LE_SET_ADV_DATA_OP_COMPLETE; | ||||||
|  | 
 | ||||||
|  | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA, | ||||||
|  | 				     sizeof(pdu.cp) + len, &pdu, | ||||||
|  | 				     HCI_CMD_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int hci_enable_per_advertising_sync(struct hci_dev *hdev, u8 instance) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_set_per_adv_enable cp; | ||||||
|  | 
 | ||||||
|  | 	/* If periodic advertising already enabled there is nothing to do. */ | ||||||
|  | 	if (hci_dev_test_flag(hdev, HCI_LE_PER_ADV)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	memset(&cp, 0, sizeof(cp)); | ||||||
|  | 
 | ||||||
|  | 	cp.enable = 0x01; | ||||||
|  | 	cp.handle = instance; | ||||||
|  | 
 | ||||||
|  | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE, | ||||||
|  | 				     sizeof(cp), &cp, HCI_CMD_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Checks if periodic advertising data contains a Basic Announcement and if it
 | ||||||
|  |  * does generates a Broadcast ID and add Broadcast Announcement. | ||||||
|  |  */ | ||||||
|  | static int hci_adv_bcast_annoucement(struct hci_dev *hdev, struct adv_info *adv) | ||||||
|  | { | ||||||
|  | 	u8 bid[3]; | ||||||
|  | 	u8 ad[4 + 3]; | ||||||
|  | 
 | ||||||
|  | 	/* Skip if NULL adv as instance 0x00 is used for general purpose
 | ||||||
|  | 	 * advertising so it cannot used for the likes of Broadcast Announcement | ||||||
|  | 	 * as it can be overwritten at any point. | ||||||
|  | 	 */ | ||||||
|  | 	if (!adv) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	/* Check if PA data doesn't contains a Basic Audio Announcement then
 | ||||||
|  | 	 * there is nothing to do. | ||||||
|  | 	 */ | ||||||
|  | 	if (!eir_get_service_data(adv->per_adv_data, adv->per_adv_data_len, | ||||||
|  | 				  0x1851, NULL)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	/* Check if advertising data already has a Broadcast Announcement since
 | ||||||
|  | 	 * the process may want to control the Broadcast ID directly and in that | ||||||
|  | 	 * case the kernel shall no interfere. | ||||||
|  | 	 */ | ||||||
|  | 	if (eir_get_service_data(adv->adv_data, adv->adv_data_len, 0x1852, | ||||||
|  | 				 NULL)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	/* Generate Broadcast ID */ | ||||||
|  | 	get_random_bytes(bid, sizeof(bid)); | ||||||
|  | 	eir_append_service_data(ad, 0, 0x1852, bid, sizeof(bid)); | ||||||
|  | 	hci_set_adv_instance_data(hdev, adv->instance, sizeof(ad), ad, 0, NULL); | ||||||
|  | 
 | ||||||
|  | 	return hci_update_adv_data_sync(hdev, adv->instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 data_len, | ||||||
|  | 			   u8 *data, u32 flags, u16 min_interval, | ||||||
|  | 			   u16 max_interval, u16 sync_interval) | ||||||
|  | { | ||||||
|  | 	struct adv_info *adv = NULL; | ||||||
|  | 	int err; | ||||||
|  | 	bool added = false; | ||||||
|  | 
 | ||||||
|  | 	hci_disable_per_advertising_sync(hdev, instance); | ||||||
|  | 
 | ||||||
|  | 	if (instance) { | ||||||
|  | 		adv = hci_find_adv_instance(hdev, instance); | ||||||
|  | 		/* Create an instance if that could not be found */ | ||||||
|  | 		if (!adv) { | ||||||
|  | 			adv = hci_add_per_instance(hdev, instance, flags, | ||||||
|  | 						   data_len, data, | ||||||
|  | 						   sync_interval, | ||||||
|  | 						   sync_interval); | ||||||
|  | 			if (IS_ERR(adv)) | ||||||
|  | 				return PTR_ERR(adv); | ||||||
|  | 			added = true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Only start advertising if instance 0 or if a dedicated instance has
 | ||||||
|  | 	 * been added. | ||||||
|  | 	 */ | ||||||
|  | 	if (!adv || added) { | ||||||
|  | 		err = hci_start_ext_adv_sync(hdev, instance); | ||||||
|  | 		if (err < 0) | ||||||
|  | 			goto fail; | ||||||
|  | 
 | ||||||
|  | 		err = hci_adv_bcast_annoucement(hdev, adv); | ||||||
|  | 		if (err < 0) | ||||||
|  | 			goto fail; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = hci_set_per_adv_params_sync(hdev, instance, min_interval, | ||||||
|  | 					  max_interval); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
|  | 	err = hci_set_per_adv_data_sync(hdev, instance); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
|  | 	err = hci_enable_per_advertising_sync(hdev, instance); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | 
 | ||||||
|  | fail: | ||||||
|  | 	if (added) | ||||||
|  | 		hci_remove_adv_instance(hdev, instance); | ||||||
|  | 
 | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int hci_start_adv_sync(struct hci_dev *hdev, u8 instance) | static int hci_start_adv_sync(struct hci_dev *hdev, u8 instance) | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
|  | @ -1116,6 +1297,42 @@ int hci_remove_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance, | ||||||
| 					HCI_CMD_TIMEOUT, sk); | 					HCI_CMD_TIMEOUT, sk); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int remove_ext_adv_sync(struct hci_dev *hdev, void *data) | ||||||
|  | { | ||||||
|  | 	struct adv_info *adv = data; | ||||||
|  | 	u8 instance = 0; | ||||||
|  | 
 | ||||||
|  | 	if (adv) | ||||||
|  | 		instance = adv->instance; | ||||||
|  | 
 | ||||||
|  | 	return hci_remove_ext_adv_instance_sync(hdev, instance, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hci_remove_ext_adv_instance(struct hci_dev *hdev, u8 instance) | ||||||
|  | { | ||||||
|  | 	struct adv_info *adv = NULL; | ||||||
|  | 
 | ||||||
|  | 	if (instance) { | ||||||
|  | 		adv = hci_find_adv_instance(hdev, instance); | ||||||
|  | 		if (!adv) | ||||||
|  | 			return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return hci_cmd_sync_queue(hdev, remove_ext_adv_sync, adv, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_term_big cp; | ||||||
|  | 
 | ||||||
|  | 	memset(&cp, 0, sizeof(cp)); | ||||||
|  | 	cp.handle = handle; | ||||||
|  | 	cp.reason = reason; | ||||||
|  | 
 | ||||||
|  | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_TERM_BIG, | ||||||
|  | 				     sizeof(cp), &cp, HCI_CMD_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void cancel_adv_timeout(struct hci_dev *hdev) | static void cancel_adv_timeout(struct hci_dev *hdev) | ||||||
| { | { | ||||||
| 	if (hdev->adv_instance_timeout) { | 	if (hdev->adv_instance_timeout) { | ||||||
|  | @ -2201,7 +2418,8 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev) | ||||||
| 
 | 
 | ||||||
| 	if (list_empty(&hdev->pend_le_conns) && | 	if (list_empty(&hdev->pend_le_conns) && | ||||||
| 	    list_empty(&hdev->pend_le_reports) && | 	    list_empty(&hdev->pend_le_reports) && | ||||||
| 	    !hci_is_adv_monitoring(hdev)) { | 	    !hci_is_adv_monitoring(hdev) && | ||||||
|  | 	    !hci_dev_test_flag(hdev, HCI_PA_SYNC)) { | ||||||
| 		/* If there is no pending LE connections or devices
 | 		/* If there is no pending LE connections or devices
 | ||||||
| 		 * to be scanned for or no ADV monitors, we should stop the | 		 * to be scanned for or no ADV monitors, we should stop the | ||||||
| 		 * background scanning. | 		 * background scanning. | ||||||
|  | @ -3405,6 +3623,13 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) | ||||||
| 			events[3] |= 0x02; /* LE CIS Request */ | 			events[3] |= 0x02; /* LE CIS Request */ | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (bis_capable(hdev)) { | ||||||
|  | 		events[3] |= 0x04;	/* LE Create BIG Complete */ | ||||||
|  | 		events[3] |= 0x08;	/* LE Terminate BIG Complete */ | ||||||
|  | 		events[3] |= 0x10;	/* LE BIG Sync Established */ | ||||||
|  | 		events[3] |= 0x20;	/* LE BIG Sync Loss */ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK, | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK, | ||||||
| 				     sizeof(events), events, HCI_CMD_TIMEOUT); | 				     sizeof(events), events, HCI_CMD_TIMEOUT); | ||||||
| } | } | ||||||
|  | @ -5484,3 +5709,25 @@ int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle) | ||||||
| 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_REMOVE_CIG, sizeof(cp), | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_REMOVE_CIG, sizeof(cp), | ||||||
| 				     &cp, HCI_CMD_TIMEOUT); | 				     &cp, HCI_CMD_TIMEOUT); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | int hci_le_big_terminate_sync(struct hci_dev *hdev, u8 handle) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_big_term_sync cp; | ||||||
|  | 
 | ||||||
|  | 	memset(&cp, 0, sizeof(cp)); | ||||||
|  | 	cp.handle = handle; | ||||||
|  | 
 | ||||||
|  | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_BIG_TERM_SYNC, | ||||||
|  | 				     sizeof(cp), &cp, HCI_CMD_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle) | ||||||
|  | { | ||||||
|  | 	struct hci_cp_le_pa_term_sync cp; | ||||||
|  | 
 | ||||||
|  | 	memset(&cp, 0, sizeof(cp)); | ||||||
|  | 	cp.handle = cpu_to_le16(handle); | ||||||
|  | 
 | ||||||
|  | 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_PA_TERM_SYNC, | ||||||
|  | 				     sizeof(cp), &cp, HCI_CMD_TIMEOUT); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -8158,7 +8158,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, | ||||||
| 	u16 timeout, duration; | 	u16 timeout, duration; | ||||||
| 	unsigned int prev_instance_cnt; | 	unsigned int prev_instance_cnt; | ||||||
| 	u8 schedule_instance = 0; | 	u8 schedule_instance = 0; | ||||||
| 	struct adv_info *next_instance; | 	struct adv_info *adv, *next_instance; | ||||||
| 	int err; | 	int err; | ||||||
| 	struct mgmt_pending_cmd *cmd; | 	struct mgmt_pending_cmd *cmd; | ||||||
| 
 | 
 | ||||||
|  | @ -8209,7 +8209,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, | ||||||
| 
 | 
 | ||||||
| 	prev_instance_cnt = hdev->adv_instance_cnt; | 	prev_instance_cnt = hdev->adv_instance_cnt; | ||||||
| 
 | 
 | ||||||
| 	err = hci_add_adv_instance(hdev, cp->instance, flags, | 	adv = hci_add_adv_instance(hdev, cp->instance, flags, | ||||||
| 				   cp->adv_data_len, cp->data, | 				   cp->adv_data_len, cp->data, | ||||||
| 				   cp->scan_rsp_len, | 				   cp->scan_rsp_len, | ||||||
| 				   cp->data + cp->adv_data_len, | 				   cp->data + cp->adv_data_len, | ||||||
|  | @ -8217,7 +8217,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, | ||||||
| 				   HCI_ADV_TX_POWER_NO_PREFERENCE, | 				   HCI_ADV_TX_POWER_NO_PREFERENCE, | ||||||
| 				   hdev->le_adv_min_interval, | 				   hdev->le_adv_min_interval, | ||||||
| 				   hdev->le_adv_max_interval); | 				   hdev->le_adv_max_interval); | ||||||
| 	if (err < 0) { | 	if (IS_ERR(adv)) { | ||||||
| 		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, | 		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, | ||||||
| 				      MGMT_STATUS_FAILED); | 				      MGMT_STATUS_FAILED); | ||||||
| 		goto unlock; | 		goto unlock; | ||||||
|  | @ -8348,6 +8348,7 @@ static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev, | ||||||
| 	struct mgmt_cp_add_ext_adv_params *cp = data; | 	struct mgmt_cp_add_ext_adv_params *cp = data; | ||||||
| 	struct mgmt_rp_add_ext_adv_params rp; | 	struct mgmt_rp_add_ext_adv_params rp; | ||||||
| 	struct mgmt_pending_cmd *cmd = NULL; | 	struct mgmt_pending_cmd *cmd = NULL; | ||||||
|  | 	struct adv_info *adv; | ||||||
| 	u32 flags, min_interval, max_interval; | 	u32 flags, min_interval, max_interval; | ||||||
| 	u16 timeout, duration; | 	u16 timeout, duration; | ||||||
| 	u8 status; | 	u8 status; | ||||||
|  | @ -8417,11 +8418,11 @@ static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev, | ||||||
| 		   HCI_ADV_TX_POWER_NO_PREFERENCE; | 		   HCI_ADV_TX_POWER_NO_PREFERENCE; | ||||||
| 
 | 
 | ||||||
| 	/* Create advertising instance with no advertising or response data */ | 	/* Create advertising instance with no advertising or response data */ | ||||||
| 	err = hci_add_adv_instance(hdev, cp->instance, flags, | 	adv = hci_add_adv_instance(hdev, cp->instance, flags, 0, NULL, 0, NULL, | ||||||
| 				   0, NULL, 0, NULL, timeout, duration, | 				   timeout, duration, tx_power, min_interval, | ||||||
| 				   tx_power, min_interval, max_interval); | 				   max_interval); | ||||||
| 
 | 
 | ||||||
| 	if (err < 0) { | 	if (IS_ERR(adv)) { | ||||||
| 		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS, | 		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS, | ||||||
| 				      MGMT_STATUS_FAILED); | 				      MGMT_STATUS_FAILED); | ||||||
| 		goto unlock; | 		goto unlock; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Luiz Augusto von Dentz
						Luiz Augusto von Dentz