Commit 5f779bbd authored by John W. Linville's avatar John W. Linville
parents d7a4858c 5a13b095
......@@ -188,7 +188,7 @@ config BT_MRVL
The core driver to support Marvell Bluetooth devices.
This driver is required if you want to support
Marvell Bluetooth devices, such as 8688/8787.
Marvell Bluetooth devices, such as 8688/8787/8797.
Say Y here to compile Marvell Bluetooth driver
into the kernel or say M to compile it as module.
......@@ -201,8 +201,8 @@ config BT_MRVL_SDIO
The driver for Marvell Bluetooth chipsets with SDIO interface.
This driver is required if you want to use Marvell Bluetooth
devices with SDIO interface. Currently SD8688/SD8787 chipsets are
supported.
devices with SDIO interface. Currently SD8688/SD8787/SD8797
chipsets are supported.
Say Y here to compile support for Marvell BT-over-SDIO driver
into the kernel or say M to compile it as module.
......
......@@ -65,7 +65,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
.io_port_1 = 0x01,
.io_port_2 = 0x02,
};
static const struct btmrvl_sdio_card_reg btmrvl_reg_8787 = {
static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
.cfg = 0x00,
.host_int_mask = 0x02,
.host_intstatus = 0x03,
......@@ -92,7 +92,14 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
.helper = NULL,
.firmware = "mrvl/sd8787_uapsta.bin",
.reg = &btmrvl_reg_8787,
.reg = &btmrvl_reg_87xx,
.sd_blksz_fw_dl = 256,
};
static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
.helper = NULL,
.firmware = "mrvl/sd8797_uapsta.bin",
.reg = &btmrvl_reg_87xx,
.sd_blksz_fw_dl = 256,
};
......@@ -103,6 +110,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = {
/* Marvell SD8787 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
/* Marvell SD8797 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
.driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
{ } /* Terminating entry */
};
......@@ -1076,3 +1086,4 @@ MODULE_LICENSE("GPL v2");
MODULE_FIRMWARE("sd8688_helper.bin");
MODULE_FIRMWARE("sd8688.bin");
MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
......@@ -785,9 +785,8 @@ static int btusb_send_frame(struct sk_buff *skb)
usb_mark_last_busy(data->udev);
}
usb_free_urb(urb);
done:
usb_free_urb(urb);
return err;
}
......
......@@ -41,6 +41,8 @@
#define VERSION "1.3"
static bool amp;
struct vhci_data {
struct hci_dev *hdev;
......@@ -239,6 +241,9 @@ static int vhci_open(struct inode *inode, struct file *file)
hdev->bus = HCI_VIRTUAL;
hdev->driver_data = data;
if (amp)
hdev->dev_type = HCI_AMP;
hdev->open = vhci_open_dev;
hdev->close = vhci_close_dev;
hdev->flush = vhci_flush;
......@@ -303,6 +308,9 @@ static void __exit vhci_exit(void)
module_init(vhci_init);
module_exit(vhci_exit);
module_param(amp, bool, 0644);
MODULE_PARM_DESC(amp, "Create AMP controller device");
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
MODULE_VERSION(VERSION);
......
......@@ -36,6 +36,11 @@
#define PF_BLUETOOTH AF_BLUETOOTH
#endif
/* Bluetooth versions */
#define BLUETOOTH_VER_1_1 1
#define BLUETOOTH_VER_1_2 2
#define BLUETOOTH_VER_2_0 3
/* Reserv for core and drivers use */
#define BT_SKB_RESERVE 8
......
......@@ -88,6 +88,14 @@ enum {
HCI_RESET,
};
/*
* BR/EDR and/or LE controller flags: the flags defined here should represent
* states from the controller.
*/
enum {
HCI_LE_SCAN,
};
/* HCI ioctl defines */
#define HCIDEVUP _IOW('H', 201, int)
#define HCIDEVDOWN _IOW('H', 202, int)
......@@ -453,6 +461,14 @@ struct hci_rp_user_confirm_reply {
#define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d
#define HCI_OP_USER_PASSKEY_REPLY 0x042e
struct hci_cp_user_passkey_reply {
bdaddr_t bdaddr;
__le32 passkey;
} __packed;
#define HCI_OP_USER_PASSKEY_NEG_REPLY 0x042f
#define HCI_OP_REMOTE_OOB_DATA_REPLY 0x0430
struct hci_cp_remote_oob_data_reply {
bdaddr_t bdaddr;
......@@ -669,6 +685,12 @@ struct hci_rp_read_local_oob_data {
#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58
#define HCI_OP_READ_FLOW_CONTROL_MODE 0x0c66
struct hci_rp_read_flow_control_mode {
__u8 status;
__u8 mode;
} __packed;
#define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d
struct hci_cp_write_le_host_supported {
__u8 le;
......@@ -760,6 +782,15 @@ struct hci_rp_le_read_buffer_size {
__u8 le_max_pkt;
} __packed;
#define HCI_OP_LE_SET_SCAN_PARAM 0x200b
struct hci_cp_le_set_scan_param {
__u8 type;
__le16 interval;
__le16 window;
__u8 own_address_type;
__u8 filter_policy;
} __packed;
#define HCI_OP_LE_SET_SCAN_ENABLE 0x200c
struct hci_cp_le_set_scan_enable {
__u8 enable;
......@@ -1076,6 +1107,11 @@ struct hci_ev_user_confirm_req {
__le32 passkey;
} __packed;
#define HCI_EV_USER_PASSKEY_REQUEST 0x34
struct hci_ev_user_passkey_req {
bdaddr_t bdaddr;
} __packed;
#define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35
struct hci_ev_remote_oob_data_request {
bdaddr_t bdaddr;
......@@ -1331,4 +1367,6 @@ struct hci_inquiry_req {
};
#define IREQ_CACHE_FLUSH 0x0001
extern int enable_hs;
#endif /* __HCI_H */
......@@ -170,6 +170,8 @@ struct hci_dev {
__u32 amp_max_flush_to;
__u32 amp_be_flush_to;
__u8 flow_ctl_mode;
unsigned int auto_accept_delay;
unsigned long quirks;
......@@ -250,6 +252,8 @@ struct hci_dev {
struct module *owner;
unsigned long dev_flags;
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
......@@ -917,11 +921,13 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
u8 persistent);
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int mgmt_disconnect_failed(struct hci_dev *hdev);
int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
u8 status);
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type);
int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type);
int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 status);
int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
......@@ -933,14 +939,20 @@ int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status);
int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr);
int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status);
int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
u8 *randomizer, u8 status);
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
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);
int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name);
int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status);
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status);
int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
......
......@@ -792,7 +792,6 @@ static inline __u8 __ctrl_size(struct l2cap_chan *chan)
}
extern int disable_ertm;
extern int enable_hs;
int l2cap_init_sockets(void);
void l2cap_cleanup_sockets(void);
......@@ -810,5 +809,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan);
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
int l2cap_chan_check_security(struct l2cap_chan *chan);
#endif /* __L2CAP_H */
......@@ -23,6 +23,23 @@
#define MGMT_INDEX_NONE 0xFFFF
#define MGMT_STATUS_SUCCESS 0x00
#define MGMT_STATUS_UNKNOWN_COMMAND 0x01
#define MGMT_STATUS_NOT_CONNECTED 0x02
#define MGMT_STATUS_FAILED 0x03
#define MGMT_STATUS_CONNECT_FAILED 0x04
#define MGMT_STATUS_AUTH_FAILED 0x05
#define MGMT_STATUS_NOT_PAIRED 0x06
#define MGMT_STATUS_NO_RESOURCES 0x07
#define MGMT_STATUS_TIMEOUT 0x08
#define MGMT_STATUS_ALREADY_CONNECTED 0x09
#define MGMT_STATUS_BUSY 0x0a
#define MGMT_STATUS_REJECTED 0x0b
#define MGMT_STATUS_NOT_SUPPORTED 0x0c
#define MGMT_STATUS_INVALID_PARAMS 0x0d
#define MGMT_STATUS_DISCONNECTED 0x0e
#define MGMT_STATUS_NOT_POWERED 0x0f
struct mgmt_hdr {
__le16 opcode;
__le16 index;
......@@ -119,6 +136,10 @@ struct mgmt_cp_remove_keys {
bdaddr_t bdaddr;
__u8 disconnect;
} __packed;
struct mgmt_rp_remove_keys {
bdaddr_t bdaddr;
__u8 status;
};
#define MGMT_OP_DISCONNECT 0x000F
struct mgmt_cp_disconnect {
......@@ -126,11 +147,12 @@ struct mgmt_cp_disconnect {
} __packed;
struct mgmt_rp_disconnect {
bdaddr_t bdaddr;
__u8 status;
} __packed;
#define MGMT_ADDR_BREDR 0x00
#define MGMT_ADDR_LE 0x01
#define MGMT_ADDR_BREDR_LE 0x02
#define MGMT_ADDR_LE_PUBLIC 0x01
#define MGMT_ADDR_LE_RANDOM 0x02
#define MGMT_ADDR_INVALID 0xff
struct mgmt_addr_info {
......@@ -167,11 +189,11 @@ struct mgmt_cp_set_io_capability {
#define MGMT_OP_PAIR_DEVICE 0x0014
struct mgmt_cp_pair_device {
bdaddr_t bdaddr;
struct mgmt_addr_info addr;
__u8 io_cap;
} __packed;
struct mgmt_rp_pair_device {
bdaddr_t bdaddr;
struct mgmt_addr_info addr;
__u8 status;
} __packed;
......@@ -210,6 +232,9 @@ struct mgmt_cp_remove_remote_oob_data {
} __packed;
#define MGMT_OP_START_DISCOVERY 0x001B
struct mgmt_cp_start_discovery {
__u8 type;
} __packed;
#define MGMT_OP_STOP_DISCOVERY 0x001C
......@@ -228,6 +253,17 @@ struct mgmt_cp_set_fast_connectable {
__u8 enable;
} __packed;
#define MGMT_OP_USER_PASSKEY_REPLY 0x0020
struct mgmt_cp_user_passkey_reply {
bdaddr_t bdaddr;
__le32 passkey;
} __packed;
#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x0021
struct mgmt_cp_user_passkey_neg_reply {
bdaddr_t bdaddr;
} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
......@@ -322,3 +358,8 @@ struct mgmt_ev_device_blocked {
struct mgmt_ev_device_unblocked {
bdaddr_t bdaddr;
} __packed;
#define MGMT_EV_USER_PASSKEY_REQUEST 0x0017
struct mgmt_ev_user_passkey_request {
bdaddr_t bdaddr;
} __packed;
......@@ -77,17 +77,12 @@ static struct bnep_session *__bnep_get_session(u8 *dst)
static void __bnep_link_session(struct bnep_session *s)
{
/* It's safe to call __module_get() here because sessions are added
by the socket layer which has to hold the reference to this module.
*/
__module_get(THIS_MODULE);
list_add(&s->list, &bnep_session_list);
}
static void __bnep_unlink_session(struct bnep_session *s)
{
list_del(&s->list);
module_put(THIS_MODULE);
}
static int bnep_send(struct bnep_session *s, void *data, size_t len)
......@@ -528,6 +523,7 @@ static int bnep_session(void *arg)
up_write(&bnep_session_sem);
free_netdev(dev);
module_put_and_exit(0);
return 0;
}
......@@ -614,9 +610,11 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
__bnep_link_session(s);
__module_get(THIS_MODULE);
s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
if (IS_ERR(s->task)) {
/* Session thread start failed, gotta cleanup. */
module_put(THIS_MODULE);
unregister_netdev(dev);
__bnep_unlink_session(s);
err = PTR_ERR(s->task);
......
......@@ -65,14 +65,12 @@ static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
static void __cmtp_link_session(struct cmtp_session *session)
{
__module_get(THIS_MODULE);
list_add(&session->list, &cmtp_session_list);
}
static void __cmtp_unlink_session(struct cmtp_session *session)
{
list_del(&session->list);
module_put(THIS_MODULE);
}
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
......@@ -325,6 +323,7 @@ static int cmtp_session(void *arg)
up_write(&cmtp_session_sem);
kfree(session);
module_put_and_exit(0);
return 0;
}
......@@ -374,9 +373,11 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
__cmtp_link_session(session);
__module_get(THIS_MODULE);
session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
session->num);
if (IS_ERR(session->task)) {
module_put(THIS_MODULE);
err = PTR_ERR(session->task);
goto unlink;
}
......
......@@ -123,7 +123,7 @@ static void hci_acl_connect_cancel(struct hci_conn *conn)
BT_DBG("%p", conn);
if (conn->hdev->hci_ver < 2)
if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2)
return;
bacpy(&cp.bdaddr, &conn->dst);
......
......@@ -54,6 +54,8 @@
#define AUTO_OFF_TIMEOUT 2000
int enable_hs;
static void hci_cmd_task(unsigned long arg);
static void hci_rx_task(unsigned long arg);
static void hci_tx_task(unsigned long arg);
......@@ -228,18 +230,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
/* Read Buffer Size (ACL mtu, max pkt, etc.) */
hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
#if 0
/* Host buffer size */
{
struct hci_cp_host_buffer_size cp;
cp.acl_mtu = cpu_to_le16(HCI_MAX_ACL_SIZE);
cp.sco_mtu = HCI_MAX_SCO_SIZE;
cp.acl_max_pkt = cpu_to_le16(0xffff);
cp.sco_max_pkt = cpu_to_le16(0xffff);
hci_send_cmd(hdev, HCI_OP_HOST_BUFFER_SIZE, sizeof(cp), &cp);
}
#endif
/* Read BD Address */
hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
......@@ -521,8 +511,9 @@ int hci_dev_open(__u16 dev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
set_bit(HCI_RAW, &hdev->flags);
/* Treat all non BR/EDR controllers as raw devices for now */
if (hdev->dev_type != HCI_BREDR)
/* Treat all non BR/EDR controllers as raw devices if
enable_hs is not set */
if (hdev->dev_type != HCI_BREDR && !enable_hs)
set_bit(HCI_RAW, &hdev->flags);
if (hdev->open(hdev)) {
......@@ -1336,14 +1327,12 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct bdaddr_list *entry;
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
if (bacmp(bdaddr, BDADDR_ANY) == 0)
return hci_blacklist_clear(hdev);
}
entry = hci_blacklist_lookup(hdev, bdaddr);
if (!entry) {
if (!entry)
return -ENOENT;
}
list_del(&entry->list);
kfree(entry);
......@@ -1451,12 +1440,13 @@ int hci_register_dev(struct hci_dev *hdev)
sprintf(hdev->name, "hci%d", id);
hdev->id = id;
list_add(&hdev->list, head);
list_add_tail(&hdev->list, head);
atomic_set(&hdev->refcnt, 1);
spin_lock_init(&hdev->lock);
hdev->flags = 0;
hdev->dev_flags = 0;
hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
hdev->esco_type = (ESCO_HV1);
hdev->link_mode = (HCI_LM_ACCEPT);
......@@ -2614,3 +2604,6 @@ int hci_cancel_inquiry(struct hci_dev *hdev)
return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
}
module_param(enable_hs, bool, 0644);
MODULE_PARM_DESC(enable_hs, "Enable High Speed");
......@@ -55,8 +55,12 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
if (status)
if (status) {
hci_dev_lock(hdev);
mgmt_stop_discovery_failed(hdev, status);
hci_dev_unlock(hdev);
return;
}
clear_bit(HCI_INQUIRY, &hdev->flags);
......@@ -190,6 +194,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
clear_bit(HCI_RESET, &hdev->flags);
hci_req_complete(hdev, HCI_OP_RESET, status);
hdev->dev_flags = 0;
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
......@@ -494,7 +500,7 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
/* CSR 1.1 dongles does not accept any bitfield so don't try to set
* any event mask for pre 1.2 devices */
if (hdev->lmp_ver <= 1)
if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return;
events[4] |= 0x01; /* Flow Specification Complete */
......@@ -558,7 +564,7 @@ static void hci_setup(struct hci_dev *hdev)
{
hci_setup_event_mask(hdev);
if (hdev->lmp_ver > 1)
if (hdev->hci_ver > BLUETOOTH_VER_1_1)
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
......@@ -713,6 +719,21 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status);
}
static void hci_cc_read_flow_control_mode(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_rp_read_flow_control_mode *rp = (void *) skb->data;
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
return;
hdev->flow_ctl_mode = rp->mode;
hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status);
}
static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_read_buffer_size *rp = (void *) skb->data;
......@@ -927,6 +948,37 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
BT_DBG("%s status 0x%x", hdev->name, rp->status);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr,
rp->status);
hci_dev_unlock(hdev);
}
static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
BT_DBG("%s status 0x%x", hdev->name, rp->status);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
rp->status);
hci_dev_unlock(hdev);
}
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
struct sk_buff *skb)
{
......@@ -940,6 +992,13 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
}
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
struct sk_buff *skb)
{
......@@ -956,12 +1015,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
return;
if (cp->enable == 0x01) {
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
del_timer(&hdev->adv_timer);
hci_dev_lock(hdev);
hci_adv_entries_clear(hdev);
hci_dev_unlock(hdev);
} else if (cp->enable == 0x00) {
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT);
}
}
......@@ -1014,7 +1077,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
hci_conn_check_pending(hdev);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_inquiry_failed(hdev, status);
mgmt_start_discovery_failed(hdev, status);
hci_dev_unlock(hdev);
return;
}
......@@ -1437,7 +1500,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.rssi = 0x00;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, 0, NULL);
}
......@@ -1472,7 +1535,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG;
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
mgmt_connected(hdev, &ev->bdaddr, conn->type);
mgmt_connected(hdev, &ev->bdaddr, conn->type,
conn->dst_type);
} else
conn->state = BT_CONNECTED;
......@@ -1494,7 +1558,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
}
/* Set packet type for incoming connection */
if (!conn->out && hdev->hci_ver < 3) {
if (!conn->out && hdev->hci_ver < BLUETOOTH_VER_2_0) {
struct hci_cp_change_conn_ptype cp;
cp.handle = ev->handle;
cp.pkt_type = cpu_to_le16(conn->pkt_type);
......@@ -1505,7 +1569,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK)
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
ev->status);
conn->dst_type, ev->status);
}
if (conn->type == ACL_LINK)
......@@ -1604,26 +1668,27 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, ev->status);
if (ev->status) {
hci_dev_lock(hdev);
mgmt_disconnect_failed(hdev);
hci_dev_unlock(hdev);
return;
}
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (!conn)
goto unlock;
if (ev->status == 0)
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK || conn->type == LE_LINK)
mgmt_disconnected(hdev, &conn->dst, conn->type);
if (conn->type == ACL_LINK || conn->type == LE_LINK) {
if (ev->status != 0)
mgmt_disconnect_failed(hdev, &conn->dst, ev->status);
else
mgmt_disconnected(hdev, &conn->dst, conn->type,
conn->dst_type);
}
if (ev->status == 0) {
hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
}
unlock:
hci_dev_unlock(hdev);
......@@ -1961,6 +2026,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_write_ca_timeout(hdev, skb);
break;
case HCI_OP_READ_FLOW_CONTROL_MODE:
hci_cc_read_flow_control_mode(hdev, skb);
break;
case HCI_OP_READ_LOCAL_AMP_INFO:
hci_cc_read_local_amp_info(hdev, skb);
break;
......@@ -2009,6 +2078,17 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_user_confirm_neg_reply(hdev, skb);
break;
case HCI_OP_USER_PASSKEY_REPLY:
hci_cc_user_passkey_reply(hdev, skb);
break;
case HCI_OP_USER_PASSKEY_NEG_REPLY:
hci_cc_user_passkey_neg_reply(hdev, skb);
case HCI_OP_LE_SET_SCAN_PARAM:
hci_cc_le_set_scan_param(hdev, skb);
break;
case HCI_OP_LE_SET_SCAN_ENABLE:
hci_cc_le_set_scan_enable(hdev, skb);
break;
......@@ -2096,7 +2176,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
case HCI_OP_DISCONNECT:
if (ev->status != 0)
mgmt_disconnect_failed(hdev);
mgmt_disconnect_failed(hdev, NULL, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
......@@ -2444,7 +2524,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, info->rssi,
NULL);
}
......@@ -2461,7 +2541,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, info->rssi,
NULL);
}
......@@ -2604,7 +2684,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x01;
hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, info->rssi, info->data);
}
......@@ -2768,6 +2848,21 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
static inline void hci_user_passkey_request_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_ev_user_passkey_req *ev = (void *) skb->data;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_user_passkey_request(hdev, &ev->bdaddr);
hci_dev_unlock(hdev);
}
static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_simple_pair_complete *ev = (void *) skb->data;
......@@ -2868,14 +2963,15 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
}
if (ev->status) {
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status);
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
conn->dst_type, ev->status);
hci_proto_connect_cfm(conn, ev->status);
conn->state = BT_CLOSED;
hci_conn_del(conn);
goto unlock;
}
mgmt_connected(hdev, &ev->bdaddr, conn->type);
mgmt_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
......@@ -3106,6 +3202,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_user_confirm_request_evt(hdev, skb);
break;
case HCI_EV_USER_PASSKEY_REQUEST:
hci_user_passkey_request_evt(hdev, skb);
break;
case HCI_EV_SIMPLE_PAIR_COMPLETE:
hci_simple_pair_complete_evt(hdev, skb);
break;
......
......@@ -57,7 +57,6 @@
#include <net/bluetooth/smp.h>
int disable_ertm;
int enable_hs;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
......@@ -97,7 +96,6 @@ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16
return c;
}
return NULL;
}
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
......@@ -154,12 +152,9 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
list_for_each_entry(c, &chan_list, global_l) {
if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
goto found;
}
c = NULL;
found:
return c;
}
return NULL;
}
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
......@@ -234,8 +229,37 @@ static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer)
chan_put(chan);
}
static char *state_to_string(int state)
{
switch(state) {
case BT_CONNECTED:
return "BT_CONNECTED";
case BT_OPEN:
return "BT_OPEN";
case BT_BOUND:
return "BT_BOUND";
case BT_LISTEN:
return "BT_LISTEN";
case BT_CONNECT:
return "BT_CONNECT";
case BT_CONNECT2:
return "BT_CONNECT2";
case BT_CONFIG:
return "BT_CONFIG";
case BT_DISCONN:
return "BT_DISCONN";
case BT_CLOSED:
return "BT_CLOSED";
}
return "invalid state";
}
static void l2cap_state_change(struct l2cap_chan *chan, int state)
{
BT_DBG("%p %s -> %s", chan, state_to_string(chan->state),
state_to_string(state));
chan->state = state;
chan->ops->state_change(chan->data, state);
}
......@@ -518,7 +542,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
}
/* Service level security */
static inline int l2cap_check_security(struct l2cap_chan *chan)
int l2cap_chan_check_security(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
__u8 auth_type;
......@@ -664,7 +688,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return;
if (l2cap_check_security(chan) &&
if (l2cap_chan_check_security(chan) &&
__l2cap_no_conn_pending(chan)) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(chan->scid);
......@@ -754,7 +778,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
if (chan->state == BT_CONNECT) {
struct l2cap_conn_req req;
if (!l2cap_check_security(chan) ||
if (!l2cap_chan_check_security(chan) ||
!__l2cap_no_conn_pending(chan)) {
bh_unlock_sock(sk);
continue;
......@@ -787,7 +811,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
if (l2cap_check_security(chan)) {
if (l2cap_chan_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent;
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
......@@ -1181,7 +1205,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
if (l2cap_check_security(chan))
if (l2cap_chan_check_security(chan))
l2cap_state_change(chan, BT_CONNECTED);
} else
l2cap_do_start(chan);
......@@ -1318,14 +1342,12 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
if (!skb)
return;
do {
if (bt_cb(skb)->tx_seq == tx_seq)
break;
while (bt_cb(skb)->tx_seq != tx_seq) {
if (skb_queue_is_last(&chan->tx_q, skb))
return;
} while ((skb = skb_queue_next(&chan->tx_q, skb)));
skb = skb_queue_next(&chan->tx_q, skb);
}
if (chan->remote_max_tx &&
bt_cb(skb)->retries == chan->remote_max_tx) {
......@@ -1906,7 +1928,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
{
struct l2cap_conf_efs efs;
switch(chan->mode) {
switch (chan->mode) {
case L2CAP_MODE_ERTM:
efs.id = chan->local_id;
efs.stype = chan->local_stype;
......@@ -2606,7 +2628,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_check_security(chan)) {
if (l2cap_chan_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
......@@ -3019,7 +3041,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan,BT_DISCONN);
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
......@@ -3562,14 +3584,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
bt_cb(skb)->sar = sar;
next_skb = skb_peek(&chan->srej_q);
if (!next_skb) {
__skb_queue_tail(&chan->srej_q, skb);
return 0;
}
tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
do {
while (next_skb) {
if (bt_cb(next_skb)->tx_seq == tx_seq)
return -EINVAL;
......@@ -3582,9 +3600,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
}
if (skb_queue_is_last(&chan->srej_q, next_skb))
break;
} while ((next_skb = skb_queue_next(&chan->srej_q, next_skb)));
next_skb = NULL;
else
next_skb = skb_queue_next(&chan->srej_q, next_skb);
}
__skb_queue_tail(&chan->srej_q, skb);
......@@ -3788,7 +3807,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
}
}
static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
{
struct srej_list *new;
u32 control;
......@@ -3799,6 +3818,9 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
l2cap_send_sframe(chan, control);
new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
if (!new)
return -ENOMEM;
new->tx_seq = chan->expected_tx_seq;
chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
......@@ -3807,6 +3829,8 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
}
chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
return 0;
}
static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
......@@ -3877,7 +3901,12 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
return 0;
}
}
l2cap_send_srejframe(chan, tx_seq);
err = l2cap_send_srejframe(chan, tx_seq);
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, -err);
return err;
}
}
} else {
expected_tx_seq_offset = __seq_offset(chan,
......@@ -3899,7 +3928,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
set_bit(CONN_SEND_PBIT, &chan->conn_state);
l2cap_send_srejframe(chan, tx_seq);
err = l2cap_send_srejframe(chan, tx_seq);
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, -err);
return err;
}
__clear_ack_timer(chan);
}
......@@ -3928,11 +3961,12 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
l2cap_retransmit_frames(chan);
}
__set_ack_timer(chan);
chan->num_acked = (chan->num_acked + 1) % num_to_ack;
if (chan->num_acked == num_to_ack - 1)
l2cap_send_ack(chan);
else
__set_ack_timer(chan);
return 0;
......@@ -4768,6 +4802,3 @@ void l2cap_exit(void)
module_param(disable_ertm, bool, 0644);
MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
module_param(enable_hs, bool, 0644);
MODULE_PARM_DESC(enable_hs, "Enable High Speed");
......@@ -626,8 +626,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
chan->sec_level = sec.level;
if (!chan->conn)
break;
conn = chan->conn;
if (conn && chan->scid == L2CAP_CID_LE_DATA) {
/*change security for LE channels */
if (chan->scid == L2CAP_CID_LE_DATA) {
if (!conn->hcon->out) {
err = -EINVAL;
break;
......@@ -635,9 +640,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
if (smp_conn_security(conn, sec.level))
break;
err = 0;
sk->sk_state = BT_CONFIG;
/* or for ACL link, under defer_setup time */
} else if (sk->sk_state == BT_CONNECT2 &&
bt_sk(sk)->defer_setup) {
err = l2cap_chan_check_security(chan);
} else {
err = -EINVAL;
}
break;
......
......@@ -22,6 +22,7 @@
/* Bluetooth HCI Management interface */
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <asm/unaligned.h>
......@@ -44,6 +45,79 @@ struct pending_cmd {
void *user_data;
};
/* 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;
......@@ -178,7 +252,8 @@ 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);
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
cancel_delayed_work_sync(&hdev->power_off);
......@@ -291,6 +366,15 @@ static void mgmt_pending_remove(struct pending_cmd *cmd)
mgmt_pending_free(cmd);
}
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_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
{
struct mgmt_mode *cp;
......@@ -303,22 +387,25 @@ 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);
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_mode_rsp(sk, index, MGMT_OP_SET_POWERED, cp->val);
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY);
err = cmd_status(sk, index, MGMT_OP_SET_POWERED,
MGMT_STATUS_BUSY);
goto failed;
}
......@@ -355,28 +442,33 @@ 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);
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, hdev) ||
mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY);
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_mode_rsp(sk, index, MGMT_OP_SET_DISCOVERABLE,
cp->val);
goto failed;
}
......@@ -421,27 +513,32 @@ 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);
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, hdev) ||
mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY);
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_mode_rsp(sk, index, MGMT_OP_SET_CONNECTABLE,
cp->val);
goto failed;
}
......@@ -496,15 +593,6 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
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)
{
......@@ -517,11 +605,13 @@ 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);
......@@ -730,11 +820,13 @@ 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);
......@@ -779,11 +871,13 @@ 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);
......@@ -805,7 +899,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;
}
......@@ -838,11 +933,13 @@ 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);
......@@ -870,11 +967,13 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
cp = (void *) data;
if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, EINVAL);
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE,
MGMT_STATUS_INVALID_PARAMS);
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE,
MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev);
......@@ -914,7 +1013,8 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
cp = (void *) data;
if (len < sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL);
return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
MGMT_STATUS_INVALID_PARAMS);
key_count = get_unaligned_le16(&cp->key_count);
......@@ -923,12 +1023,14 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
if (expected_len != len) {
BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
len, expected_len);
return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, 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_LINK_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);
......@@ -951,6 +1053,8 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
key->pin_len);
}
cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
......@@ -962,41 +1066,64 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
{
struct hci_dev *hdev;
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_KEYS, 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_KEYS, ENODEV);
return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS,
MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(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_KEYS, -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;
}
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:
if (err < 0)
err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
sizeof(rp));
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
......@@ -1017,21 +1144,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);
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, hdev)) {
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY);
err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
MGMT_STATUS_BUSY);
goto failed;
}
......@@ -1040,7 +1171,8 @@ 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;
}
......@@ -1064,11 +1196,18 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
return err;
}
static u8 link_to_mgmt(u8 link_type)
static u8 link_to_mgmt(u8 link_type, u8 addr_type)
{
switch (link_type) {
case LE_LINK:
return MGMT_ADDR_LE;
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:
......@@ -1090,7 +1229,8 @@ 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);
......@@ -1111,7 +1251,7 @@ static int get_connections(struct sock *sk, u16 index)
i = 0;
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);
rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
if (rp->addr[i].type == MGMT_ADDR_INVALID)
continue;
i++;
......@@ -1164,22 +1304,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);
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;
}
......@@ -1191,7 +1335,7 @@ 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;
}
......@@ -1230,18 +1374,18 @@ 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);
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;
}
......@@ -1265,11 +1409,13 @@ 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);
......@@ -1307,7 +1453,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));
......@@ -1325,27 +1472,22 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
static void pairing_complete_cb(struct hci_conn *conn, u8 status)
{
struct pending_cmd *cmd;
struct hci_dev *hdev = conn->hdev;
BT_DBG("status %u", status);
hci_dev_lock_bh(hdev);
cmd = find_pairing(conn);
if (!cmd)
BT_DBG("Unable to find a pending command");
else
pairing_complete(cmd, status);
hci_dev_unlock_bh(hdev);
}
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;
......@@ -1355,11 +1497,13 @@ 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);
......@@ -1369,22 +1513,29 @@ 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;
}
......@@ -1396,7 +1547,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;
......@@ -1417,56 +1568,138 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
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("");
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;
}
if (len != sizeof(*cp))
return cmd_status(sk, index, mgmt_op, EINVAL);
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, mgmt_op, ENODEV);
return cmd_status(sk, index, mgmt_op,
MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
goto failed;
err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
goto done;
}
/*
* 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;
}
cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len);
/* Continue with pairing via SMP */
err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_SUCCESS);
goto done;
}
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:
done:
hci_dev_unlock_bh(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_reply *cp = (void *) 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)
{
......@@ -1479,11 +1712,13 @@ 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);
......@@ -1517,24 +1752,25 @@ 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);
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, hdev)) {
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY);
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
MGMT_STATUS_BUSY);
goto unlock;
}
......@@ -1566,19 +1802,20 @@ 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);
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);
......@@ -1600,19 +1837,19 @@ 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);
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);
......@@ -1623,22 +1860,30 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
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)
{
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);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENETDOWN);
err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
MGMT_STATUS_NOT_POWERED);
goto failed;
}
......@@ -1669,7 +1914,8 @@ 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);
......@@ -1701,18 +1947,19 @@ 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);
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);
......@@ -1734,19 +1981,20 @@ 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);
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);
......@@ -1770,12 +2018,12 @@ 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);
......@@ -1793,14 +2041,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;
}
......@@ -1903,10 +2151,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);
......@@ -1922,7 +2178,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);
......@@ -1939,7 +2195,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
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;
}
......@@ -2062,13 +2319,15 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
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, &status);
cmd_status_rsp, &mgmt_err);
if (scan & SCAN_INQUIRY)
mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
cmd_status_rsp, &status);
cmd_status_rsp, &mgmt_err);
return 0;
}
......@@ -2089,12 +2348,13 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
}
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type)
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type)
{
struct mgmt_addr_info ev;
bacpy(&ev.bdaddr, bdaddr);
ev.type = link_to_mgmt(link_type);
ev.type = link_to_mgmt(link_type, addr_type);
return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL);
}
......@@ -2106,6 +2366,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));
......@@ -2115,7 +2376,25 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_remove(cmd);
}
int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
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_addr_info ev;
struct sock *sk = NULL;
......@@ -2124,40 +2403,53 @@ int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
bacpy(&ev.bdaddr, bdaddr);
ev.type = link_to_mgmt(type);
ev.type = link_to_mgmt(link_type, addr_type);
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(struct hci_dev *hdev)
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, hdev);
if (!cmd)
return -ENOENT;
err = cmd_status(cmd->sk, hdev->id, 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(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
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.addr.bdaddr, bdaddr);
ev.addr.type = link_to_mgmt(type);
ev.status = status;
ev.addr.type = link_to_mgmt(link_type, addr_type);
ev.status = mgmt_status(status);
return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
}
......@@ -2185,7 +2477,7 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
return -ENOENT;
bacpy(&rp.bdaddr, bdaddr);
rp.status = status;
rp.status = mgmt_status(status);
err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
sizeof(rp));
......@@ -2207,7 +2499,7 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
return -ENOENT;
bacpy(&rp.bdaddr, bdaddr);
rp.status = status;
rp.status = mgmt_status(status);
err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
sizeof(rp));
......@@ -2232,7 +2524,19 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
NULL);
}
static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
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 user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status, u8 opcode)
{
struct pending_cmd *cmd;
......@@ -2244,7 +2548,7 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
return -ENOENT;
bacpy(&rp.bdaddr, bdaddr);
rp.status = status;
rp.status = mgmt_status(status);
err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
mgmt_pending_remove(cmd);
......@@ -2255,23 +2559,37 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status)
{
return confirm_reply_complete(hdev, bdaddr, status,
return user_pairing_resp_complete(hdev, bdaddr, status,
MGMT_OP_USER_CONFIRM_REPLY);
}
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status)
{
return confirm_reply_complete(hdev, bdaddr, status,
return user_pairing_resp_complete(hdev, bdaddr, status,
MGMT_OP_USER_CONFIRM_NEG_REPLY);
}
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, hdev, &ev, sizeof(ev), NULL);
}
......@@ -2291,7 +2609,7 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
if (status) {
err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
EIO);
mgmt_status(status));
goto failed;
}
......@@ -2326,7 +2644,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
if (status) {
err = cmd_status(cmd->sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_DATA, EIO);
MGMT_OP_READ_LOCAL_OOB_DATA,
mgmt_status(status));
} else {
struct mgmt_rp_read_local_oob_data rp;
......@@ -2343,15 +2662,15 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
return err;
}
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
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.addr.bdaddr, bdaddr);
ev.addr.type = link_to_mgmt(type);
ev.addr.type = link_to_mgmt(link_type, addr_type);
ev.rssi = rssi;
if (eir)
......@@ -2375,7 +2694,7 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name)
return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL);
}
int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status)
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
int err;
......@@ -2384,6 +2703,21 @@ int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status)
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);
......
......@@ -232,6 +232,18 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
return 0;
}
static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
{
if (send)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason);
del_timer(&conn->security_timer);
smp_chan_destroy(conn);
}
static void confirm_work(struct work_struct *work)
{
struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
......@@ -270,8 +282,7 @@ static void confirm_work(struct work_struct *work)
return;
error:
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
smp_chan_destroy(conn);
smp_failure(conn, reason, 1);
}
static void random_work(struct work_struct *work)
......@@ -354,8 +365,7 @@ static void random_work(struct work_struct *work)
return;
error:
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
smp_chan_destroy(conn);
smp_failure(conn, reason, 1);
}
static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
......@@ -379,7 +389,15 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
void smp_chan_destroy(struct l2cap_conn *conn)
{
kfree(conn->smp_chan);
struct smp_chan *smp = conn->smp_chan;
clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
if (smp->tfm)
crypto_free_blkcipher(smp->tfm);
kfree(smp);
conn->smp_chan = NULL;
hci_conn_put(conn->hcon);
}
......@@ -647,6 +665,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
break;
case SMP_CMD_PAIRING_FAIL:
smp_failure(conn, skb->data[0], 0);
reason = 0;
err = -EPERM;
break;
......@@ -692,8 +711,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
done:
if (reason)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
smp_failure(conn, reason, 1);
kfree_skb(skb);
return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment