[PATCH BlueZ v2 07/11] emulator: Add 5.0 feature support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This adds new hciemu for BT 5.0. Also adds extended advertising,
scanning and connection support in btdev and bthost
---
 emulator/btdev.c  | 458 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 emulator/btdev.h  |   1 +
 emulator/bthost.c |  99 ++++++++++++
 emulator/bthost.h |   7 +
 emulator/hciemu.c |   3 +
 emulator/hciemu.h |   1 +
 monitor/bt.h      |   1 +
 7 files changed, 560 insertions(+), 10 deletions(-)

diff --git a/emulator/btdev.c b/emulator/btdev.c
index 69d84a5..c2859a6 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -145,6 +145,8 @@ struct btdev {
 	uint16_t sync_train_interval;
 	uint32_t sync_train_timeout;
 	uint8_t  sync_train_service_data;
+
+	uint16_t le_ext_adv_type;
 };
 
 struct inquiry_data {
@@ -469,6 +471,34 @@ static void set_bredrle_commands(struct btdev *btdev)
 	btdev->commands[32] |= 0x40;	/* Read Local OOB Extended Data */
 }
 
+static void set_bredrle50_commands(struct btdev *btdev)
+{
+	set_bredrle_commands(btdev);
+
+	btdev->commands[36] |= 0x02;	/* LE Set Advertising Set Random Address */
+	btdev->commands[36] |= 0x04;	/* LE Set Extended Advertising Parameters */
+	btdev->commands[36] |= 0x08;	/* LE Set Extended Advertising Data */
+	btdev->commands[36] |= 0x10;	/* LE Set Extended Scan Response Data */
+	btdev->commands[36] |= 0x20;	/* LE Set Extended Advertising Enable */
+	btdev->commands[36] |= 0x40;	/* LE Read Maximum Advertising Data Length */
+	btdev->commands[36] |= 0x80;	/* LE Read Number of Supported Advertising Sets */
+	btdev->commands[37] |= 0x01;	/* LE Remove Advertising Set */
+	btdev->commands[37] |= 0x02;	/* LE Clear Advertising Sets */
+	btdev->commands[37] |= 0x04;	/* LE Set Periodic Advertising Parameters */
+	btdev->commands[37] |= 0x08;	/* LE Set Periodic Advertising Data */
+	btdev->commands[37] |= 0x10;	/* LE Set Periodic Advertising Enable */
+	btdev->commands[37] |= 0x20;	/* LE Set Extended Scan Parameters */
+	btdev->commands[37] |= 0x40;	/* LE Set Extended Scan Enable */
+	btdev->commands[37] |= 0x80;	/* LE Extended Create Connection */
+	btdev->commands[38] |= 0x01;	/* LE Periodic Advertising Create Sync */
+	btdev->commands[38] |= 0x02;	/* LE Periodic Advertising Create Sync Cancel */
+	btdev->commands[38] |= 0x04;	/* LE Periodic Advertising Terminate Sync */
+	btdev->commands[38] |= 0x08;	/* LE Add Device To Periodic Advertiser List */
+	btdev->commands[38] |= 0x10;	/* LE Remove Device From Periodic Advertiser List */
+	btdev->commands[38] |= 0x20;	/* LE Clear Periodic Advertiser List */
+	btdev->commands[38] |= 0x40;	/* LE Read Periodic Advertiser List Size */
+}
+
 static void set_amp_commands(struct btdev *btdev)
 {
 	set_common_commands_all(btdev);
@@ -570,6 +600,15 @@ static void set_le_features(struct btdev *btdev)
 	btdev->le_features[0] |= 0x08;	/* Slave-initiated Features Exchange */
 }
 
+static void set_bredrle50_features(struct btdev *btdev)
+{
+	set_bredrle_features(btdev);
+
+	btdev->le_features[1] |= 0x01;	/* LE 2M PHY */
+	btdev->le_features[1] |= 0x08;	/* LE Coded PHY */
+	btdev->le_features[1] |= 0x10;  /* LE EXT ADV */
+}
+
 static void set_le_states(struct btdev *btdev)
 {
 	/* Set all 41 bits as per Bluetooth 5.0 specification */
@@ -596,7 +635,8 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
 
 	memset(btdev, 0, sizeof(*btdev));
 
-	if (type == BTDEV_TYPE_BREDRLE || type == BTDEV_TYPE_LE) {
+	if (type == BTDEV_TYPE_BREDRLE || type == BTDEV_TYPE_LE
+				|| BTDEV_TYPE_BREDRLE50) {
 		btdev->crypto = bt_crypto_new();
 		if (!btdev->crypto) {
 			free(btdev);
@@ -637,6 +677,12 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
 		set_bredr20_features(btdev);
 		set_bredr20_commands(btdev);
 		break;
+	case BTDEV_TYPE_BREDRLE50:
+		btdev->version = 0x05;
+		set_bredrle50_features(btdev);
+		set_bredrle50_commands(btdev);
+		set_le_states(btdev);
+		break;
 	}
 
 	btdev->page_scan_interval = 0x0800;
@@ -1174,6 +1220,53 @@ static void le_conn_complete(struct btdev *btdev,
 	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
 }
 
+static void le_ext_conn_complete(struct btdev *btdev,
+				const struct bt_hci_cmd_le_ext_create_conn *leecc,
+				uint8_t status)
+{
+	char buf[1 + sizeof(struct bt_hci_evt_le_enhanced_conn_complete)];
+	struct bt_hci_evt_le_enhanced_conn_complete *cc = (void *) &buf[1];
+	struct bt_hci_le_ext_create_conn *lecc = (void *)leecc->data;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE;
+
+	if (!status) {
+		struct btdev *remote;
+
+		remote = find_btdev_by_bdaddr_type(leecc->peer_addr,
+							leecc->peer_addr_type);
+
+		btdev->conn = remote;
+		btdev->le_adv_enable = 0;
+		remote->conn = btdev;
+		remote->le_adv_enable = 0;
+
+		cc->status = status;
+		cc->peer_addr_type = btdev->le_scan_own_addr_type;
+		if (cc->peer_addr_type == 0x01)
+			memcpy(cc->peer_addr, btdev->random_addr, 6);
+		else
+			memcpy(cc->peer_addr, btdev->bdaddr, 6);
+
+		cc->role = 0x01;
+		cc->handle = cpu_to_le16(42);
+		cc->interval = lecc->max_interval;
+		cc->latency = lecc->latency;
+		cc->supv_timeout = lecc->supv_timeout;
+
+		send_event(remote, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
+	}
+
+	cc->status = status;
+	cc->peer_addr_type = leecc->peer_addr_type;
+	memcpy(cc->peer_addr, leecc->peer_addr, 6);
+	cc->role = 0x00;
+
+	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
+}
+
 static const uint8_t *scan_addr(const struct btdev *btdev)
 {
 	if (btdev->le_scan_own_addr_type == 0x01)
@@ -1202,6 +1295,18 @@ static bool adv_match(struct btdev *scan, struct btdev *adv)
 	return !memcmp(scan_addr(scan), adv->le_adv_direct_addr, 6);
 }
 
+static bool ext_adv_match(struct btdev *scan, struct btdev *adv)
+{
+	/* Match everything if this is not directed advertising */
+	if (!(adv->le_ext_adv_type & 0x04))
+		return true;
+
+	if (scan->le_scan_own_addr_type != adv->le_adv_direct_addr_type)
+		return false;
+
+	return !memcmp(scan_addr(scan), adv->le_adv_direct_addr, 6);
+}
+
 static bool adv_connectable(struct btdev *btdev)
 {
 	if (!btdev->le_adv_enable)
@@ -1210,6 +1315,14 @@ static bool adv_connectable(struct btdev *btdev)
 	return btdev->le_adv_type != 0x03;
 }
 
+static bool ext_adv_connectable(struct btdev *btdev)
+{
+	if (!btdev->le_adv_enable)
+		return false;
+
+	return btdev->le_ext_adv_type & 0x01;
+}
+
 static void le_conn_request(struct btdev *btdev,
 				const struct bt_hci_cmd_le_create_conn *lecc)
 {
@@ -1224,6 +1337,20 @@ static void le_conn_request(struct btdev *btdev,
 					BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH);
 }
 
+static void le_ext_conn_request(struct btdev *btdev,
+				const struct bt_hci_cmd_le_ext_create_conn *leecc)
+{
+	struct btdev *remote = find_btdev_by_bdaddr_type(leecc->peer_addr,
+							leecc->peer_addr_type);
+
+	if (remote && ext_adv_connectable(remote) && ext_adv_match(btdev, remote) &&
+				remote->le_adv_own_addr == leecc->peer_addr_type)
+		le_ext_conn_complete(btdev, leecc, 0);
+	else
+		le_ext_conn_complete(btdev, leecc,
+					BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH);
+}
+
 static void conn_request(struct btdev *btdev, const uint8_t *bdaddr)
 {
 	struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
@@ -1851,6 +1978,47 @@ static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote,
 					1 + 10 + meta_event.lar.data_len + 1);
 }
 
+static void le_send_ext_adv_report(struct btdev *btdev,
+					const struct btdev *remote,
+					uint16_t type, bool is_scan_rsp)
+{
+	struct __packed {
+		uint8_t subevent;
+		uint8_t num_reports;
+		union {
+			struct bt_hci_le_ext_adv_report lear;
+			uint8_t raw[24 + 31];
+		};
+	} meta_event;
+
+	meta_event.subevent = BT_HCI_EVT_LE_EXT_ADV_REPORT;
+
+	memset(&meta_event.lear, 0, sizeof(meta_event.lear));
+	meta_event.num_reports = 1;
+	meta_event.lear.event_type = cpu_to_le16(type);
+	meta_event.lear.addr_type = remote->le_adv_own_addr;
+	memcpy(meta_event.lear.addr, adv_addr(remote), 6);
+	meta_event.lear.rssi = 127;
+	meta_event.lear.tx_power = 127;
+	/* Right now we dont care about phy in adv report */
+	meta_event.lear.primary_phy = 0x01;
+	meta_event.lear.secondary_phy = 0x01;
+
+	/* Scan or advertising response */
+	if (is_scan_rsp) {
+		meta_event.lear.data_len = remote->le_scan_data_len;
+		memcpy(meta_event.lear.data, remote->le_scan_data,
+						meta_event.lear.data_len);
+	} else {
+		meta_event.lear.data_len = remote->le_adv_data_len;
+		memcpy(meta_event.lear.data, remote->le_adv_data,
+						meta_event.lear.data_len);
+	}
+
+	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, &meta_event,
+					1 + 1 + 24 + meta_event.lear.data_len);
+}
+
 static uint8_t get_adv_report_type(uint8_t adv_type)
 {
 	/*
@@ -1863,6 +2031,22 @@ static uint8_t get_adv_report_type(uint8_t adv_type)
 	return adv_type;
 }
 
+static uint8_t get_ext_adv_report_type(uint8_t ext_adv_type)
+{
+	/* If legacy bit is not set then just reset high duty cycle directed bit */
+	if (!(ext_adv_type & 0x10))
+		return (ext_adv_type & 0xf7);
+
+	/*
+	 * Connectable low duty cycle directed advertising creates a
+	 * connectable directed advertising report type.
+	 */
+	if (ext_adv_type == 0x001d)
+		return 0x0015;
+
+	return ext_adv_type;
+}
+
 static void le_set_adv_enable_complete(struct btdev *btdev)
 {
 	uint8_t report_type;
@@ -1891,6 +2075,44 @@ static void le_set_adv_enable_complete(struct btdev *btdev)
 	}
 }
 
+static void le_set_ext_adv_enable_complete(struct btdev *btdev)
+{
+	uint16_t report_type;
+	int i;
+
+	report_type = get_ext_adv_report_type(btdev->le_ext_adv_type);
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		if (!btdev_list[i] || btdev_list[i] == btdev)
+			continue;
+
+		if (!btdev_list[i]->le_scan_enable)
+			continue;
+
+		if (!ext_adv_match(btdev_list[i], btdev))
+			continue;
+
+		le_send_ext_adv_report(btdev_list[i], btdev, report_type, false);
+
+		if (btdev_list[i]->le_scan_type != 0x01)
+			continue;
+
+		/* if scannable bit is set the send scan response */
+		if (btdev->le_ext_adv_type & 0x02) {
+			if (btdev->le_ext_adv_type == 0x13)
+				report_type = 0x1b;
+			else if (btdev->le_ext_adv_type == 0x12)
+				report_type = 0x1a;
+			else if (!(btdev->le_ext_adv_type & 0x10))
+				report_type &= 0x08;
+			else
+				continue;
+
+			le_send_ext_adv_report(btdev_list[i], btdev, report_type, true);
+		}
+	}
+}
+
 static void le_set_scan_enable_complete(struct btdev *btdev)
 {
 	int i;
@@ -1920,6 +2142,44 @@ static void le_set_scan_enable_complete(struct btdev *btdev)
 	}
 }
 
+static void le_set_ext_scan_enable_complete(struct btdev *btdev)
+{
+	int i;
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		uint16_t report_type;
+
+		if (!btdev_list[i] || btdev_list[i] == btdev)
+			continue;
+
+		if (!btdev_list[i]->le_adv_enable)
+			continue;
+
+		if (!ext_adv_match(btdev, btdev_list[i]))
+			continue;
+
+		report_type = get_ext_adv_report_type(btdev_list[i]->le_ext_adv_type);
+		le_send_ext_adv_report(btdev, btdev_list[i], report_type, false);
+
+		if (btdev->le_scan_type != 0x01)
+			continue;
+
+				/* if scannable bit is set the send scan response */
+		if (btdev_list[i]->le_ext_adv_type & 0x02) {
+			if (btdev_list[i]->le_ext_adv_type == 0x13)
+				report_type = 0x1b;
+			else if (btdev_list[i]->le_ext_adv_type == 0x12)
+				report_type = 0x1a;
+			else if (!(btdev_list[i]->le_ext_adv_type & 0x10))
+				report_type &= 0x08;
+			else
+				continue;
+
+			le_send_ext_adv_report(btdev, btdev_list[i], report_type, true);
+		}
+	}
+}
+
 static void le_read_remote_features_complete(struct btdev *btdev)
 {
 	char buf[1 + sizeof(struct bt_hci_evt_le_remote_features_complete)];
@@ -2086,6 +2346,15 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 	const struct bt_hci_cmd_read_local_amp_assoc *rlaa_cmd;
 	const struct bt_hci_cmd_read_rssi *rrssi;
 	const struct bt_hci_cmd_read_tx_power *rtxp;
+	const struct bt_hci_cmd_le_set_adv_set_rand_addr *lsasra;
+	const struct bt_hci_cmd_le_set_ext_adv_params *lseap;
+	const struct bt_hci_cmd_le_set_ext_adv_enable *lseae;
+	const struct bt_hci_cmd_le_set_ext_adv_data *lsead;
+	const struct bt_hci_cmd_le_set_ext_scan_rsp_data *lsesrd;
+	const struct bt_hci_cmd_le_set_default_phy *phys;
+	const struct bt_hci_cmd_le_set_ext_scan_params *lsesp;
+	const struct bt_hci_le_scan_phy *lsp;
+	const struct bt_hci_cmd_le_set_ext_scan_enable *lsese;
 	struct bt_hci_rsp_read_default_link_policy rdlp;
 	struct bt_hci_rsp_read_stored_link_key rslk;
 	struct bt_hci_rsp_write_stored_link_key wslk;
@@ -2145,6 +2414,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 	struct bt_hci_rsp_read_tx_power rtxp_rsp;
 	struct bt_hci_evt_le_read_local_pk256_complete pk_evt;
 	struct bt_hci_evt_le_generate_dhkey_complete dh_evt;
+	struct bt_hci_rsp_le_read_num_supported_adv_sets rlrnsas;
+	struct bt_hci_rsp_le_set_ext_adv_params rlseap;
 	uint8_t status, page;
 
 	switch (opcode) {
@@ -2679,7 +2950,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		break;
 
 	case BT_HCI_CMD_READ_LE_HOST_SUPPORTED:
-		if (btdev->type != BTDEV_TYPE_BREDRLE)
+		if (btdev->type != BTDEV_TYPE_BREDRLE &&
+				btdev->type != BTDEV_TYPE_BREDRLE50)
 			goto unsupported;
 		rlhs.status = BT_HCI_ERR_SUCCESS;
 		rlhs.supported = btdev->le_supported;
@@ -2688,7 +2960,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		break;
 
 	case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED:
-		if (btdev->type != BTDEV_TYPE_BREDRLE)
+		if (btdev->type != BTDEV_TYPE_BREDRLE &&
+				btdev->type != BTDEV_TYPE_BREDRLE50)
 			goto unsupported;
 		wlhs = data;
 		btdev->le_supported = wlhs->supported;
@@ -2698,7 +2971,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		break;
 
 	case BT_HCI_CMD_READ_SECURE_CONN_SUPPORT:
-		if (btdev->type != BTDEV_TYPE_BREDRLE)
+		if (btdev->type != BTDEV_TYPE_BREDRLE &&
+				btdev->type != BTDEV_TYPE_BREDRLE50)
 			goto unsupported;
 		rscs.status = BT_HCI_ERR_SUCCESS;
 		rscs.support = btdev->secure_conn_support;
@@ -2706,7 +2980,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		break;
 
 	case BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT:
-		if (btdev->type != BTDEV_TYPE_BREDRLE)
+		if (btdev->type != BTDEV_TYPE_BREDRLE &&
+				btdev->type != BTDEV_TYPE_BREDRLE50)
 			goto unsupported;
 		wscs = data;
 		btdev->secure_conn_support = wscs->support;
@@ -2715,14 +2990,16 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		break;
 
 	case BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA:
-		if (btdev->type != BTDEV_TYPE_BREDRLE)
+		if (btdev->type != BTDEV_TYPE_BREDRLE &&
+				btdev->type != BTDEV_TYPE_BREDRLE50)
 			goto unsupported;
 		rloed.status = BT_HCI_ERR_SUCCESS;
 		cmd_complete(btdev, opcode, &rloed, sizeof(rloed));
 		break;
 
 	case BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS:
-		if (btdev->type != BTDEV_TYPE_BREDRLE)
+		if (btdev->type != BTDEV_TYPE_BREDRLE &&
+				btdev->type != BTDEV_TYPE_BREDRLE50)
 			goto unsupported;
 		rstp.status = BT_HCI_ERR_SUCCESS;
 		rstp.interval = cpu_to_le16(btdev->sync_train_interval);
@@ -2872,7 +3149,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 
 	case BT_HCI_CMD_READ_ENCRYPT_KEY_SIZE:
 		if (btdev->type != BTDEV_TYPE_BREDRLE &&
-					btdev->type != BTDEV_TYPE_BREDR)
+					btdev->type != BTDEV_TYPE_BREDR &&
+					btdev->type != BTDEV_TYPE_BREDRLE50)
 			goto unsupported;
 		reks = data;
 		read_enc_key_size_complete(btdev, le16_to_cpu(reks->handle));
@@ -2918,7 +3196,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		break;
 
 	case BT_HCI_CMD_SET_EVENT_MASK_PAGE2:
-		if (btdev->type != BTDEV_TYPE_BREDRLE)
+		if (btdev->type != BTDEV_TYPE_BREDRLE &&
+				btdev->type != BTDEV_TYPE_BREDRLE50)
 			goto unsupported;
 		semp2 = data;
 		memcpy(btdev->event_mask_page2, semp2->mask, 8);
@@ -3245,6 +3524,149 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		lcprnr_rsp.status = BT_HCI_ERR_SUCCESS;
 		cmd_complete(btdev, opcode, &lcprnr_rsp, sizeof(lcprnr_rsp));
 		break;
+	case BT_HCI_CMD_LE_READ_NUM_SUPPORTED_ADV_SETS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		rlrnsas.status = BT_HCI_ERR_SUCCESS;
+		/* Support one set as of now */
+		rlrnsas.num_of_sets = 1;
+		cmd_complete(btdev, opcode, &rlrnsas, sizeof(rlrnsas));
+		break;
+	case BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		lsasra = data;
+		memcpy(btdev->random_addr, lsasra->bdaddr, 6);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		if (btdev->le_adv_enable) {
+			status = BT_HCI_ERR_COMMAND_DISALLOWED;
+			cmd_complete(btdev, opcode, &status, sizeof(status));
+			break;
+		}
+
+		lseap = data;
+		btdev->le_ext_adv_type = le16_to_cpu(lseap->evt_properties);
+		btdev->le_adv_own_addr = lseap->own_addr_type;
+		btdev->le_adv_direct_addr_type = lseap->peer_addr_type;
+		memcpy(btdev->le_adv_direct_addr, lseap->peer_addr, 6);
+
+		rlseap.status = BT_HCI_ERR_SUCCESS;
+		rlseap.tx_power = 0;
+		cmd_complete(btdev, opcode, &rlseap, sizeof(rlseap));
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		lseae = data;
+		if (btdev->le_adv_enable == lseae->enable)
+			status = BT_HCI_ERR_COMMAND_DISALLOWED;
+		else {
+			btdev->le_adv_enable = lseae->enable;
+			status = BT_HCI_ERR_SUCCESS;
+		}
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		if (status == BT_HCI_ERR_SUCCESS && btdev->le_adv_enable)
+			le_set_ext_adv_enable_complete(btdev);
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_ADV_DATA:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		lsead = data;
+		btdev->le_adv_data_len = lsead->data_len;
+		memcpy(btdev->le_adv_data, lsead->data, 31);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_SCAN_RSP_DATA:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		lsesrd = data;
+		btdev->le_scan_data_len = lsesrd->data_len;
+		memcpy(btdev->le_scan_data, lsesrd->data, 31);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+	case BT_HCI_CMD_LE_REMOVE_ADV_SET:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+	case BT_HCI_CMD_LE_CLEAR_ADV_SETS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+	case BT_HCI_CMD_LE_SET_DEFAULT_PHY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		phys = data;
+		if (phys->all_phys > 0x03 ||
+			(!(phys->all_phys & 0x01) &&
+				(!phys->tx_phys || phys->tx_phys > 0x07)) ||
+			(!(phys->all_phys & 0x02) &&
+				(!phys->rx_phys || phys->rx_phys > 0x07)))
+			status = BT_HCI_ERR_INVALID_PARAMETERS;
+		else
+			status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_SCAN_PARAMS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		lsesp = data;
+		lsp = (void *)lsesp->data;
+
+		if (btdev->le_scan_enable)
+			status = BT_HCI_ERR_COMMAND_DISALLOWED;
+		else if (lsesp->num_phys == 0)
+			status = BT_HCI_ERR_INVALID_PARAMETERS;
+		else {
+			status = BT_HCI_ERR_SUCCESS;
+			/* Currently we dont support multiple types in single command
+			 * So just take the first one will do.
+			 */
+			btdev->le_scan_type = lsp->type;
+			btdev->le_scan_own_addr_type = lsesp->own_addr_type;
+		}
+
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		lsese = data;
+		if (btdev->le_scan_enable == lsese->enable)
+			status = BT_HCI_ERR_COMMAND_DISALLOWED;
+		else {
+			btdev->le_scan_enable = lsese->enable;
+			btdev->le_filter_dup = lsese->filter_dup;
+			status = BT_HCI_ERR_SUCCESS;
+		}
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+	case BT_HCI_CMD_LE_EXT_CREATE_CONN:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			goto unsupported;
+
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
 	default:
 		goto unsupported;
 	}
@@ -3282,6 +3704,8 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
 	const struct bt_hci_cmd_le_conn_param_req_reply *lcprr;
 	const struct bt_hci_cmd_le_conn_param_req_neg_reply *lcprnr;
 	const struct bt_hci_cmd_le_set_scan_enable *lsse;
+	const struct bt_hci_cmd_le_set_ext_scan_enable *lsese;
+	const struct bt_hci_cmd_le_ext_create_conn *leecc;
 
 	switch (opcode) {
 	case BT_HCI_CMD_INQUIRY:
@@ -3476,7 +3900,21 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
 		lsse = data;
 		if (btdev->le_scan_enable && lsse->enable)
 			le_set_scan_enable_complete(btdev);
-
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			return;
+		lsese = data;
+		if (btdev->le_scan_enable && lsese->enable)
+			le_set_ext_scan_enable_complete(btdev);
+		break;
+	case BT_HCI_CMD_LE_EXT_CREATE_CONN:
+		if (btdev->type != BTDEV_TYPE_BREDRLE50)
+			return;
+		leecc = data;
+		btdev->le_scan_own_addr_type = leecc->own_addr_type;
+		le_ext_conn_request(btdev, leecc);
+		break;
 	}
 }
 
diff --git a/emulator/btdev.h b/emulator/btdev.h
index ba06a10..362d1e7 100644
--- a/emulator/btdev.h
+++ b/emulator/btdev.h
@@ -63,6 +63,7 @@ enum btdev_type {
 	BTDEV_TYPE_LE,
 	BTDEV_TYPE_AMP,
 	BTDEV_TYPE_BREDR20,
+	BTDEV_TYPE_BREDRLE50,
 };
 
 enum btdev_hook_type {
diff --git a/emulator/bthost.c b/emulator/bthost.c
index 2bcdc31..d37957f 100644
--- a/emulator/bthost.c
+++ b/emulator/bthost.c
@@ -839,6 +839,12 @@ static void evt_cmd_complete(struct bthost *bthost, const void *data,
 		break;
 	case BT_HCI_CMD_LE_SET_ADV_DATA:
 		break;
+	case BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS:
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_ADV_DATA:
+		break;
+	case BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE:
+		break;
 	default:
 		printf("Unhandled cmd_complete opcode 0x%04x\n", opcode);
 		break;
@@ -1161,6 +1167,26 @@ static void evt_le_conn_complete(struct bthost *bthost, const void *data,
 	init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type);
 }
 
+static void evt_le_ext_conn_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_le_enhanced_conn_complete *ev = data;
+	uint8_t addr_type;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+
+	if (ev->peer_addr_type == 0x00)
+		addr_type = BDADDR_LE_PUBLIC;
+	else
+		addr_type = BDADDR_LE_RANDOM;
+
+	init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type);
+}
+
 static void evt_le_conn_update_complete(struct bthost *bthost, const void *data,
 								uint8_t len)
 {
@@ -1240,6 +1266,9 @@ static void evt_le_meta_event(struct bthost *bthost, const void *data,
 	case BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST:
 		evt_le_ltk_request(bthost, evt_data, len - 1);
 		break;
+	case BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE:
+		evt_le_ext_conn_complete(bthost, evt_data, len - 1);
+		break;
 	default:
 		printf("Unsupported LE Meta event 0x%2.2x\n", *event);
 		break;
@@ -2269,6 +2298,38 @@ void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr,
 	}
 }
 
+void bthost_hci_ext_connect(struct bthost *bthost, const uint8_t *bdaddr,
+							uint8_t addr_type)
+{
+	struct bt_hci_cmd_le_ext_create_conn *cc;
+	struct bt_hci_le_ext_create_conn *cp;
+	uint8_t buf[sizeof(*cc) + sizeof(*cp)];
+
+	cc = (void *)buf;
+	cp = (void *)cc->data;
+
+	bthost->conn_init = true;
+
+	memset(cc, 0, sizeof(*cc));
+	memset(cp, 0, sizeof(*cp));
+
+	memcpy(cc->peer_addr, bdaddr, sizeof(cc->peer_addr));
+
+	if (addr_type == BDADDR_LE_RANDOM)
+		cc->peer_addr_type = 0x01;
+
+	cc->phys = 0x01;
+
+	cp->scan_interval = cpu_to_le16(0x0060);
+	cp->scan_window = cpu_to_le16(0x0030);
+	cp->min_interval = cpu_to_le16(0x0028);
+	cp->max_interval = cpu_to_le16(0x0038);
+	cp->supv_timeout = cpu_to_le16(0x002a);
+
+	send_command(bthost, BT_HCI_CMD_LE_EXT_CREATE_CONN,
+						buf, sizeof(buf));
+}
+
 void bthost_hci_disconnect(struct bthost *bthost, uint16_t handle,
 								uint8_t reason)
 {
@@ -2301,6 +2362,29 @@ void bthost_set_adv_data(struct bthost *bthost, const uint8_t *data,
 							sizeof(adv_cp));
 }
 
+void bthost_set_ext_adv_data(struct bthost *bthost, const uint8_t *data,
+								uint8_t len)
+{
+	struct bt_hci_cmd_le_set_ext_adv_data *adv_cp;
+	uint8_t buf[sizeof(*adv_cp) + 31];
+
+	adv_cp = (void *)buf;
+
+	memset(adv_cp, 0, sizeof(*adv_cp));
+	memset(adv_cp->data, 0, 31);
+
+	adv_cp->operation = 0x03;
+	adv_cp->fragment_preference = 0x01;
+
+	if (len) {
+		adv_cp->data_len = len;
+		memcpy(adv_cp->data, data, len);
+	}
+
+	send_command(bthost, BT_HCI_CMD_LE_SET_EXT_ADV_DATA, buf,
+							sizeof(buf));
+}
+
 void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable)
 {
 	struct bt_hci_cmd_le_set_adv_parameters cp;
@@ -2312,6 +2396,21 @@ void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable)
 	send_command(bthost, BT_HCI_CMD_LE_SET_ADV_ENABLE, &enable, 1);
 }
 
+void bthost_set_ext_adv_enable(struct bthost *bthost, uint8_t enable)
+{
+	struct bt_hci_cmd_le_set_ext_adv_params cp;
+	struct bt_hci_cmd_le_set_ext_adv_enable cp_enable;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.evt_properties = cpu_to_le16(0x0013);
+	send_command(bthost, BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS,
+							&cp, sizeof(cp));
+
+	cp_enable.enable = enable;
+	send_command(bthost, BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE, &cp_enable,
+					sizeof(cp_enable));
+}
+
 void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode)
 {
 	send_command(bthost, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &mode, 1);
diff --git a/emulator/bthost.h b/emulator/bthost.h
index 553865a..b5f3696 100644
--- a/emulator/bthost.h
+++ b/emulator/bthost.h
@@ -56,6 +56,9 @@ void bthost_set_connect_cb(struct bthost *bthost, bthost_new_conn_cb cb,
 void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr,
 							uint8_t addr_type);
 
+void bthost_hci_ext_connect(struct bthost *bthost, const uint8_t *bdaddr,
+							uint8_t addr_type);
+
 void bthost_hci_disconnect(struct bthost *bthost, uint16_t handle,
 								uint8_t reason);
 
@@ -83,6 +86,10 @@ void bthost_set_adv_data(struct bthost *bthost, const uint8_t *data,
 								uint8_t len);
 void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable);
 
+void bthost_set_ext_adv_data(struct bthost *bthost, const uint8_t *data,
+								uint8_t len);
+void bthost_set_ext_adv_enable(struct bthost *bthost, uint8_t enable);
+
 void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode);
 
 void bthost_write_le_host_supported(struct bthost *bthost, uint8_t mode);
diff --git a/emulator/hciemu.c b/emulator/hciemu.c
index 1787a6c..bc36773 100644
--- a/emulator/hciemu.c
+++ b/emulator/hciemu.c
@@ -331,6 +331,9 @@ struct hciemu *hciemu_new(enum hciemu_type type)
 	case HCIEMU_TYPE_LEGACY:
 		hciemu->btdev_type = BTDEV_TYPE_BREDR20;
 		break;
+	case HCIEMU_TYPE_BREDRLE50:
+		hciemu->btdev_type = BTDEV_TYPE_BREDRLE50;
+		break;
 	default:
 		return NULL;
 	}
diff --git a/emulator/hciemu.h b/emulator/hciemu.h
index 5c0c4c3..e37c069 100644
--- a/emulator/hciemu.h
+++ b/emulator/hciemu.h
@@ -31,6 +31,7 @@ enum hciemu_type {
 	HCIEMU_TYPE_BREDR,
 	HCIEMU_TYPE_LE,
 	HCIEMU_TYPE_LEGACY,
+	HCIEMU_TYPE_BREDRLE50,
 };
 
 enum hciemu_hook_type {
diff --git a/monitor/bt.h b/monitor/bt.h
index 595b6a7..ec62864 100644
--- a/monitor/bt.h
+++ b/monitor/bt.h
@@ -3010,6 +3010,7 @@ struct bt_hci_le_ext_adv_report {
 	uint8_t  direct_addr_type;
 	uint8_t  direct_addr[6];
 	uint8_t  data_len;
+	uint8_t  data[0];
 } __attribute__ ((packed));
 
 #define BT_HCI_EVT_LE_ADV_SET_TERM		0x12
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux