diff options
Diffstat (limited to 'net/bluetooth/mgmt.c')
| -rw-r--r-- | net/bluetooth/mgmt.c | 1730 | 
1 files changed, 1079 insertions, 651 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 2c763429686..2540944d871 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -22,6 +22,7 @@  /* Bluetooth HCI Management interface */ +#include <linux/kernel.h>  #include <linux/uaccess.h>  #include <linux/module.h>  #include <asm/unaligned.h> @@ -29,26 +30,103 @@  #include <net/bluetooth/bluetooth.h>  #include <net/bluetooth/hci_core.h>  #include <net/bluetooth/mgmt.h> +#include <net/bluetooth/smp.h>  #define MGMT_VERSION	0  #define MGMT_REVISION	1 +#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */ + +#define SERVICE_CACHE_TIMEOUT (5 * 1000) +  struct pending_cmd {  	struct list_head list; -	__u16 opcode; +	u16 opcode;  	int index;  	void *param;  	struct sock *sk;  	void *user_data;  }; -static LIST_HEAD(cmd_list); +/* HCI to MGMT error code conversion table */ +static u8 mgmt_status_table[] = { +	MGMT_STATUS_SUCCESS, +	MGMT_STATUS_UNKNOWN_COMMAND,	/* Unknown Command */ +	MGMT_STATUS_NOT_CONNECTED,	/* No Connection */ +	MGMT_STATUS_FAILED,		/* Hardware Failure */ +	MGMT_STATUS_CONNECT_FAILED,	/* Page Timeout */ +	MGMT_STATUS_AUTH_FAILED,	/* Authentication Failed */ +	MGMT_STATUS_NOT_PAIRED,		/* PIN or Key Missing */ +	MGMT_STATUS_NO_RESOURCES,	/* Memory Full */ +	MGMT_STATUS_TIMEOUT,		/* Connection Timeout */ +	MGMT_STATUS_NO_RESOURCES,	/* Max Number of Connections */ +	MGMT_STATUS_NO_RESOURCES,	/* Max Number of SCO Connections */ +	MGMT_STATUS_ALREADY_CONNECTED,	/* ACL Connection Exists */ +	MGMT_STATUS_BUSY,		/* Command Disallowed */ +	MGMT_STATUS_NO_RESOURCES,	/* Rejected Limited Resources */ +	MGMT_STATUS_REJECTED,		/* Rejected Security */ +	MGMT_STATUS_REJECTED,		/* Rejected Personal */ +	MGMT_STATUS_TIMEOUT,		/* Host Timeout */ +	MGMT_STATUS_NOT_SUPPORTED,	/* Unsupported Feature */ +	MGMT_STATUS_INVALID_PARAMS,	/* Invalid Parameters */ +	MGMT_STATUS_DISCONNECTED,	/* OE User Ended Connection */ +	MGMT_STATUS_NO_RESOURCES,	/* OE Low Resources */ +	MGMT_STATUS_DISCONNECTED,	/* OE Power Off */ +	MGMT_STATUS_DISCONNECTED,	/* Connection Terminated */ +	MGMT_STATUS_BUSY,		/* Repeated Attempts */ +	MGMT_STATUS_REJECTED,		/* Pairing Not Allowed */ +	MGMT_STATUS_FAILED,		/* Unknown LMP PDU */ +	MGMT_STATUS_NOT_SUPPORTED,	/* Unsupported Remote Feature */ +	MGMT_STATUS_REJECTED,		/* SCO Offset Rejected */ +	MGMT_STATUS_REJECTED,		/* SCO Interval Rejected */ +	MGMT_STATUS_REJECTED,		/* Air Mode Rejected */ +	MGMT_STATUS_INVALID_PARAMS,	/* Invalid LMP Parameters */ +	MGMT_STATUS_FAILED,		/* Unspecified Error */ +	MGMT_STATUS_NOT_SUPPORTED,	/* Unsupported LMP Parameter Value */ +	MGMT_STATUS_FAILED,		/* Role Change Not Allowed */ +	MGMT_STATUS_TIMEOUT,		/* LMP Response Timeout */ +	MGMT_STATUS_FAILED,		/* LMP Error Transaction Collision */ +	MGMT_STATUS_FAILED,		/* LMP PDU Not Allowed */ +	MGMT_STATUS_REJECTED,		/* Encryption Mode Not Accepted */ +	MGMT_STATUS_FAILED,		/* Unit Link Key Used */ +	MGMT_STATUS_NOT_SUPPORTED,	/* QoS Not Supported */ +	MGMT_STATUS_TIMEOUT,		/* Instant Passed */ +	MGMT_STATUS_NOT_SUPPORTED,	/* Pairing Not Supported */ +	MGMT_STATUS_FAILED,		/* Transaction Collision */ +	MGMT_STATUS_INVALID_PARAMS,	/* Unacceptable Parameter */ +	MGMT_STATUS_REJECTED,		/* QoS Rejected */ +	MGMT_STATUS_NOT_SUPPORTED,	/* Classification Not Supported */ +	MGMT_STATUS_REJECTED,		/* Insufficient Security */ +	MGMT_STATUS_INVALID_PARAMS,	/* Parameter Out Of Range */ +	MGMT_STATUS_BUSY,		/* Role Switch Pending */ +	MGMT_STATUS_FAILED,		/* Slot Violation */ +	MGMT_STATUS_FAILED,		/* Role Switch Failed */ +	MGMT_STATUS_INVALID_PARAMS,	/* EIR Too Large */ +	MGMT_STATUS_NOT_SUPPORTED,	/* Simple Pairing Not Supported */ +	MGMT_STATUS_BUSY,		/* Host Busy Pairing */ +	MGMT_STATUS_REJECTED,		/* Rejected, No Suitable Channel */ +	MGMT_STATUS_BUSY,		/* Controller Busy */ +	MGMT_STATUS_INVALID_PARAMS,	/* Unsuitable Connection Interval */ +	MGMT_STATUS_TIMEOUT,		/* Directed Advertising Timeout */ +	MGMT_STATUS_AUTH_FAILED,	/* Terminated Due to MIC Failure */ +	MGMT_STATUS_CONNECT_FAILED,	/* Connection Establishment Failed */ +	MGMT_STATUS_CONNECT_FAILED,	/* MAC Connection Failed */ +}; + +static u8 mgmt_status(u8 hci_status) +{ +	if (hci_status < ARRAY_SIZE(mgmt_status_table)) +		return mgmt_status_table[hci_status]; + +	return MGMT_STATUS_FAILED; +}  static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)  {  	struct sk_buff *skb;  	struct mgmt_hdr *hdr;  	struct mgmt_ev_cmd_status *ev; +	int err;  	BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); @@ -66,10 +144,11 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)  	ev->status = status;  	put_unaligned_le16(cmd, &ev->opcode); -	if (sock_queue_rcv_skb(sk, skb) < 0) +	err = sock_queue_rcv_skb(sk, skb); +	if (err < 0)  		kfree_skb(skb); -	return 0; +	return err;  }  static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, @@ -78,6 +157,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,  	struct sk_buff *skb;  	struct mgmt_hdr *hdr;  	struct mgmt_ev_cmd_complete *ev; +	int err;  	BT_DBG("sock %p", sk); @@ -97,10 +177,11 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,  	if (rp)  		memcpy(ev->data, rp, rp_len); -	if (sock_queue_rcv_skb(sk, skb) < 0) +	err = sock_queue_rcv_skb(sk, skb); +	if (err < 0)  		kfree_skb(skb); -	return 0; +	return err;;  }  static int read_version(struct sock *sk) @@ -120,6 +201,7 @@ static int read_index_list(struct sock *sk)  {  	struct mgmt_rp_read_index_list *rp;  	struct list_head *p; +	struct hci_dev *d;  	size_t rp_len;  	u16 count;  	int i, err; @@ -143,10 +225,9 @@ static int read_index_list(struct sock *sk)  	put_unaligned_le16(count, &rp->num_controllers);  	i = 0; -	list_for_each(p, &hci_dev_list) { -		struct hci_dev *d = list_entry(p, struct hci_dev, list); - -		hci_del_off_timer(d); +	list_for_each_entry(d, &hci_dev_list, list) { +		if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags)) +			cancel_delayed_work(&d->power_off);  		if (test_bit(HCI_SETUP, &d->flags))  			continue; @@ -165,6 +246,262 @@ static int read_index_list(struct sock *sk)  	return err;  } +static u32 get_supported_settings(struct hci_dev *hdev) +{ +	u32 settings = 0; + +	settings |= MGMT_SETTING_POWERED; +	settings |= MGMT_SETTING_CONNECTABLE; +	settings |= MGMT_SETTING_FAST_CONNECTABLE; +	settings |= MGMT_SETTING_DISCOVERABLE; +	settings |= MGMT_SETTING_PAIRABLE; + +	if (hdev->features[6] & LMP_SIMPLE_PAIR) +		settings |= MGMT_SETTING_SSP; + +	if (!(hdev->features[4] & LMP_NO_BREDR)) { +		settings |= MGMT_SETTING_BREDR; +		settings |= MGMT_SETTING_LINK_SECURITY; +	} + +	if (hdev->features[4] & LMP_LE) +		settings |= MGMT_SETTING_LE; + +	return settings; +} + +static u32 get_current_settings(struct hci_dev *hdev) +{ +	u32 settings = 0; + +	if (test_bit(HCI_UP, &hdev->flags)) +		settings |= MGMT_SETTING_POWERED; +	else +		return settings; + +	if (test_bit(HCI_PSCAN, &hdev->flags)) +		settings |= MGMT_SETTING_CONNECTABLE; + +	if (test_bit(HCI_ISCAN, &hdev->flags)) +		settings |= MGMT_SETTING_DISCOVERABLE; + +	if (test_bit(HCI_PAIRABLE, &hdev->flags)) +		settings |= MGMT_SETTING_PAIRABLE; + +	if (!(hdev->features[4] & LMP_NO_BREDR)) +		settings |= MGMT_SETTING_BREDR; + +	if (hdev->extfeatures[0] & LMP_HOST_LE) +		settings |= MGMT_SETTING_LE; + +	if (test_bit(HCI_AUTH, &hdev->flags)) +		settings |= MGMT_SETTING_LINK_SECURITY; + +	if (hdev->ssp_mode > 0) +		settings |= MGMT_SETTING_SSP; + +	return settings; +} + +#define EIR_FLAGS		0x01 /* flags */ +#define EIR_UUID16_SOME		0x02 /* 16-bit UUID, more available */ +#define EIR_UUID16_ALL		0x03 /* 16-bit UUID, all listed */ +#define EIR_UUID32_SOME		0x04 /* 32-bit UUID, more available */ +#define EIR_UUID32_ALL		0x05 /* 32-bit UUID, all listed */ +#define EIR_UUID128_SOME	0x06 /* 128-bit UUID, more available */ +#define EIR_UUID128_ALL		0x07 /* 128-bit UUID, all listed */ +#define EIR_NAME_SHORT		0x08 /* shortened local name */ +#define EIR_NAME_COMPLETE	0x09 /* complete local name */ +#define EIR_TX_POWER		0x0A /* transmit power level */ +#define EIR_DEVICE_ID		0x10 /* device ID */ + +#define PNP_INFO_SVCLASS_ID		0x1200 + +static u8 bluetooth_base_uuid[] = { +			0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, +			0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static u16 get_uuid16(u8 *uuid128) +{ +	u32 val; +	int i; + +	for (i = 0; i < 12; i++) { +		if (bluetooth_base_uuid[i] != uuid128[i]) +			return 0; +	} + +	memcpy(&val, &uuid128[12], 4); + +	val = le32_to_cpu(val); +	if (val > 0xffff) +		return 0; + +	return (u16) val; +} + +static void create_eir(struct hci_dev *hdev, u8 *data) +{ +	u8 *ptr = data; +	u16 eir_len = 0; +	u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; +	int i, truncated = 0; +	struct bt_uuid *uuid; +	size_t name_len; + +	name_len = strlen(hdev->dev_name); + +	if (name_len > 0) { +		/* EIR Data type */ +		if (name_len > 48) { +			name_len = 48; +			ptr[1] = EIR_NAME_SHORT; +		} else +			ptr[1] = EIR_NAME_COMPLETE; + +		/* EIR Data length */ +		ptr[0] = name_len + 1; + +		memcpy(ptr + 2, hdev->dev_name, name_len); + +		eir_len += (name_len + 2); +		ptr += (name_len + 2); +	} + +	memset(uuid16_list, 0, sizeof(uuid16_list)); + +	/* Group all UUID16 types */ +	list_for_each_entry(uuid, &hdev->uuids, list) { +		u16 uuid16; + +		uuid16 = get_uuid16(uuid->uuid); +		if (uuid16 == 0) +			return; + +		if (uuid16 < 0x1100) +			continue; + +		if (uuid16 == PNP_INFO_SVCLASS_ID) +			continue; + +		/* Stop if not enough space to put next UUID */ +		if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { +			truncated = 1; +			break; +		} + +		/* Check for duplicates */ +		for (i = 0; uuid16_list[i] != 0; i++) +			if (uuid16_list[i] == uuid16) +				break; + +		if (uuid16_list[i] == 0) { +			uuid16_list[i] = uuid16; +			eir_len += sizeof(u16); +		} +	} + +	if (uuid16_list[0] != 0) { +		u8 *length = ptr; + +		/* EIR Data type */ +		ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; + +		ptr += 2; +		eir_len += 2; + +		for (i = 0; uuid16_list[i] != 0; i++) { +			*ptr++ = (uuid16_list[i] & 0x00ff); +			*ptr++ = (uuid16_list[i] & 0xff00) >> 8; +		} + +		/* EIR Data length */ +		*length = (i * sizeof(u16)) + 1; +	} +} + +static int update_eir(struct hci_dev *hdev) +{ +	struct hci_cp_write_eir cp; + +	if (!(hdev->features[6] & LMP_EXT_INQ)) +		return 0; + +	if (hdev->ssp_mode == 0) +		return 0; + +	if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) +		return 0; + +	memset(&cp, 0, sizeof(cp)); + +	create_eir(hdev, cp.data); + +	if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) +		return 0; + +	memcpy(hdev->eir, cp.data, sizeof(cp.data)); + +	return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); +} + +static u8 get_service_classes(struct hci_dev *hdev) +{ +	struct bt_uuid *uuid; +	u8 val = 0; + +	list_for_each_entry(uuid, &hdev->uuids, list) +		val |= uuid->svc_hint; + +	return val; +} + +static int update_class(struct hci_dev *hdev) +{ +	u8 cod[3]; + +	BT_DBG("%s", hdev->name); + +	if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) +		return 0; + +	cod[0] = hdev->minor_class; +	cod[1] = hdev->major_class; +	cod[2] = get_service_classes(hdev); + +	if (memcmp(cod, hdev->dev_class, 3) == 0) +		return 0; + +	return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); +} + +static void service_cache_off(struct work_struct *work) +{ +	struct hci_dev *hdev = container_of(work, struct hci_dev, +							service_cache.work); + +	if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags)) +		return; + +	hci_dev_lock(hdev); + +	update_eir(hdev); +	update_class(hdev); + +	hci_dev_unlock(hdev); +} + +static void mgmt_init_hdev(struct hci_dev *hdev) +{ +	if (!test_and_set_bit(HCI_MGMT, &hdev->flags)) +		INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); + +	if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->flags)) +		schedule_delayed_work(&hdev->service_cache, +				msecs_to_jiffies(SERVICE_CACHE_TIMEOUT)); +} +  static int read_controller_info(struct sock *sk, u16 index)  {  	struct mgmt_rp_read_info rp; @@ -174,40 +511,33 @@ static int read_controller_info(struct sock *sk, u16 index)  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_READ_INFO, ENODEV); +		return cmd_status(sk, index, MGMT_OP_READ_INFO, +						MGMT_STATUS_INVALID_PARAMS); -	hci_del_off_timer(hdev); +	if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) +		cancel_delayed_work_sync(&hdev->power_off); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev); -	set_bit(HCI_MGMT, &hdev->flags); +	if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags)) +		mgmt_init_hdev(hdev);  	memset(&rp, 0, sizeof(rp)); -	rp.type = hdev->dev_type; +	bacpy(&rp.bdaddr, &hdev->bdaddr); -	rp.powered = test_bit(HCI_UP, &hdev->flags); -	rp.connectable = test_bit(HCI_PSCAN, &hdev->flags); -	rp.discoverable = test_bit(HCI_ISCAN, &hdev->flags); -	rp.pairable = test_bit(HCI_PSCAN, &hdev->flags); +	rp.version = hdev->hci_ver; -	if (test_bit(HCI_AUTH, &hdev->flags)) -		rp.sec_mode = 3; -	else if (hdev->ssp_mode > 0) -		rp.sec_mode = 4; -	else -		rp.sec_mode = 2; +	put_unaligned_le16(hdev->manufacturer, &rp.manufacturer); + +	rp.supported_settings = cpu_to_le32(get_supported_settings(hdev)); +	rp.current_settings = cpu_to_le32(get_current_settings(hdev)); -	bacpy(&rp.bdaddr, &hdev->bdaddr); -	memcpy(rp.features, hdev->features, 8);  	memcpy(rp.dev_class, hdev->dev_class, 3); -	put_unaligned_le16(hdev->manufacturer, &rp.manufacturer); -	rp.hci_ver = hdev->hci_ver; -	put_unaligned_le16(hdev->hci_rev, &rp.hci_rev);  	memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name)); -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp)); @@ -221,7 +551,8 @@ static void mgmt_pending_free(struct pending_cmd *cmd)  }  static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, -						u16 index, void *data, u16 len) +							struct hci_dev *hdev, +							void *data, u16 len)  {  	struct pending_cmd *cmd; @@ -230,7 +561,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,  		return NULL;  	cmd->opcode = opcode; -	cmd->index = index; +	cmd->index = hdev->id;  	cmd->param = kmalloc(len, GFP_ATOMIC);  	if (!cmd->param) { @@ -244,48 +575,36 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,  	cmd->sk = sk;  	sock_hold(sk); -	list_add(&cmd->list, &cmd_list); +	list_add(&cmd->list, &hdev->mgmt_pending);  	return cmd;  } -static void mgmt_pending_foreach(u16 opcode, int index, +static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,  				void (*cb)(struct pending_cmd *cmd, void *data),  				void *data)  {  	struct list_head *p, *n; -	list_for_each_safe(p, n, &cmd_list) { +	list_for_each_safe(p, n, &hdev->mgmt_pending) {  		struct pending_cmd *cmd;  		cmd = list_entry(p, struct pending_cmd, list); -		if (cmd->opcode != opcode) -			continue; - -		if (index >= 0 && cmd->index != index) +		if (opcode > 0 && cmd->opcode != opcode)  			continue;  		cb(cmd, data);  	}  } -static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) +static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)  { -	struct list_head *p; - -	list_for_each(p, &cmd_list) { -		struct pending_cmd *cmd; - -		cmd = list_entry(p, struct pending_cmd, list); - -		if (cmd->opcode != opcode) -			continue; - -		if (index >= 0 && cmd->index != index) -			continue; +	struct pending_cmd *cmd; -		return cmd; +	list_for_each_entry(cmd, &hdev->mgmt_pending, list) { +		if (cmd->opcode == opcode) +			return cmd;  	}  	return NULL; @@ -297,6 +616,13 @@ static void mgmt_pending_remove(struct pending_cmd *cmd)  	mgmt_pending_free(cmd);  } +static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) +{ +	__le32 settings = cpu_to_le32(get_current_settings(hdev)); + +	return cmd_complete(sk, hdev->id, opcode, &settings, sizeof(settings)); +} +  static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)  {  	struct mgmt_mode *cp; @@ -309,40 +635,43 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)  	BT_DBG("request for hci%u", index);  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_SET_POWERED, EINVAL); +		return cmd_status(sk, index, MGMT_OP_SET_POWERED, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV); +		return cmd_status(sk, index, MGMT_OP_SET_POWERED, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	up = test_bit(HCI_UP, &hdev->flags);  	if ((cp->val && up) || (!cp->val && !up)) { -		err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EALREADY); +		err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);  		goto failed;  	} -	if (mgmt_pending_find(MGMT_OP_SET_POWERED, index)) { -		err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY); +	if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { +		err = cmd_status(sk, index, MGMT_OP_SET_POWERED, +							MGMT_STATUS_BUSY);  		goto failed;  	} -	cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, index, data, len); +	cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);  	if (!cmd) {  		err = -ENOMEM;  		goto failed;  	}  	if (cp->val) -		queue_work(hdev->workqueue, &hdev->power_on); +		schedule_work(&hdev->power_on);  	else -		queue_work(hdev->workqueue, &hdev->power_off); +		schedule_work(&hdev->power_off.work);  	err = 0;  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } @@ -350,7 +679,7 @@ failed:  static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,  									u16 len)  { -	struct mgmt_mode *cp; +	struct mgmt_cp_set_discoverable *cp;  	struct hci_dev *hdev;  	struct pending_cmd *cmd;  	u8 scan; @@ -361,32 +690,36 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,  	BT_DBG("request for hci%u", index);  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EINVAL); +		return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV); +		return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	if (!test_bit(HCI_UP, &hdev->flags)) { -		err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); +		err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, +						MGMT_STATUS_NOT_POWERED);  		goto failed;  	} -	if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || -			mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { -		err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY); +	if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || +			mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { +		err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, +							MGMT_STATUS_BUSY);  		goto failed;  	}  	if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&  					test_bit(HCI_PSCAN, &hdev->flags)) { -		err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EALREADY); +		err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);  		goto failed;  	} -	cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, index, data, len); +	cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);  	if (!cmd) {  		err = -ENOMEM;  		goto failed; @@ -396,13 +729,18 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,  	if (cp->val)  		scan |= SCAN_INQUIRY; +	else +		cancel_delayed_work(&hdev->discov_off);  	err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);  	if (err < 0)  		mgmt_pending_remove(cmd); +	if (cp->val) +		hdev->discov_timeout = get_unaligned_le16(&cp->timeout); +  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -422,31 +760,35 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,  	BT_DBG("request for hci%u", index);  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EINVAL); +		return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV); +		return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	if (!test_bit(HCI_UP, &hdev->flags)) { -		err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN); +		err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, +						MGMT_STATUS_NOT_POWERED);  		goto failed;  	} -	if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || -			mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { -		err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY); +	if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || +			mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { +		err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, +							MGMT_STATUS_BUSY);  		goto failed;  	}  	if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { -		err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EALREADY); +		err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);  		goto failed;  	} -	cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, index, data, len); +	cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);  	if (!cmd) {  		err = -ENOMEM;  		goto failed; @@ -462,14 +804,14 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,  		mgmt_pending_remove(cmd);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } -static int mgmt_event(u16 event, u16 index, void *data, u16 data_len, -							struct sock *skip_sk) +static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, +					u16 data_len, struct sock *skip_sk)  {  	struct sk_buff *skb;  	struct mgmt_hdr *hdr; @@ -482,7 +824,10 @@ static int mgmt_event(u16 event, u16 index, void *data, u16 data_len,  	hdr = (void *) skb_put(skb, sizeof(*hdr));  	hdr->opcode = cpu_to_le16(event); -	hdr->index = cpu_to_le16(index); +	if (hdev) +		hdr->index = cpu_to_le16(hdev->id); +	else +		hdr->index = cpu_to_le16(MGMT_INDEX_NONE);  	hdr->len = cpu_to_le16(data_len);  	if (data) @@ -494,20 +839,12 @@ static int mgmt_event(u16 event, u16 index, void *data, u16 data_len,  	return 0;  } -static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) -{ -	struct mgmt_mode rp; - -	rp.val = val; - -	return cmd_complete(sk, index, opcode, &rp, sizeof(rp)); -} -  static int set_pairable(struct sock *sk, u16 index, unsigned char *data,  									u16 len)  { -	struct mgmt_mode *cp, ev; +	struct mgmt_mode *cp;  	struct hci_dev *hdev; +	__le32 ev;  	int err;  	cp = (void *) data; @@ -515,211 +852,36 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,  	BT_DBG("request for hci%u", index);  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, EINVAL); +		return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV); +		return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	if (cp->val)  		set_bit(HCI_PAIRABLE, &hdev->flags);  	else  		clear_bit(HCI_PAIRABLE, &hdev->flags); -	err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, index, cp->val); +	err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);  	if (err < 0)  		goto failed; -	ev.val = cp->val; +	ev = cpu_to_le32(get_current_settings(hdev)); -	err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk); +	err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } -#define EIR_FLAGS		0x01 /* flags */ -#define EIR_UUID16_SOME		0x02 /* 16-bit UUID, more available */ -#define EIR_UUID16_ALL		0x03 /* 16-bit UUID, all listed */ -#define EIR_UUID32_SOME		0x04 /* 32-bit UUID, more available */ -#define EIR_UUID32_ALL		0x05 /* 32-bit UUID, all listed */ -#define EIR_UUID128_SOME	0x06 /* 128-bit UUID, more available */ -#define EIR_UUID128_ALL		0x07 /* 128-bit UUID, all listed */ -#define EIR_NAME_SHORT		0x08 /* shortened local name */ -#define EIR_NAME_COMPLETE	0x09 /* complete local name */ -#define EIR_TX_POWER		0x0A /* transmit power level */ -#define EIR_DEVICE_ID		0x10 /* device ID */ - -#define PNP_INFO_SVCLASS_ID		0x1200 - -static u8 bluetooth_base_uuid[] = { -			0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, -			0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static u16 get_uuid16(u8 *uuid128) -{ -	u32 val; -	int i; - -	for (i = 0; i < 12; i++) { -		if (bluetooth_base_uuid[i] != uuid128[i]) -			return 0; -	} - -	memcpy(&val, &uuid128[12], 4); - -	val = le32_to_cpu(val); -	if (val > 0xffff) -		return 0; - -	return (u16) val; -} - -static void create_eir(struct hci_dev *hdev, u8 *data) -{ -	u8 *ptr = data; -	u16 eir_len = 0; -	u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; -	int i, truncated = 0; -	struct list_head *p; -	size_t name_len; - -	name_len = strlen(hdev->dev_name); - -	if (name_len > 0) { -		/* EIR Data type */ -		if (name_len > 48) { -			name_len = 48; -			ptr[1] = EIR_NAME_SHORT; -		} else -			ptr[1] = EIR_NAME_COMPLETE; - -		/* EIR Data length */ -		ptr[0] = name_len + 1; - -		memcpy(ptr + 2, hdev->dev_name, name_len); - -		eir_len += (name_len + 2); -		ptr += (name_len + 2); -	} - -	memset(uuid16_list, 0, sizeof(uuid16_list)); - -	/* Group all UUID16 types */ -	list_for_each(p, &hdev->uuids) { -		struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); -		u16 uuid16; - -		uuid16 = get_uuid16(uuid->uuid); -		if (uuid16 == 0) -			return; - -		if (uuid16 < 0x1100) -			continue; - -		if (uuid16 == PNP_INFO_SVCLASS_ID) -			continue; - -		/* Stop if not enough space to put next UUID */ -		if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { -			truncated = 1; -			break; -		} - -		/* Check for duplicates */ -		for (i = 0; uuid16_list[i] != 0; i++) -			if (uuid16_list[i] == uuid16) -				break; - -		if (uuid16_list[i] == 0) { -			uuid16_list[i] = uuid16; -			eir_len += sizeof(u16); -		} -	} - -	if (uuid16_list[0] != 0) { -		u8 *length = ptr; - -		/* EIR Data type */ -		ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; - -		ptr += 2; -		eir_len += 2; - -		for (i = 0; uuid16_list[i] != 0; i++) { -			*ptr++ = (uuid16_list[i] & 0x00ff); -			*ptr++ = (uuid16_list[i] & 0xff00) >> 8; -		} - -		/* EIR Data length */ -		*length = (i * sizeof(u16)) + 1; -	} -} - -static int update_eir(struct hci_dev *hdev) -{ -	struct hci_cp_write_eir cp; - -	if (!(hdev->features[6] & LMP_EXT_INQ)) -		return 0; - -	if (hdev->ssp_mode == 0) -		return 0; - -	if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) -		return 0; - -	memset(&cp, 0, sizeof(cp)); - -	create_eir(hdev, cp.data); - -	if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) -		return 0; - -	memcpy(hdev->eir, cp.data, sizeof(cp.data)); - -	return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); -} - -static u8 get_service_classes(struct hci_dev *hdev) -{ -	struct list_head *p; -	u8 val = 0; - -	list_for_each(p, &hdev->uuids) { -		struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); - -		val |= uuid->svc_hint; -	} - -	return val; -} - -static int update_class(struct hci_dev *hdev) -{ -	u8 cod[3]; - -	BT_DBG("%s", hdev->name); - -	if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) -		return 0; - -	cod[0] = hdev->minor_class; -	cod[1] = hdev->major_class; -	cod[2] = get_service_classes(hdev); - -	if (memcmp(cod, hdev->dev_class, 3) == 0) -		return 0; - -	return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); -} -  static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)  {  	struct mgmt_cp_add_uuid *cp; @@ -732,13 +894,15 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)  	BT_DBG("request for hci%u", index);  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_ADD_UUID, EINVAL); +		return cmd_status(sk, index, MGMT_OP_ADD_UUID, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV); +		return cmd_status(sk, index, MGMT_OP_ADD_UUID, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);  	if (!uuid) { @@ -762,7 +926,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)  	err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -781,13 +945,15 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)  	BT_DBG("request for hci%u", index);  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, EINVAL); +		return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV); +		return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {  		err = hci_uuids_clear(hdev); @@ -807,7 +973,8 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)  	}  	if (found == 0) { -		err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENOENT); +		err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID, +						MGMT_STATUS_INVALID_PARAMS);  		goto unlock;  	} @@ -822,7 +989,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)  	err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);  unlock: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -840,97 +1007,71 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,  	BT_DBG("request for hci%u", index);  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, EINVAL); +		return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV); +		return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	hdev->major_class = cp->major;  	hdev->minor_class = cp->minor; +	if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags)) { +		hci_dev_unlock(hdev); +		cancel_delayed_work_sync(&hdev->service_cache); +		hci_dev_lock(hdev); +		update_eir(hdev); +	} +  	err = update_class(hdev);  	if (err == 0)  		err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0); -	hci_dev_unlock_bh(hdev); -	hci_dev_put(hdev); - -	return err; -} - -static int set_service_cache(struct sock *sk, u16 index,  unsigned char *data, -									u16 len) -{ -	struct hci_dev *hdev; -	struct mgmt_cp_set_service_cache *cp; -	int err; - -	cp = (void *) data; - -	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, EINVAL); - -	hdev = hci_dev_get(index); -	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV); - -	hci_dev_lock_bh(hdev); - -	BT_DBG("hci%u enable %d", index, cp->enable); - -	if (cp->enable) { -		set_bit(HCI_SERVICE_CACHE, &hdev->flags); -		err = 0; -	} else { -		clear_bit(HCI_SERVICE_CACHE, &hdev->flags); -		err = update_class(hdev); -		if (err == 0) -			err = update_eir(hdev); -	} - -	if (err == 0) -		err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL, -									0); - -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } -static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) +static int load_link_keys(struct sock *sk, u16 index, unsigned char *data, +								u16 len)  {  	struct hci_dev *hdev; -	struct mgmt_cp_load_keys *cp; +	struct mgmt_cp_load_link_keys *cp;  	u16 key_count, expected_len;  	int i;  	cp = (void *) data;  	if (len < sizeof(*cp)) -		return -EINVAL; +		return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, +						MGMT_STATUS_INVALID_PARAMS);  	key_count = get_unaligned_le16(&cp->key_count); -	expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); +	expected_len = sizeof(*cp) + key_count * +					sizeof(struct mgmt_link_key_info);  	if (expected_len != len) { -		BT_ERR("load_keys: expected %u bytes, got %u bytes", +		BT_ERR("load_link_keys: expected %u bytes, got %u bytes",  							len, expected_len); -		return -EINVAL; +		return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, +						MGMT_STATUS_INVALID_PARAMS);  	}  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, ENODEV); +		return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, +						MGMT_STATUS_INVALID_PARAMS);  	BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,  								key_count); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	hci_link_keys_clear(hdev); @@ -942,58 +1083,84 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)  		clear_bit(HCI_DEBUG_KEYS, &hdev->flags);  	for (i = 0; i < key_count; i++) { -		struct mgmt_key_info *key = &cp->keys[i]; +		struct mgmt_link_key_info *key = &cp->keys[i];  		hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,  								key->pin_len);  	} -	hci_dev_unlock_bh(hdev); +	cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0); + +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return 0;  } -static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) +static int remove_keys(struct sock *sk, u16 index, unsigned char *data, +								u16 len)  {  	struct hci_dev *hdev; -	struct mgmt_cp_remove_key *cp; +	struct mgmt_cp_remove_keys *cp; +	struct mgmt_rp_remove_keys rp; +	struct hci_cp_disconnect dc; +	struct pending_cmd *cmd;  	struct hci_conn *conn;  	int err;  	cp = (void *) data;  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, EINVAL); +		return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV); +		return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev); + +	memset(&rp, 0, sizeof(rp)); +	bacpy(&rp.bdaddr, &cp->bdaddr); +	rp.status = MGMT_STATUS_FAILED;  	err = hci_remove_link_key(hdev, &cp->bdaddr);  	if (err < 0) { -		err = cmd_status(sk, index, MGMT_OP_REMOVE_KEY, -err); +		rp.status = MGMT_STATUS_NOT_PAIRED;  		goto unlock;  	} -	err = 0; - -	if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) +	if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) { +		err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, +								sizeof(rp));  		goto unlock; +	}  	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); -	if (conn) { -		struct hci_cp_disconnect dc; +	if (!conn) { +		err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, +								sizeof(rp)); +		goto unlock; +	} -		put_unaligned_le16(conn->handle, &dc.handle); -		dc.reason = 0x13; /* Remote User Terminated Connection */ -		err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); +	cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_KEYS, hdev, cp, sizeof(*cp)); +	if (!cmd) { +		err = -ENOMEM; +		goto unlock;  	} +	put_unaligned_le16(conn->handle, &dc.handle); +	dc.reason = 0x13; /* Remote User Terminated Connection */ +	err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); +	if (err < 0) +		mgmt_pending_remove(cmd); +  unlock: -	hci_dev_unlock_bh(hdev); +	if (err < 0) +		err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, +								sizeof(rp)); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1013,21 +1180,25 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)  	cp = (void *) data;  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_DISCONNECT, EINVAL); +		return cmd_status(sk, index, MGMT_OP_DISCONNECT, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV); +		return cmd_status(sk, index, MGMT_OP_DISCONNECT, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	if (!test_bit(HCI_UP, &hdev->flags)) { -		err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN); +		err = cmd_status(sk, index, MGMT_OP_DISCONNECT, +						MGMT_STATUS_NOT_POWERED);  		goto failed;  	} -	if (mgmt_pending_find(MGMT_OP_DISCONNECT, index)) { -		err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY); +	if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { +		err = cmd_status(sk, index, MGMT_OP_DISCONNECT, +							MGMT_STATUS_BUSY);  		goto failed;  	} @@ -1036,11 +1207,12 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)  		conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);  	if (!conn) { -		err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN); +		err = cmd_status(sk, index, MGMT_OP_DISCONNECT, +						MGMT_STATUS_NOT_CONNECTED);  		goto failed;  	} -	cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, index, data, len); +	cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);  	if (!cmd) {  		err = -ENOMEM;  		goto failed; @@ -1054,16 +1226,36 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)  		mgmt_pending_remove(cmd);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } +static u8 link_to_mgmt(u8 link_type, u8 addr_type) +{ +	switch (link_type) { +	case LE_LINK: +		switch (addr_type) { +		case ADDR_LE_DEV_PUBLIC: +			return MGMT_ADDR_LE_PUBLIC; +		case ADDR_LE_DEV_RANDOM: +			return MGMT_ADDR_LE_RANDOM; +		default: +			return MGMT_ADDR_INVALID; +		} +	case ACL_LINK: +		return MGMT_ADDR_BREDR; +	default: +		return MGMT_ADDR_INVALID; +	} +} +  static int get_connections(struct sock *sk, u16 index)  {  	struct mgmt_rp_get_connections *rp;  	struct hci_dev *hdev; +	struct hci_conn *c;  	struct list_head *p;  	size_t rp_len;  	u16 count; @@ -1073,16 +1265,17 @@ static int get_connections(struct sock *sk, u16 index)  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV); +		return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	count = 0;  	list_for_each(p, &hdev->conn_hash.list) {  		count++;  	} -	rp_len = sizeof(*rp) + (count * sizeof(bdaddr_t)); +	rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));  	rp = kmalloc(rp_len, GFP_ATOMIC);  	if (!rp) {  		err = -ENOMEM; @@ -1092,17 +1285,22 @@ static int get_connections(struct sock *sk, u16 index)  	put_unaligned_le16(count, &rp->conn_count);  	i = 0; -	list_for_each(p, &hdev->conn_hash.list) { -		struct hci_conn *c = list_entry(p, struct hci_conn, list); - -		bacpy(&rp->conn[i++], &c->dst); +	list_for_each_entry(c, &hdev->conn_hash.list, list) { +		bacpy(&rp->addr[i].bdaddr, &c->dst); +		rp->addr[i].type = link_to_mgmt(c->type, c->dst_type); +		if (rp->addr[i].type == MGMT_ADDR_INVALID) +			continue; +		i++;  	} +	/* Recalculate length in case of filtered SCO connections, etc */ +	rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); +  	err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);  unlock:  	kfree(rp); -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } @@ -1113,7 +1311,7 @@ static int send_pin_code_neg_reply(struct sock *sk, u16 index,  	struct pending_cmd *cmd;  	int err; -	cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, cp, +	cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,  								sizeof(*cp));  	if (!cmd)  		return -ENOMEM; @@ -1142,22 +1340,26 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,  	cp = (void *) data;  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, EINVAL); +		return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV); +		return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	if (!test_bit(HCI_UP, &hdev->flags)) { -		err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN); +		err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, +						MGMT_STATUS_NOT_POWERED);  		goto failed;  	}  	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);  	if (!conn) { -		err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENOTCONN); +		err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, +						MGMT_STATUS_NOT_CONNECTED);  		goto failed;  	} @@ -1169,12 +1371,12 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,  		err = send_pin_code_neg_reply(sk, index, hdev, &ncp);  		if (err >= 0)  			err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, -								EINVAL); +						MGMT_STATUS_INVALID_PARAMS);  		goto failed;  	} -	cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len); +	cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len);  	if (!cmd) {  		err = -ENOMEM;  		goto failed; @@ -1189,7 +1391,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,  		mgmt_pending_remove(cmd);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1208,25 +1410,25 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,  	if (len != sizeof(*cp))  		return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, -									EINVAL); +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev)  		return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, -									ENODEV); +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	if (!test_bit(HCI_UP, &hdev->flags)) {  		err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, -								ENETDOWN); +						MGMT_STATUS_NOT_POWERED);  		goto failed;  	}  	err = send_pin_code_neg_reply(sk, index, hdev, cp);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1243,20 +1445,22 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,  	cp = (void *) data;  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, EINVAL); +		return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV); +		return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	hdev->io_capability = cp->io_capability;  	BT_DBG("%s IO capability set to 0x%02x", hdev->name,  							hdev->io_capability); -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0); @@ -1265,19 +1469,12 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,  static inline struct pending_cmd *find_pairing(struct hci_conn *conn)  {  	struct hci_dev *hdev = conn->hdev; -	struct list_head *p; - -	list_for_each(p, &cmd_list) { -		struct pending_cmd *cmd; - -		cmd = list_entry(p, struct pending_cmd, list); +	struct pending_cmd *cmd; +	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {  		if (cmd->opcode != MGMT_OP_PAIR_DEVICE)  			continue; -		if (cmd->index != hdev->id) -			continue; -  		if (cmd->user_data != conn)  			continue; @@ -1292,7 +1489,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)  	struct mgmt_rp_pair_device rp;  	struct hci_conn *conn = cmd->user_data; -	bacpy(&rp.bdaddr, &conn->dst); +	bacpy(&rp.addr.bdaddr, &conn->dst); +	rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);  	rp.status = status;  	cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp)); @@ -1314,20 +1512,18 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)  	BT_DBG("status %u", status);  	cmd = find_pairing(conn); -	if (!cmd) { +	if (!cmd)  		BT_DBG("Unable to find a pending command"); -		return; -	} - -	pairing_complete(cmd, status); +	else +		pairing_complete(cmd, status);  }  static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)  {  	struct hci_dev *hdev;  	struct mgmt_cp_pair_device *cp; +	struct mgmt_rp_pair_device rp;  	struct pending_cmd *cmd; -	struct adv_entry *entry;  	u8 sec_level, auth_type;  	struct hci_conn *conn;  	int err; @@ -1337,13 +1533,15 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)  	cp = (void *) data;  	if (len != sizeof(*cp)) -		return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EINVAL); +		return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV); +		return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	sec_level = BT_SECURITY_MEDIUM;  	if (cp->io_cap == 0x03) @@ -1351,26 +1549,33 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)  	else  		auth_type = HCI_AT_DEDICATED_BONDING_MITM; -	entry = hci_find_adv_entry(hdev, &cp->bdaddr); -	if (entry) -		conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level, +	if (cp->addr.type == MGMT_ADDR_BREDR) +		conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,  								auth_type);  	else -		conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, +		conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,  								auth_type); +	memset(&rp, 0, sizeof(rp)); +	bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); +	rp.addr.type = cp->addr.type; +  	if (IS_ERR(conn)) { -		err = PTR_ERR(conn); +		rp.status = -PTR_ERR(conn); +		err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE, +							&rp, sizeof(rp));  		goto unlock;  	}  	if (conn->connect_cfm_cb) {  		hci_conn_put(conn); -		err = cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EBUSY); +		rp.status = EBUSY; +		err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE, +							&rp, sizeof(rp));  		goto unlock;  	} -	cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, index, data, len); +	cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);  	if (!cmd) {  		err = -ENOMEM;  		hci_conn_put(conn); @@ -1378,7 +1583,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)  	}  	/* For LE, just connecting isn't a proof that the pairing finished */ -	if (!entry) +	if (cp->addr.type == MGMT_ADDR_BREDR)  		conn->connect_cfm_cb = pairing_complete_cb;  	conn->security_cfm_cb = pairing_complete_cb; @@ -1393,62 +1598,151 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)  	err = 0;  unlock: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } -static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data, -							u16 len, int success) +static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr, +					u16 mgmt_op, u16 hci_op, __le32 passkey)  { -	struct mgmt_cp_user_confirm_reply *cp = (void *) data; -	u16 mgmt_op, hci_op;  	struct pending_cmd *cmd;  	struct hci_dev *hdev; +	struct hci_conn *conn;  	int err; -	BT_DBG(""); +	hdev = hci_dev_get(index); +	if (!hdev) +		return cmd_status(sk, index, mgmt_op, +						MGMT_STATUS_INVALID_PARAMS); -	if (success) { -		mgmt_op = MGMT_OP_USER_CONFIRM_REPLY; -		hci_op = HCI_OP_USER_CONFIRM_REPLY; -	} else { -		mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY; -		hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY; +	hci_dev_lock(hdev); + +	if (!test_bit(HCI_UP, &hdev->flags)) { +		err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED); +		goto done;  	} -	if (len != sizeof(*cp)) -		return cmd_status(sk, index, mgmt_op, EINVAL); +	/* +	 * Check for an existing ACL link, if present pair via +	 * HCI commands. +	 * +	 * If no ACL link is present, check for an LE link and if +	 * present, pair via the SMP engine. +	 * +	 * If neither ACL nor LE links are present, fail with error. +	 */ +	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); +	if (!conn) { +		conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr); +		if (!conn) { +			err = cmd_status(sk, index, mgmt_op, +						MGMT_STATUS_NOT_CONNECTED); +			goto done; +		} -	hdev = hci_dev_get(index); -	if (!hdev) -		return cmd_status(sk, index, mgmt_op, ENODEV); +		/* Continue with pairing via SMP */ +		err = smp_user_confirm_reply(conn, mgmt_op, passkey); -	hci_dev_lock_bh(hdev); +		if (!err) +			err = cmd_status(sk, index, mgmt_op, +							MGMT_STATUS_SUCCESS); +		else +			err = cmd_status(sk, index, mgmt_op, +							MGMT_STATUS_FAILED); -	if (!test_bit(HCI_UP, &hdev->flags)) { -		err = cmd_status(sk, index, mgmt_op, ENETDOWN); -		goto failed; +		goto done;  	} -	cmd = mgmt_pending_add(sk, mgmt_op, index, data, len); +	cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));  	if (!cmd) {  		err = -ENOMEM; -		goto failed; +		goto done;  	} -	err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr); +	/* Continue with pairing via HCI */ +	if (hci_op == HCI_OP_USER_PASSKEY_REPLY) { +		struct hci_cp_user_passkey_reply cp; + +		bacpy(&cp.bdaddr, bdaddr); +		cp.passkey = passkey; +		err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp); +	} else +		err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr); +  	if (err < 0)  		mgmt_pending_remove(cmd); -failed: -	hci_dev_unlock_bh(hdev); +done: +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } +static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len) +{ +	struct mgmt_cp_user_confirm_reply *cp = (void *) data; + +	BT_DBG(""); + +	if (len != sizeof(*cp)) +		return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY, +						MGMT_STATUS_INVALID_PARAMS); + +	return user_pairing_resp(sk, index, &cp->bdaddr, +			MGMT_OP_USER_CONFIRM_REPLY, +			HCI_OP_USER_CONFIRM_REPLY, 0); +} + +static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data, +									u16 len) +{ +	struct mgmt_cp_user_confirm_neg_reply *cp = data; + +	BT_DBG(""); + +	if (len != sizeof(*cp)) +		return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY, +						MGMT_STATUS_INVALID_PARAMS); + +	return user_pairing_resp(sk, index, &cp->bdaddr, +			MGMT_OP_USER_CONFIRM_NEG_REPLY, +			HCI_OP_USER_CONFIRM_NEG_REPLY, 0); +} + +static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len) +{ +	struct mgmt_cp_user_passkey_reply *cp = (void *) data; + +	BT_DBG(""); + +	if (len != sizeof(*cp)) +		return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY, +									EINVAL); + +	return user_pairing_resp(sk, index, &cp->bdaddr, +			MGMT_OP_USER_PASSKEY_REPLY, +			HCI_OP_USER_PASSKEY_REPLY, cp->passkey); +} + +static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data, +									u16 len) +{ +	struct mgmt_cp_user_passkey_neg_reply *cp = (void *) data; + +	BT_DBG(""); + +	if (len != sizeof(*cp)) +		return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY, +									EINVAL); + +	return user_pairing_resp(sk, index, &cp->bdaddr, +			MGMT_OP_USER_PASSKEY_NEG_REPLY, +			HCI_OP_USER_PASSKEY_NEG_REPLY, 0); +} +  static int set_local_name(struct sock *sk, u16 index, unsigned char *data,  								u16 len)  { @@ -1461,15 +1755,17 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,  	BT_DBG("");  	if (len != sizeof(*mgmt_cp)) -		return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, EINVAL); +		return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV); +		return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev); -	cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len); +	cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len);  	if (!cmd) {  		err = -ENOMEM;  		goto failed; @@ -1482,7 +1778,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,  		mgmt_pending_remove(cmd);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1499,28 +1795,29 @@ static int read_local_oob_data(struct sock *sk, u16 index)  	hdev = hci_dev_get(index);  	if (!hdev)  		return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, -									ENODEV); +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	if (!test_bit(HCI_UP, &hdev->flags)) {  		err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, -								ENETDOWN); +						MGMT_STATUS_NOT_POWERED);  		goto unlock;  	}  	if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {  		err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, -								EOPNOTSUPP); +						MGMT_STATUS_NOT_SUPPORTED);  		goto unlock;  	} -	if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index)) { -		err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY); +	if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { +		err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, +							MGMT_STATUS_BUSY);  		goto unlock;  	} -	cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, index, NULL, 0); +	cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);  	if (!cmd) {  		err = -ENOMEM;  		goto unlock; @@ -1531,7 +1828,7 @@ static int read_local_oob_data(struct sock *sk, u16 index)  		mgmt_pending_remove(cmd);  unlock: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1548,24 +1845,25 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,  	if (len != sizeof(*cp))  		return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -									EINVAL); +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev)  		return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -									ENODEV); +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,  								cp->randomizer);  	if (err < 0) -		err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -err); +		err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, +							MGMT_STATUS_FAILED);  	else  		err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,  									0); -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1582,62 +1880,68 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,  	if (len != sizeof(*cp))  		return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, -									EINVAL); +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev)  		return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, -									ENODEV); +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev);  	err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);  	if (err < 0)  		err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, -									-err); +						MGMT_STATUS_INVALID_PARAMS);  	else  		err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,  								NULL, 0); -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err;  } -static int start_discovery(struct sock *sk, u16 index) +static int start_discovery(struct sock *sk, u16 index, +						unsigned char *data, u16 len)  { -	u8 lap[3] = { 0x33, 0x8b, 0x9e }; -	struct hci_cp_inquiry cp; +	struct mgmt_cp_start_discovery *cp = (void *) data;  	struct pending_cmd *cmd;  	struct hci_dev *hdev;  	int err;  	BT_DBG("hci%u", index); +	if (len != sizeof(*cp)) +		return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, +						MGMT_STATUS_INVALID_PARAMS); +  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV); +		return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev); -	cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0); +	if (!test_bit(HCI_UP, &hdev->flags)) { +		err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, +						MGMT_STATUS_NOT_POWERED); +		goto failed; +	} + +	cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);  	if (!cmd) {  		err = -ENOMEM;  		goto failed;  	} -	memset(&cp, 0, sizeof(cp)); -	memcpy(&cp.lap, lap, 3); -	cp.length  = 0x08; -	cp.num_rsp = 0x00; - -	err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); +	err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);  	if (err < 0)  		mgmt_pending_remove(cmd);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1653,22 +1957,23 @@ static int stop_discovery(struct sock *sk, u16 index)  	hdev = hci_dev_get(index);  	if (!hdev) -		return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV); +		return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); +	hci_dev_lock(hdev); -	cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0); +	cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);  	if (!cmd) {  		err = -ENOMEM;  		goto failed;  	} -	err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); +	err = hci_cancel_inquiry(hdev);  	if (err < 0)  		mgmt_pending_remove(cmd);  failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1678,7 +1983,6 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,  								u16 len)  {  	struct hci_dev *hdev; -	struct pending_cmd *cmd;  	struct mgmt_cp_block_device *cp = (void *) data;  	int err; @@ -1686,33 +1990,24 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,  	if (len != sizeof(*cp))  		return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -							EINVAL); +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev)  		return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -							ENODEV); +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); - -	cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0); -	if (!cmd) { -		err = -ENOMEM; -		goto failed; -	} +	hci_dev_lock(hdev);  	err = hci_blacklist_add(hdev, &cp->bdaddr); -  	if (err < 0) -		err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err); +		err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, +							MGMT_STATUS_FAILED);  	else  		err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,  							NULL, 0); -	mgmt_pending_remove(cmd); - -failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1722,7 +2017,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,  								u16 len)  {  	struct hci_dev *hdev; -	struct pending_cmd *cmd;  	struct mgmt_cp_unblock_device *cp = (void *) data;  	int err; @@ -1730,33 +2024,25 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,  	if (len != sizeof(*cp))  		return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -								EINVAL); +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev)  		return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -								ENODEV); +						MGMT_STATUS_INVALID_PARAMS); -	hci_dev_lock_bh(hdev); - -	cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0); -	if (!cmd) { -		err = -ENOMEM; -		goto failed; -	} +	hci_dev_lock(hdev);  	err = hci_blacklist_del(hdev, &cp->bdaddr);  	if (err < 0) -		err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err); +		err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, +						MGMT_STATUS_INVALID_PARAMS);  	else  		err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,  								NULL, 0); -	mgmt_pending_remove(cmd); - -failed: -	hci_dev_unlock_bh(hdev); +	hci_dev_unlock(hdev);  	hci_dev_put(hdev);  	return err; @@ -1766,7 +2052,7 @@ static int set_fast_connectable(struct sock *sk, u16 index,  					unsigned char *data, u16 len)  {  	struct hci_dev *hdev; -	struct mgmt_cp_set_fast_connectable *cp = (void *) data; +	struct mgmt_mode *cp = (void *) data;  	struct hci_cp_write_page_scan_activity acp;  	u8 type;  	int err; @@ -1775,16 +2061,16 @@ static int set_fast_connectable(struct sock *sk, u16 index,  	if (len != sizeof(*cp))  		return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, -								EINVAL); +						MGMT_STATUS_INVALID_PARAMS);  	hdev = hci_dev_get(index);  	if (!hdev)  		return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, -								ENODEV); +						MGMT_STATUS_INVALID_PARAMS);  	hci_dev_lock(hdev); -	if (cp->enable) { +	if (cp->val) {  		type = PAGE_SCAN_TYPE_INTERLACED;  		acp.interval = 0x0024;	/* 22.5 msec page scan interval */  	} else { @@ -1798,14 +2084,14 @@ static int set_fast_connectable(struct sock *sk, u16 index,  						sizeof(acp), &acp);  	if (err < 0) {  		err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, -								-err); +							MGMT_STATUS_FAILED);  		goto done;  	}  	err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);  	if (err < 0) {  		err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, -								-err); +							MGMT_STATUS_FAILED);  		goto done;  	} @@ -1868,6 +2154,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)  	case MGMT_OP_SET_CONNECTABLE:  		err = set_connectable(sk, index, buf + sizeof(*hdr), len);  		break; +	case MGMT_OP_SET_FAST_CONNECTABLE: +		err = set_fast_connectable(sk, index, buf + sizeof(*hdr), +								len); +		break;  	case MGMT_OP_SET_PAIRABLE:  		err = set_pairable(sk, index, buf + sizeof(*hdr), len);  		break; @@ -1880,14 +2170,11 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)  	case MGMT_OP_SET_DEV_CLASS:  		err = set_dev_class(sk, index, buf + sizeof(*hdr), len);  		break; -	case MGMT_OP_SET_SERVICE_CACHE: -		err = set_service_cache(sk, index, buf + sizeof(*hdr), len); -		break; -	case MGMT_OP_LOAD_KEYS: -		err = load_keys(sk, index, buf + sizeof(*hdr), len); +	case MGMT_OP_LOAD_LINK_KEYS: +		err = load_link_keys(sk, index, buf + sizeof(*hdr), len);  		break; -	case MGMT_OP_REMOVE_KEY: -		err = remove_key(sk, index, buf + sizeof(*hdr), len); +	case MGMT_OP_REMOVE_KEYS: +		err = remove_keys(sk, index, buf + sizeof(*hdr), len);  		break;  	case MGMT_OP_DISCONNECT:  		err = disconnect(sk, index, buf + sizeof(*hdr), len); @@ -1908,10 +2195,18 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)  		err = pair_device(sk, index, buf + sizeof(*hdr), len);  		break;  	case MGMT_OP_USER_CONFIRM_REPLY: -		err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 1); +		err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len);  		break;  	case MGMT_OP_USER_CONFIRM_NEG_REPLY: -		err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0); +		err = user_confirm_neg_reply(sk, index, buf + sizeof(*hdr), +									len); +		break; +	case MGMT_OP_USER_PASSKEY_REPLY: +		err = user_passkey_reply(sk, index, buf + sizeof(*hdr), len); +		break; +	case MGMT_OP_USER_PASSKEY_NEG_REPLY: +		err = user_passkey_neg_reply(sk, index, buf + sizeof(*hdr), +									len);  		break;  	case MGMT_OP_SET_LOCAL_NAME:  		err = set_local_name(sk, index, buf + sizeof(*hdr), len); @@ -1927,7 +2222,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)  									len);  		break;  	case MGMT_OP_START_DISCOVERY: -		err = start_discovery(sk, index); +		err = start_discovery(sk, index, buf + sizeof(*hdr), len);  		break;  	case MGMT_OP_STOP_DISCOVERY:  		err = stop_discovery(sk, index); @@ -1938,13 +2233,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)  	case MGMT_OP_UNBLOCK_DEVICE:  		err = unblock_device(sk, index, buf + sizeof(*hdr), len);  		break; -	case MGMT_OP_SET_FAST_CONNECTABLE: -		err = set_fast_connectable(sk, index, buf + sizeof(*hdr), -								len); -		break;  	default:  		BT_DBG("Unknown op %u", opcode); -		err = cmd_status(sk, index, opcode, 0x01); +		err = cmd_status(sk, index, opcode, +						MGMT_STATUS_UNKNOWN_COMMAND);  		break;  	} @@ -1958,30 +2250,39 @@ done:  	return err;  } -int mgmt_index_added(u16 index) +static void cmd_status_rsp(struct pending_cmd *cmd, void *data)  { -	return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL); +	u8 *status = data; + +	cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); +	mgmt_pending_remove(cmd);  } -int mgmt_index_removed(u16 index) +int mgmt_index_added(struct hci_dev *hdev)  { -	return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL); +	return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); +} + +int mgmt_index_removed(struct hci_dev *hdev) +{ +	u8 status = ENODEV; + +	mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); + +	return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);  }  struct cmd_lookup {  	u8 val;  	struct sock *sk; +	struct hci_dev *hdev;  }; -static void mode_rsp(struct pending_cmd *cmd, void *data) +static void settings_rsp(struct pending_cmd *cmd, void *data)  { -	struct mgmt_mode *cp = cmd->param;  	struct cmd_lookup *match = data; -	if (cp->val != match->val) -		return; - -	send_mode_rsp(cmd->sk, cmd->opcode, cmd->index, cp->val); +	send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);  	list_del(&cmd->list); @@ -1993,17 +2294,23 @@ static void mode_rsp(struct pending_cmd *cmd, void *data)  	mgmt_pending_free(cmd);  } -int mgmt_powered(u16 index, u8 powered) +int mgmt_powered(struct hci_dev *hdev, u8 powered)  { -	struct mgmt_mode ev; -	struct cmd_lookup match = { powered, NULL }; +	struct cmd_lookup match = { powered, NULL, hdev }; +	__le32 ev;  	int ret; -	mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); +	mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); -	ev.val = powered; +	if (!powered) { +		u8 status = ENETDOWN; +		mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); +	} + +	ev = cpu_to_le32(get_current_settings(hdev)); -	ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk); +	ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), +								match.sk);  	if (match.sk)  		sock_put(match.sk); @@ -2011,36 +2318,36 @@ int mgmt_powered(u16 index, u8 powered)  	return ret;  } -int mgmt_discoverable(u16 index, u8 discoverable) +int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)  { -	struct mgmt_mode ev; -	struct cmd_lookup match = { discoverable, NULL }; +	struct cmd_lookup match = { discoverable, NULL, hdev }; +	__le32 ev;  	int ret; -	mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, mode_rsp, &match); +	mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match); -	ev.val = discoverable; +	ev = cpu_to_le32(get_current_settings(hdev)); -	ret = mgmt_event(MGMT_EV_DISCOVERABLE, index, &ev, sizeof(ev), +	ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),  								match.sk); -  	if (match.sk)  		sock_put(match.sk);  	return ret;  } -int mgmt_connectable(u16 index, u8 connectable) +int mgmt_connectable(struct hci_dev *hdev, u8 connectable)  { -	struct mgmt_mode ev; -	struct cmd_lookup match = { connectable, NULL }; +	__le32 ev; +	struct cmd_lookup match = { connectable, NULL, hdev };  	int ret; -	mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match); +	mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, +								&match); -	ev.val = connectable; +	ev = cpu_to_le32(get_current_settings(hdev)); -	ret = mgmt_event(MGMT_EV_CONNECTABLE, index, &ev, sizeof(ev), match.sk); +	ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);  	if (match.sk)  		sock_put(match.sk); @@ -2048,9 +2355,25 @@ int mgmt_connectable(u16 index, u8 connectable)  	return ret;  } -int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) +int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) +{ +	u8 mgmt_err = mgmt_status(status); + +	if (scan & SCAN_PAGE) +		mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, +						cmd_status_rsp, &mgmt_err); + +	if (scan & SCAN_INQUIRY) +		mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, +						cmd_status_rsp, &mgmt_err); + +	return 0; +} + +int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, +								u8 persistent)  { -	struct mgmt_ev_new_key ev; +	struct mgmt_ev_new_link_key ev;  	memset(&ev, 0, sizeof(ev)); @@ -2060,17 +2383,18 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)  	memcpy(ev.key.val, key->val, 16);  	ev.key.pin_len = key->pin_len; -	return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); +	return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);  } -int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type) +int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, +								u8 addr_type)  { -	struct mgmt_ev_connected ev; +	struct mgmt_addr_info ev;  	bacpy(&ev.bdaddr, bdaddr); -	ev.link_type = link_type; +	ev.type = link_to_mgmt(link_type, addr_type); -	return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); +	return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL);  }  static void disconnect_rsp(struct pending_cmd *cmd, void *data) @@ -2080,6 +2404,7 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)  	struct mgmt_rp_disconnect rp;  	bacpy(&rp.bdaddr, &cp->bdaddr); +	rp.status = 0;  	cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); @@ -2089,75 +2414,110 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)  	mgmt_pending_remove(cmd);  } -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) +static void remove_keys_rsp(struct pending_cmd *cmd, void *data) +{ +	u8 *status = data; +	struct mgmt_cp_remove_keys *cp = cmd->param; +	struct mgmt_rp_remove_keys rp; + +	memset(&rp, 0, sizeof(rp)); +	bacpy(&rp.bdaddr, &cp->bdaddr); +	if (status != NULL) +		rp.status = *status; + +	cmd_complete(cmd->sk, cmd->index, MGMT_OP_REMOVE_KEYS, &rp, +								sizeof(rp)); + +	mgmt_pending_remove(cmd); +} + +int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, +								u8 addr_type)  { -	struct mgmt_ev_disconnected ev; +	struct mgmt_addr_info ev;  	struct sock *sk = NULL;  	int err; -	mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); +	mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);  	bacpy(&ev.bdaddr, bdaddr); +	ev.type = link_to_mgmt(link_type, addr_type); -	err = mgmt_event(MGMT_EV_DISCONNECTED, index, &ev, sizeof(ev), sk); +	err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk);  	if (sk)  		sock_put(sk); +	mgmt_pending_foreach(MGMT_OP_REMOVE_KEYS, hdev, remove_keys_rsp, NULL); +  	return err;  } -int mgmt_disconnect_failed(u16 index) +int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)  {  	struct pending_cmd *cmd; +	u8 mgmt_err = mgmt_status(status);  	int err; -	cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index); +	cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);  	if (!cmd)  		return -ENOENT; -	err = cmd_status(cmd->sk, index, MGMT_OP_DISCONNECT, EIO); +	if (bdaddr) { +		struct mgmt_rp_disconnect rp; + +		bacpy(&rp.bdaddr, bdaddr); +		rp.status = status; + +		err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, +							&rp, sizeof(rp)); +	} else +		err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT, +								mgmt_err);  	mgmt_pending_remove(cmd);  	return err;  } -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, +						u8 addr_type, u8 status)  {  	struct mgmt_ev_connect_failed ev; -	bacpy(&ev.bdaddr, bdaddr); -	ev.status = status; +	bacpy(&ev.addr.bdaddr, bdaddr); +	ev.addr.type = link_to_mgmt(link_type, addr_type); +	ev.status = mgmt_status(status); -	return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL); +	return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);  } -int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure) +int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)  {  	struct mgmt_ev_pin_code_request ev;  	bacpy(&ev.bdaddr, bdaddr);  	ev.secure = secure; -	return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev), +	return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),  									NULL);  } -int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, +								u8 status)  {  	struct pending_cmd *cmd;  	struct mgmt_rp_pin_code_reply rp;  	int err; -	cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index); +	cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);  	if (!cmd)  		return -ENOENT;  	bacpy(&rp.bdaddr, bdaddr); -	rp.status = status; +	rp.status = mgmt_status(status); -	err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_REPLY, &rp, +	err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,  								sizeof(rp));  	mgmt_pending_remove(cmd); @@ -2165,20 +2525,21 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)  	return err;  } -int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, +								u8 status)  {  	struct pending_cmd *cmd;  	struct mgmt_rp_pin_code_reply rp;  	int err; -	cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index); +	cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);  	if (!cmd)  		return -ENOENT;  	bacpy(&rp.bdaddr, bdaddr); -	rp.status = status; +	rp.status = mgmt_status(status); -	err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, +	err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,  								sizeof(rp));  	mgmt_pending_remove(cmd); @@ -2186,97 +2547,119 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)  	return err;  } -int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value, -							u8 confirm_hint) +int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, +						__le32 value, u8 confirm_hint)  {  	struct mgmt_ev_user_confirm_request ev; -	BT_DBG("hci%u", index); +	BT_DBG("%s", hdev->name);  	bacpy(&ev.bdaddr, bdaddr);  	ev.confirm_hint = confirm_hint;  	put_unaligned_le32(value, &ev.value); -	return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev), +	return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), +									NULL); +} + +int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ +	struct mgmt_ev_user_passkey_request ev; + +	BT_DBG("%s", hdev->name); + +	bacpy(&ev.bdaddr, bdaddr); + +	return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),  									NULL);  } -static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status, -								u8 opcode) +static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, +							u8 status, u8 opcode)  {  	struct pending_cmd *cmd;  	struct mgmt_rp_user_confirm_reply rp;  	int err; -	cmd = mgmt_pending_find(opcode, index); +	cmd = mgmt_pending_find(opcode, hdev);  	if (!cmd)  		return -ENOENT;  	bacpy(&rp.bdaddr, bdaddr); -	rp.status = status; -	err = cmd_complete(cmd->sk, index, opcode, &rp, sizeof(rp)); +	rp.status = mgmt_status(status); +	err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));  	mgmt_pending_remove(cmd);  	return err;  } -int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, +								u8 status)  { -	return confirm_reply_complete(index, bdaddr, status, +	return user_pairing_resp_complete(hdev, bdaddr, status,  						MGMT_OP_USER_CONFIRM_REPLY);  } -int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, +						bdaddr_t *bdaddr, u8 status)  { -	return confirm_reply_complete(index, bdaddr, status, +	return user_pairing_resp_complete(hdev, bdaddr, status,  					MGMT_OP_USER_CONFIRM_NEG_REPLY);  } -int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, +								u8 status) +{ +	return user_pairing_resp_complete(hdev, bdaddr, status, +						MGMT_OP_USER_PASSKEY_REPLY); +} + +int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, +						bdaddr_t *bdaddr, u8 status) +{ +	return user_pairing_resp_complete(hdev, bdaddr, status, +					MGMT_OP_USER_PASSKEY_NEG_REPLY); +} + +int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)  {  	struct mgmt_ev_auth_failed ev;  	bacpy(&ev.bdaddr, bdaddr); -	ev.status = status; +	ev.status = mgmt_status(status); -	return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL); +	return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);  } -int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status) +int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)  {  	struct pending_cmd *cmd; -	struct hci_dev *hdev;  	struct mgmt_cp_set_local_name ev;  	int err;  	memset(&ev, 0, sizeof(ev));  	memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); -	cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, index); +	cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);  	if (!cmd)  		goto send_event;  	if (status) { -		err = cmd_status(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, EIO); +		err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, +							mgmt_status(status));  		goto failed;  	} -	hdev = hci_dev_get(index); -	if (hdev) { -		hci_dev_lock_bh(hdev); -		update_eir(hdev); -		hci_dev_unlock_bh(hdev); -		hci_dev_put(hdev); -	} +	update_eir(hdev); -	err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev, +	err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev,  								sizeof(ev));  	if (err < 0)  		goto failed;  send_event: -	err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, index, &ev, sizeof(ev), +	err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),  							cmd ? cmd->sk : NULL);  failed: @@ -2285,29 +2668,31 @@ failed:  	return err;  } -int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, -								u8 status) +int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, +						u8 *randomizer, u8 status)  {  	struct pending_cmd *cmd;  	int err; -	BT_DBG("hci%u status %u", index, status); +	BT_DBG("%s status %u", hdev->name, status); -	cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index); +	cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);  	if (!cmd)  		return -ENOENT;  	if (status) { -		err = cmd_status(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, -									EIO); +		err = cmd_status(cmd->sk, hdev->id, +						MGMT_OP_READ_LOCAL_OOB_DATA, +						mgmt_status(status));  	} else {  		struct mgmt_rp_read_local_oob_data rp;  		memcpy(rp.hash, hash, sizeof(rp.hash));  		memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); -		err = cmd_complete(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, -							&rp, sizeof(rp)); +		err = cmd_complete(cmd->sk, hdev->id, +						MGMT_OP_READ_LOCAL_OOB_DATA, +						&rp, sizeof(rp));  	}  	mgmt_pending_remove(cmd); @@ -2315,14 +2700,15 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,  	return err;  } -int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, -								u8 *eir) +int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, +				u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir)  {  	struct mgmt_ev_device_found ev;  	memset(&ev, 0, sizeof(ev)); -	bacpy(&ev.bdaddr, bdaddr); +	bacpy(&ev.addr.bdaddr, bdaddr); +	ev.addr.type = link_to_mgmt(link_type, addr_type);  	ev.rssi = rssi;  	if (eir) @@ -2331,10 +2717,10 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,  	if (dev_class)  		memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); -	return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL); +	return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL);  } -int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name) +int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name)  {  	struct mgmt_ev_remote_name ev; @@ -2343,37 +2729,79 @@ int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name)  	bacpy(&ev.bdaddr, bdaddr);  	memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); -	return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL); +	return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL); +} + +int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) +{ +	struct pending_cmd *cmd; +	int err; + +	cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); +	if (!cmd) +		return -ENOENT; + +	err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status)); +	mgmt_pending_remove(cmd); + +	return err; +} + +int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) +{ +	struct pending_cmd *cmd; +	int err; + +	cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); +	if (!cmd) +		return -ENOENT; + +	err = cmd_status(cmd->sk, hdev->id, cmd->opcode, status); +	mgmt_pending_remove(cmd); + +	return err;  } -int mgmt_discovering(u16 index, u8 discovering) +int mgmt_discovering(struct hci_dev *hdev, u8 discovering)  { -	return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering, +	struct pending_cmd *cmd; + +	if (discovering) +		cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); +	else +		cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); + +	if (cmd != NULL) { +		cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0); +		mgmt_pending_remove(cmd); +	} + +	return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering,  						sizeof(discovering), NULL);  } -int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr) +int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr)  {  	struct pending_cmd *cmd;  	struct mgmt_ev_device_blocked ev; -	cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index); +	cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);  	bacpy(&ev.bdaddr, bdaddr); -	return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev), -						cmd ? cmd->sk : NULL); +	return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), +							cmd ? cmd->sk : NULL);  } -int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr) +int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr)  {  	struct pending_cmd *cmd;  	struct mgmt_ev_device_unblocked ev; -	cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index); +	cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);  	bacpy(&ev.bdaddr, bdaddr); -	return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev), -						cmd ? cmd->sk : NULL); +	return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), +							cmd ? cmd->sk : NULL);  }  |