提交 5f779bbd 编写于 作者: J John W. Linville
...@@ -188,7 +188,7 @@ config BT_MRVL ...@@ -188,7 +188,7 @@ config BT_MRVL
The core driver to support Marvell Bluetooth devices. The core driver to support Marvell Bluetooth devices.
This driver is required if you want to support 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 Say Y here to compile Marvell Bluetooth driver
into the kernel or say M to compile it as module. into the kernel or say M to compile it as module.
...@@ -201,8 +201,8 @@ config BT_MRVL_SDIO ...@@ -201,8 +201,8 @@ config BT_MRVL_SDIO
The driver for Marvell Bluetooth chipsets with SDIO interface. The driver for Marvell Bluetooth chipsets with SDIO interface.
This driver is required if you want to use Marvell Bluetooth This driver is required if you want to use Marvell Bluetooth
devices with SDIO interface. Currently SD8688/SD8787 chipsets are devices with SDIO interface. Currently SD8688/SD8787/SD8797
supported. chipsets are supported.
Say Y here to compile support for Marvell BT-over-SDIO driver Say Y here to compile support for Marvell BT-over-SDIO driver
into the kernel or say M to compile it as module. 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 = { ...@@ -65,7 +65,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
.io_port_1 = 0x01, .io_port_1 = 0x01,
.io_port_2 = 0x02, .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, .cfg = 0x00,
.host_int_mask = 0x02, .host_int_mask = 0x02,
.host_intstatus = 0x03, .host_intstatus = 0x03,
...@@ -92,7 +92,14 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { ...@@ -92,7 +92,14 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
.helper = NULL, .helper = NULL,
.firmware = "mrvl/sd8787_uapsta.bin", .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, .sd_blksz_fw_dl = 256,
}; };
...@@ -103,6 +110,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = { ...@@ -103,6 +110,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = {
/* Marvell SD8787 Bluetooth device */ /* Marvell SD8787 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A), { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
.driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, .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 */ { } /* Terminating entry */
}; };
...@@ -1076,3 +1086,4 @@ MODULE_LICENSE("GPL v2"); ...@@ -1076,3 +1086,4 @@ MODULE_LICENSE("GPL v2");
MODULE_FIRMWARE("sd8688_helper.bin"); MODULE_FIRMWARE("sd8688_helper.bin");
MODULE_FIRMWARE("sd8688.bin"); MODULE_FIRMWARE("sd8688.bin");
MODULE_FIRMWARE("mrvl/sd8787_uapsta.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) ...@@ -785,9 +785,8 @@ static int btusb_send_frame(struct sk_buff *skb)
usb_mark_last_busy(data->udev); usb_mark_last_busy(data->udev);
} }
usb_free_urb(urb);
done: done:
usb_free_urb(urb);
return err; return err;
} }
......
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#define VERSION "1.3" #define VERSION "1.3"
static bool amp;
struct vhci_data { struct vhci_data {
struct hci_dev *hdev; struct hci_dev *hdev;
...@@ -239,6 +241,9 @@ static int vhci_open(struct inode *inode, struct file *file) ...@@ -239,6 +241,9 @@ static int vhci_open(struct inode *inode, struct file *file)
hdev->bus = HCI_VIRTUAL; hdev->bus = HCI_VIRTUAL;
hdev->driver_data = data; hdev->driver_data = data;
if (amp)
hdev->dev_type = HCI_AMP;
hdev->open = vhci_open_dev; hdev->open = vhci_open_dev;
hdev->close = vhci_close_dev; hdev->close = vhci_close_dev;
hdev->flush = vhci_flush; hdev->flush = vhci_flush;
...@@ -303,6 +308,9 @@ static void __exit vhci_exit(void) ...@@ -303,6 +308,9 @@ static void __exit vhci_exit(void)
module_init(vhci_init); module_init(vhci_init);
module_exit(vhci_exit); 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_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION); MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
......
...@@ -36,6 +36,11 @@ ...@@ -36,6 +36,11 @@
#define PF_BLUETOOTH AF_BLUETOOTH #define PF_BLUETOOTH AF_BLUETOOTH
#endif #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 */ /* Reserv for core and drivers use */
#define BT_SKB_RESERVE 8 #define BT_SKB_RESERVE 8
......
...@@ -88,6 +88,14 @@ enum { ...@@ -88,6 +88,14 @@ enum {
HCI_RESET, 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 */ /* HCI ioctl defines */
#define HCIDEVUP _IOW('H', 201, int) #define HCIDEVUP _IOW('H', 201, int)
#define HCIDEVDOWN _IOW('H', 202, int) #define HCIDEVDOWN _IOW('H', 202, int)
...@@ -453,6 +461,14 @@ struct hci_rp_user_confirm_reply { ...@@ -453,6 +461,14 @@ struct hci_rp_user_confirm_reply {
#define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d #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 #define HCI_OP_REMOTE_OOB_DATA_REPLY 0x0430
struct hci_cp_remote_oob_data_reply { struct hci_cp_remote_oob_data_reply {
bdaddr_t bdaddr; bdaddr_t bdaddr;
...@@ -669,6 +685,12 @@ struct hci_rp_read_local_oob_data { ...@@ -669,6 +685,12 @@ struct hci_rp_read_local_oob_data {
#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58 #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 #define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d
struct hci_cp_write_le_host_supported { struct hci_cp_write_le_host_supported {
__u8 le; __u8 le;
...@@ -760,6 +782,15 @@ struct hci_rp_le_read_buffer_size { ...@@ -760,6 +782,15 @@ struct hci_rp_le_read_buffer_size {
__u8 le_max_pkt; __u8 le_max_pkt;
} __packed; } __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 #define HCI_OP_LE_SET_SCAN_ENABLE 0x200c
struct hci_cp_le_set_scan_enable { struct hci_cp_le_set_scan_enable {
__u8 enable; __u8 enable;
...@@ -1076,6 +1107,11 @@ struct hci_ev_user_confirm_req { ...@@ -1076,6 +1107,11 @@ struct hci_ev_user_confirm_req {
__le32 passkey; __le32 passkey;
} __packed; } __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 #define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35
struct hci_ev_remote_oob_data_request { struct hci_ev_remote_oob_data_request {
bdaddr_t bdaddr; bdaddr_t bdaddr;
...@@ -1331,4 +1367,6 @@ struct hci_inquiry_req { ...@@ -1331,4 +1367,6 @@ struct hci_inquiry_req {
}; };
#define IREQ_CACHE_FLUSH 0x0001 #define IREQ_CACHE_FLUSH 0x0001
extern int enable_hs;
#endif /* __HCI_H */ #endif /* __HCI_H */
...@@ -170,6 +170,8 @@ struct hci_dev { ...@@ -170,6 +170,8 @@ struct hci_dev {
__u32 amp_max_flush_to; __u32 amp_max_flush_to;
__u32 amp_be_flush_to; __u32 amp_be_flush_to;
__u8 flow_ctl_mode;
unsigned int auto_accept_delay; unsigned int auto_accept_delay;
unsigned long quirks; unsigned long quirks;
...@@ -250,6 +252,8 @@ struct hci_dev { ...@@ -250,6 +252,8 @@ struct hci_dev {
struct module *owner; struct module *owner;
unsigned long dev_flags;
int (*open)(struct hci_dev *hdev); int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev);
...@@ -917,11 +921,13 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable); ...@@ -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_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
u8 persistent); u8 persistent);
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); u8 addr_type);
int mgmt_disconnect_failed(struct hci_dev *hdev); int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, u8 addr_type);
u8 status); 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_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status); u8 status);
...@@ -933,14 +939,20 @@ int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, ...@@ -933,14 +939,20 @@ int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status); u8 status);
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status); 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_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_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, int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
u8 *randomizer, u8 status); u8 *randomizer, u8 status);
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 *dev_class, s8 rssi, u8 *eir); 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_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_discovering(struct hci_dev *hdev, u8 discovering);
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
int mgmt_device_unblocked(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) ...@@ -792,7 +792,6 @@ static inline __u8 __ctrl_size(struct l2cap_chan *chan)
} }
extern int disable_ertm; extern int disable_ertm;
extern int enable_hs;
int l2cap_init_sockets(void); int l2cap_init_sockets(void);
void l2cap_cleanup_sockets(void); void l2cap_cleanup_sockets(void);
...@@ -810,5 +809,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan); ...@@ -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, int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority); u32 priority);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy); void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
int l2cap_chan_check_security(struct l2cap_chan *chan);
#endif /* __L2CAP_H */ #endif /* __L2CAP_H */
...@@ -23,6 +23,23 @@ ...@@ -23,6 +23,23 @@
#define MGMT_INDEX_NONE 0xFFFF #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 { struct mgmt_hdr {
__le16 opcode; __le16 opcode;
__le16 index; __le16 index;
...@@ -119,6 +136,10 @@ struct mgmt_cp_remove_keys { ...@@ -119,6 +136,10 @@ struct mgmt_cp_remove_keys {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 disconnect; __u8 disconnect;
} __packed; } __packed;
struct mgmt_rp_remove_keys {
bdaddr_t bdaddr;
__u8 status;
};
#define MGMT_OP_DISCONNECT 0x000F #define MGMT_OP_DISCONNECT 0x000F
struct mgmt_cp_disconnect { struct mgmt_cp_disconnect {
...@@ -126,11 +147,12 @@ struct mgmt_cp_disconnect { ...@@ -126,11 +147,12 @@ struct mgmt_cp_disconnect {
} __packed; } __packed;
struct mgmt_rp_disconnect { struct mgmt_rp_disconnect {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 status;
} __packed; } __packed;
#define MGMT_ADDR_BREDR 0x00 #define MGMT_ADDR_BREDR 0x00
#define MGMT_ADDR_LE 0x01 #define MGMT_ADDR_LE_PUBLIC 0x01
#define MGMT_ADDR_BREDR_LE 0x02 #define MGMT_ADDR_LE_RANDOM 0x02
#define MGMT_ADDR_INVALID 0xff #define MGMT_ADDR_INVALID 0xff
struct mgmt_addr_info { struct mgmt_addr_info {
...@@ -167,11 +189,11 @@ struct mgmt_cp_set_io_capability { ...@@ -167,11 +189,11 @@ struct mgmt_cp_set_io_capability {
#define MGMT_OP_PAIR_DEVICE 0x0014 #define MGMT_OP_PAIR_DEVICE 0x0014
struct mgmt_cp_pair_device { struct mgmt_cp_pair_device {
bdaddr_t bdaddr; struct mgmt_addr_info addr;
__u8 io_cap; __u8 io_cap;
} __packed; } __packed;
struct mgmt_rp_pair_device { struct mgmt_rp_pair_device {
bdaddr_t bdaddr; struct mgmt_addr_info addr;
__u8 status; __u8 status;
} __packed; } __packed;
...@@ -210,6 +232,9 @@ struct mgmt_cp_remove_remote_oob_data { ...@@ -210,6 +232,9 @@ struct mgmt_cp_remove_remote_oob_data {
} __packed; } __packed;
#define MGMT_OP_START_DISCOVERY 0x001B #define MGMT_OP_START_DISCOVERY 0x001B
struct mgmt_cp_start_discovery {
__u8 type;
} __packed;
#define MGMT_OP_STOP_DISCOVERY 0x001C #define MGMT_OP_STOP_DISCOVERY 0x001C
...@@ -228,6 +253,17 @@ struct mgmt_cp_set_fast_connectable { ...@@ -228,6 +253,17 @@ struct mgmt_cp_set_fast_connectable {
__u8 enable; __u8 enable;
} __packed; } __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 #define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete { struct mgmt_ev_cmd_complete {
__le16 opcode; __le16 opcode;
...@@ -322,3 +358,8 @@ struct mgmt_ev_device_blocked { ...@@ -322,3 +358,8 @@ struct mgmt_ev_device_blocked {
struct mgmt_ev_device_unblocked { struct mgmt_ev_device_unblocked {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __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) ...@@ -77,17 +77,12 @@ static struct bnep_session *__bnep_get_session(u8 *dst)
static void __bnep_link_session(struct bnep_session *s) 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); list_add(&s->list, &bnep_session_list);
} }
static void __bnep_unlink_session(struct bnep_session *s) static void __bnep_unlink_session(struct bnep_session *s)
{ {
list_del(&s->list); list_del(&s->list);
module_put(THIS_MODULE);
} }
static int bnep_send(struct bnep_session *s, void *data, size_t len) static int bnep_send(struct bnep_session *s, void *data, size_t len)
...@@ -528,6 +523,7 @@ static int bnep_session(void *arg) ...@@ -528,6 +523,7 @@ static int bnep_session(void *arg)
up_write(&bnep_session_sem); up_write(&bnep_session_sem);
free_netdev(dev); free_netdev(dev);
module_put_and_exit(0);
return 0; return 0;
} }
...@@ -614,9 +610,11 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) ...@@ -614,9 +610,11 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
__bnep_link_session(s); __bnep_link_session(s);
__module_get(THIS_MODULE);
s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name); s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
if (IS_ERR(s->task)) { if (IS_ERR(s->task)) {
/* Session thread start failed, gotta cleanup. */ /* Session thread start failed, gotta cleanup. */
module_put(THIS_MODULE);
unregister_netdev(dev); unregister_netdev(dev);
__bnep_unlink_session(s); __bnep_unlink_session(s);
err = PTR_ERR(s->task); err = PTR_ERR(s->task);
......
...@@ -65,14 +65,12 @@ static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) ...@@ -65,14 +65,12 @@ static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
static void __cmtp_link_session(struct cmtp_session *session) static void __cmtp_link_session(struct cmtp_session *session)
{ {
__module_get(THIS_MODULE);
list_add(&session->list, &cmtp_session_list); list_add(&session->list, &cmtp_session_list);
} }
static void __cmtp_unlink_session(struct cmtp_session *session) static void __cmtp_unlink_session(struct cmtp_session *session)
{ {
list_del(&session->list); list_del(&session->list);
module_put(THIS_MODULE);
} }
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
...@@ -325,6 +323,7 @@ static int cmtp_session(void *arg) ...@@ -325,6 +323,7 @@ static int cmtp_session(void *arg)
up_write(&cmtp_session_sem); up_write(&cmtp_session_sem);
kfree(session); kfree(session);
module_put_and_exit(0);
return 0; return 0;
} }
...@@ -374,9 +373,11 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) ...@@ -374,9 +373,11 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
__cmtp_link_session(session); __cmtp_link_session(session);
__module_get(THIS_MODULE);
session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d", session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
session->num); session->num);
if (IS_ERR(session->task)) { if (IS_ERR(session->task)) {
module_put(THIS_MODULE);
err = PTR_ERR(session->task); err = PTR_ERR(session->task);
goto unlink; goto unlink;
} }
......
...@@ -123,7 +123,7 @@ static void hci_acl_connect_cancel(struct hci_conn *conn) ...@@ -123,7 +123,7 @@ static void hci_acl_connect_cancel(struct hci_conn *conn)
BT_DBG("%p", conn); BT_DBG("%p", conn);
if (conn->hdev->hci_ver < 2) if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2)
return; return;
bacpy(&cp.bdaddr, &conn->dst); bacpy(&cp.bdaddr, &conn->dst);
......
...@@ -54,6 +54,8 @@ ...@@ -54,6 +54,8 @@
#define AUTO_OFF_TIMEOUT 2000 #define AUTO_OFF_TIMEOUT 2000
int enable_hs;
static void hci_cmd_task(unsigned long arg); static void hci_cmd_task(unsigned long arg);
static void hci_rx_task(unsigned long arg); static void hci_rx_task(unsigned long arg);
static void hci_tx_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) ...@@ -228,18 +230,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
/* Read Buffer Size (ACL mtu, max pkt, etc.) */ /* Read Buffer Size (ACL mtu, max pkt, etc.) */
hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); 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 */ /* Read BD Address */
hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
...@@ -521,8 +511,9 @@ int hci_dev_open(__u16 dev) ...@@ -521,8 +511,9 @@ int hci_dev_open(__u16 dev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
set_bit(HCI_RAW, &hdev->flags); set_bit(HCI_RAW, &hdev->flags);
/* Treat all non BR/EDR controllers as raw devices for now */ /* Treat all non BR/EDR controllers as raw devices if
if (hdev->dev_type != HCI_BREDR) enable_hs is not set */
if (hdev->dev_type != HCI_BREDR && !enable_hs)
set_bit(HCI_RAW, &hdev->flags); set_bit(HCI_RAW, &hdev->flags);
if (hdev->open(hdev)) { if (hdev->open(hdev)) {
...@@ -1336,14 +1327,12 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) ...@@ -1336,14 +1327,12 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
{ {
struct bdaddr_list *entry; struct bdaddr_list *entry;
if (bacmp(bdaddr, BDADDR_ANY) == 0) { if (bacmp(bdaddr, BDADDR_ANY) == 0)
return hci_blacklist_clear(hdev); return hci_blacklist_clear(hdev);
}
entry = hci_blacklist_lookup(hdev, bdaddr); entry = hci_blacklist_lookup(hdev, bdaddr);
if (!entry) { if (!entry)
return -ENOENT; return -ENOENT;
}
list_del(&entry->list); list_del(&entry->list);
kfree(entry); kfree(entry);
...@@ -1451,12 +1440,13 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -1451,12 +1440,13 @@ int hci_register_dev(struct hci_dev *hdev)
sprintf(hdev->name, "hci%d", id); sprintf(hdev->name, "hci%d", id);
hdev->id = id; hdev->id = id;
list_add(&hdev->list, head); list_add_tail(&hdev->list, head);
atomic_set(&hdev->refcnt, 1); atomic_set(&hdev->refcnt, 1);
spin_lock_init(&hdev->lock); spin_lock_init(&hdev->lock);
hdev->flags = 0; hdev->flags = 0;
hdev->dev_flags = 0;
hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
hdev->esco_type = (ESCO_HV1); hdev->esco_type = (ESCO_HV1);
hdev->link_mode = (HCI_LM_ACCEPT); hdev->link_mode = (HCI_LM_ACCEPT);
...@@ -2614,3 +2604,6 @@ int hci_cancel_inquiry(struct hci_dev *hdev) ...@@ -2614,3 +2604,6 @@ int hci_cancel_inquiry(struct hci_dev *hdev)
return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); 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) ...@@ -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); 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; return;
}
clear_bit(HCI_INQUIRY, &hdev->flags); clear_bit(HCI_INQUIRY, &hdev->flags);
...@@ -190,6 +194,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -190,6 +194,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
clear_bit(HCI_RESET, &hdev->flags); clear_bit(HCI_RESET, &hdev->flags);
hci_req_complete(hdev, HCI_OP_RESET, status); 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) 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) ...@@ -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 /* CSR 1.1 dongles does not accept any bitfield so don't try to set
* any event mask for pre 1.2 devices */ * any event mask for pre 1.2 devices */
if (hdev->lmp_ver <= 1) if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return; return;
events[4] |= 0x01; /* Flow Specification Complete */ events[4] |= 0x01; /* Flow Specification Complete */
...@@ -558,7 +564,7 @@ static void hci_setup(struct hci_dev *hdev) ...@@ -558,7 +564,7 @@ static void hci_setup(struct hci_dev *hdev)
{ {
hci_setup_event_mask(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); hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (hdev->features[6] & LMP_SIMPLE_PAIR) { if (hdev->features[6] & LMP_SIMPLE_PAIR) {
...@@ -713,6 +719,21 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, ...@@ -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); 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) 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; 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, ...@@ -927,6 +948,37 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
hci_dev_unlock(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, static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -940,6 +992,13 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, ...@@ -940,6 +992,13 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
hci_dev_unlock(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, static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -956,12 +1015,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, ...@@ -956,12 +1015,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
return; return;
if (cp->enable == 0x01) { if (cp->enable == 0x01) {
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
del_timer(&hdev->adv_timer); del_timer(&hdev->adv_timer);
hci_dev_lock(hdev); hci_dev_lock(hdev);
hci_adv_entries_clear(hdev); hci_adv_entries_clear(hdev);
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
} else if (cp->enable == 0x00) { } else if (cp->enable == 0x00) {
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT); 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) ...@@ -1014,7 +1077,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
hci_conn_check_pending(hdev); hci_conn_check_pending(hdev);
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags)) if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_inquiry_failed(hdev, status); mgmt_start_discovery_failed(hdev, status);
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
return; return;
} }
...@@ -1437,7 +1500,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * ...@@ -1437,7 +1500,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.rssi = 0x00; data.rssi = 0x00;
data.ssp_mode = 0x00; data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data); 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); info->dev_class, 0, NULL);
} }
...@@ -1472,7 +1535,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -1472,7 +1535,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG; conn->state = BT_CONFIG;
hci_conn_hold(conn); hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT; conn->disc_timeout = HCI_DISCONN_TIMEOUT;
mgmt_connected(hdev, &ev->bdaddr, conn->type); mgmt_connected(hdev, &ev->bdaddr, conn->type,
conn->dst_type);
} else } else
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
...@@ -1494,7 +1558,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -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 */ /* 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; struct hci_cp_change_conn_ptype cp;
cp.handle = ev->handle; cp.handle = ev->handle;
cp.pkt_type = cpu_to_le16(conn->pkt_type); 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 ...@@ -1505,7 +1569,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
if (conn->type == ACL_LINK) if (conn->type == ACL_LINK)
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
ev->status); conn->dst_type, ev->status);
} }
if (conn->type == ACL_LINK) if (conn->type == ACL_LINK)
...@@ -1604,26 +1668,27 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff ...@@ -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); 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); hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (!conn) if (!conn)
goto unlock; goto unlock;
conn->state = BT_CLOSED; if (ev->status == 0)
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK || conn->type == LE_LINK) if (conn->type == ACL_LINK || conn->type == LE_LINK) {
mgmt_disconnected(hdev, &conn->dst, conn->type); if (ev->status != 0)
mgmt_disconnect_failed(hdev, &conn->dst, ev->status);
else
mgmt_disconnected(hdev, &conn->dst, conn->type,
conn->dst_type);
}
hci_proto_disconn_cfm(conn, ev->reason); if (ev->status == 0) {
hci_conn_del(conn); hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
}
unlock: unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
...@@ -1961,6 +2026,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk ...@@ -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); hci_cc_write_ca_timeout(hdev, skb);
break; break;
case HCI_OP_READ_FLOW_CONTROL_MODE:
hci_cc_read_flow_control_mode(hdev, skb);
break;
case HCI_OP_READ_LOCAL_AMP_INFO: case HCI_OP_READ_LOCAL_AMP_INFO:
hci_cc_read_local_amp_info(hdev, skb); hci_cc_read_local_amp_info(hdev, skb);
break; break;
...@@ -2009,6 +2078,17 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk ...@@ -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); hci_cc_user_confirm_neg_reply(hdev, skb);
break; 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: case HCI_OP_LE_SET_SCAN_ENABLE:
hci_cc_le_set_scan_enable(hdev, skb); hci_cc_le_set_scan_enable(hdev, skb);
break; break;
...@@ -2096,7 +2176,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -2096,7 +2176,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
case HCI_OP_DISCONNECT: case HCI_OP_DISCONNECT:
if (ev->status != 0) if (ev->status != 0)
mgmt_disconnect_failed(hdev); mgmt_disconnect_failed(hdev, NULL, ev->status);
break; break;
case HCI_OP_LE_CREATE_CONN: case HCI_OP_LE_CREATE_CONN:
...@@ -2444,7 +2524,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct ...@@ -2444,7 +2524,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi; data.rssi = info->rssi;
data.ssp_mode = 0x00; data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data); 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->dev_class, info->rssi,
NULL); NULL);
} }
...@@ -2461,7 +2541,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct ...@@ -2461,7 +2541,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi; data.rssi = info->rssi;
data.ssp_mode = 0x00; data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data); 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->dev_class, info->rssi,
NULL); NULL);
} }
...@@ -2604,7 +2684,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct ...@@ -2604,7 +2684,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi; data.rssi = info->rssi;
data.ssp_mode = 0x01; data.ssp_mode = 0x01;
hci_inquiry_cache_update(hdev, &data); 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); info->dev_class, info->rssi, info->data);
} }
...@@ -2768,6 +2848,21 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, ...@@ -2768,6 +2848,21 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
hci_dev_unlock(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) 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; 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 ...@@ -2868,14 +2963,15 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
} }
if (ev->status) { 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); hci_proto_connect_cfm(conn, ev->status);
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
hci_conn_del(conn); hci_conn_del(conn);
goto unlock; 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->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle); conn->handle = __le16_to_cpu(ev->handle);
...@@ -3106,6 +3202,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -3106,6 +3202,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_user_confirm_request_evt(hdev, skb); hci_user_confirm_request_evt(hdev, skb);
break; break;
case HCI_EV_USER_PASSKEY_REQUEST:
hci_user_passkey_request_evt(hdev, skb);
break;
case HCI_EV_SIMPLE_PAIR_COMPLETE: case HCI_EV_SIMPLE_PAIR_COMPLETE:
hci_simple_pair_complete_evt(hdev, skb); hci_simple_pair_complete_evt(hdev, skb);
break; break;
......
...@@ -57,7 +57,6 @@ ...@@ -57,7 +57,6 @@
#include <net/bluetooth/smp.h> #include <net/bluetooth/smp.h>
int disable_ertm; int disable_ertm;
int enable_hs;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, }; 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 ...@@ -97,7 +96,6 @@ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16
return c; return c;
} }
return NULL; return NULL;
} }
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) 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) ...@@ -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) { list_for_each_entry(c, &chan_list, global_l) {
if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src)) if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
goto found; return c;
} }
return NULL;
c = NULL;
found:
return c;
} }
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) 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) ...@@ -234,8 +229,37 @@ static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer)
chan_put(chan); 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) 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->state = state;
chan->ops->state_change(chan->data, state); chan->ops->state_change(chan->data, state);
} }
...@@ -518,7 +542,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) ...@@ -518,7 +542,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
} }
/* Service level security */ /* 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; struct l2cap_conn *conn = chan->conn;
__u8 auth_type; __u8 auth_type;
...@@ -664,7 +688,7 @@ static void l2cap_do_start(struct l2cap_chan *chan) ...@@ -664,7 +688,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return; return;
if (l2cap_check_security(chan) && if (l2cap_chan_check_security(chan) &&
__l2cap_no_conn_pending(chan)) { __l2cap_no_conn_pending(chan)) {
struct l2cap_conn_req req; struct l2cap_conn_req req;
req.scid = cpu_to_le16(chan->scid); req.scid = cpu_to_le16(chan->scid);
...@@ -754,7 +778,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -754,7 +778,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
if (chan->state == BT_CONNECT) { if (chan->state == BT_CONNECT) {
struct l2cap_conn_req req; struct l2cap_conn_req req;
if (!l2cap_check_security(chan) || if (!l2cap_chan_check_security(chan) ||
!__l2cap_no_conn_pending(chan)) { !__l2cap_no_conn_pending(chan)) {
bh_unlock_sock(sk); bh_unlock_sock(sk);
continue; continue;
...@@ -787,7 +811,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -787,7 +811,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.scid = cpu_to_le16(chan->dcid); rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid); rsp.dcid = cpu_to_le16(chan->scid);
if (l2cap_check_security(chan)) { if (l2cap_chan_check_security(chan)) {
if (bt_sk(sk)->defer_setup) { if (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent; struct sock *parent = bt_sk(sk)->parent;
rsp.result = cpu_to_le16(L2CAP_CR_PEND); rsp.result = cpu_to_le16(L2CAP_CR_PEND);
...@@ -1181,7 +1205,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan) ...@@ -1181,7 +1205,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
if (hcon->state == BT_CONNECTED) { if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan); __clear_chan_timer(chan);
if (l2cap_check_security(chan)) if (l2cap_chan_check_security(chan))
l2cap_state_change(chan, BT_CONNECTED); l2cap_state_change(chan, BT_CONNECTED);
} else } else
l2cap_do_start(chan); l2cap_do_start(chan);
...@@ -1318,14 +1342,12 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) ...@@ -1318,14 +1342,12 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
if (!skb) if (!skb)
return; return;
do { while (bt_cb(skb)->tx_seq != tx_seq) {
if (bt_cb(skb)->tx_seq == tx_seq)
break;
if (skb_queue_is_last(&chan->tx_q, skb)) if (skb_queue_is_last(&chan->tx_q, skb))
return; return;
} while ((skb = skb_queue_next(&chan->tx_q, skb))); skb = skb_queue_next(&chan->tx_q, skb);
}
if (chan->remote_max_tx && if (chan->remote_max_tx &&
bt_cb(skb)->retries == 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) ...@@ -1906,7 +1928,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
{ {
struct l2cap_conf_efs efs; struct l2cap_conf_efs efs;
switch(chan->mode) { switch (chan->mode) {
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
efs.id = chan->local_id; efs.id = chan->local_id;
efs.stype = chan->local_stype; efs.stype = chan->local_stype;
...@@ -2606,7 +2628,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2606,7 +2628,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
chan->ident = cmd->ident; chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { 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) { if (bt_sk(sk)->defer_setup) {
l2cap_state_change(chan, BT_CONNECT2); l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND; result = L2CAP_CR_PEND;
...@@ -3019,7 +3041,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -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 */ /* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) { if (sock_owned_by_user(sk)) {
l2cap_state_change(chan,BT_DISCONN); l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan); __clear_chan_timer(chan);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT); __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk); bh_unlock_sock(sk);
...@@ -3562,14 +3584,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, ...@@ -3562,14 +3584,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
bt_cb(skb)->sar = sar; bt_cb(skb)->sar = sar;
next_skb = skb_peek(&chan->srej_q); 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); tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
do { while (next_skb) {
if (bt_cb(next_skb)->tx_seq == tx_seq) if (bt_cb(next_skb)->tx_seq == tx_seq)
return -EINVAL; return -EINVAL;
...@@ -3582,9 +3600,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, ...@@ -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)) if (skb_queue_is_last(&chan->srej_q, next_skb))
break; next_skb = NULL;
else
} while ((next_skb = skb_queue_next(&chan->srej_q, next_skb))); next_skb = skb_queue_next(&chan->srej_q, next_skb);
}
__skb_queue_tail(&chan->srej_q, skb); __skb_queue_tail(&chan->srej_q, skb);
...@@ -3788,7 +3807,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) ...@@ -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; struct srej_list *new;
u32 control; u32 control;
...@@ -3799,6 +3818,9 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) ...@@ -3799,6 +3818,9 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
l2cap_send_sframe(chan, control); l2cap_send_sframe(chan, control);
new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
if (!new)
return -ENOMEM;
new->tx_seq = chan->expected_tx_seq; new->tx_seq = chan->expected_tx_seq;
chan->expected_tx_seq = __next_seq(chan, 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) ...@@ -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); 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) 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 ...@@ -3877,7 +3901,12 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
return 0; 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 { } else {
expected_tx_seq_offset = __seq_offset(chan, 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 ...@@ -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); 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); __clear_ack_timer(chan);
} }
...@@ -3928,11 +3961,12 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont ...@@ -3928,11 +3961,12 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
l2cap_retransmit_frames(chan); l2cap_retransmit_frames(chan);
} }
__set_ack_timer(chan);
chan->num_acked = (chan->num_acked + 1) % num_to_ack; chan->num_acked = (chan->num_acked + 1) % num_to_ack;
if (chan->num_acked == num_to_ack - 1) if (chan->num_acked == num_to_ack - 1)
l2cap_send_ack(chan); l2cap_send_ack(chan);
else
__set_ack_timer(chan);
return 0; return 0;
...@@ -4768,6 +4802,3 @@ void l2cap_exit(void) ...@@ -4768,6 +4802,3 @@ void l2cap_exit(void)
module_param(disable_ertm, bool, 0644); module_param(disable_ertm, bool, 0644);
MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); 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 ...@@ -626,8 +626,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
chan->sec_level = sec.level; chan->sec_level = sec.level;
if (!chan->conn)
break;
conn = chan->conn; 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) { if (!conn->hcon->out) {
err = -EINVAL; err = -EINVAL;
break; break;
...@@ -635,9 +640,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch ...@@ -635,9 +640,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
if (smp_conn_security(conn, sec.level)) if (smp_conn_security(conn, sec.level))
break; break;
err = 0;
sk->sk_state = BT_CONFIG; 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; break;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
/* Bluetooth HCI Management interface */ /* Bluetooth HCI Management interface */
#include <linux/kernel.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -44,6 +45,79 @@ struct pending_cmd { ...@@ -44,6 +45,79 @@ struct pending_cmd {
void *user_data; 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) static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
{ {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -178,7 +252,8 @@ static int read_controller_info(struct sock *sk, u16 index) ...@@ -178,7 +252,8 @@ static int read_controller_info(struct sock *sk, u16 index)
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) 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)) if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
cancel_delayed_work_sync(&hdev->power_off); cancel_delayed_work_sync(&hdev->power_off);
...@@ -291,6 +366,15 @@ static void mgmt_pending_remove(struct pending_cmd *cmd) ...@@ -291,6 +366,15 @@ static void mgmt_pending_remove(struct pending_cmd *cmd)
mgmt_pending_free(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) static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
{ {
struct mgmt_mode *cp; struct mgmt_mode *cp;
...@@ -303,22 +387,25 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -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); BT_DBG("request for hci%u", index);
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
up = test_bit(HCI_UP, &hdev->flags); up = test_bit(HCI_UP, &hdev->flags);
if ((cp->val && up) || (!cp->val && !up)) { 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; goto failed;
} }
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { 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; goto failed;
} }
...@@ -355,28 +442,33 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, ...@@ -355,28 +442,33 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
BT_DBG("request for hci%u", index); BT_DBG("request for hci%u", index);
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { 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; goto failed;
} }
if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, 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; goto failed;
} }
if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
test_bit(HCI_PSCAN, &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; goto failed;
} }
...@@ -421,27 +513,32 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, ...@@ -421,27 +513,32 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
BT_DBG("request for hci%u", index); BT_DBG("request for hci%u", index);
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { 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; goto failed;
} }
if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, 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; goto failed;
} }
if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { 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; goto failed;
} }
...@@ -496,15 +593,6 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, ...@@ -496,15 +593,6 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
return 0; 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, static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
u16 len) u16 len)
{ {
...@@ -517,11 +605,13 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, ...@@ -517,11 +605,13 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
BT_DBG("request for hci%u", index); BT_DBG("request for hci%u", index);
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -730,11 +820,13 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -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); BT_DBG("request for hci%u", index);
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -779,11 +871,13 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -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); BT_DBG("request for hci%u", index);
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -805,7 +899,8 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -805,7 +899,8 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
} }
if (found == 0) { 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; goto unlock;
} }
...@@ -838,11 +933,13 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, ...@@ -838,11 +933,13 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
BT_DBG("request for hci%u", index); BT_DBG("request for hci%u", index);
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -870,11 +967,13 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, ...@@ -870,11 +967,13 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
cp = (void *) data; cp = (void *) data;
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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); hci_dev_lock_bh(hdev);
...@@ -914,7 +1013,8 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data, ...@@ -914,7 +1013,8 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
cp = (void *) data; cp = (void *) data;
if (len < sizeof(*cp)) 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); 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, ...@@ -923,12 +1023,14 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
if (expected_len != len) { if (expected_len != len) {
BT_ERR("load_link_keys: expected %u bytes, got %u bytes", BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
len, expected_len); 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); hdev = hci_dev_get(index);
if (!hdev) 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, BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
key_count); key_count);
...@@ -951,6 +1053,8 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data, ...@@ -951,6 +1053,8 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
key->pin_len); key->pin_len);
} }
cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
hci_dev_unlock_bh(hdev); hci_dev_unlock_bh(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
...@@ -962,41 +1066,64 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data, ...@@ -962,41 +1066,64 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
{ {
struct hci_dev *hdev; struct hci_dev *hdev;
struct mgmt_cp_remove_keys *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; struct hci_conn *conn;
int err; int err;
cp = (void *) data; cp = (void *) data;
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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); 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); err = hci_remove_link_key(hdev, &cp->bdaddr);
if (err < 0) { if (err < 0) {
err = cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, -err); rp.status = MGMT_STATUS_NOT_PAIRED;
goto unlock; goto unlock;
} }
err = 0; if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) sizeof(rp));
goto unlock; goto unlock;
}
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
if (conn) { if (!conn) {
struct hci_cp_disconnect dc; err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
sizeof(rp));
goto unlock;
}
put_unaligned_le16(conn->handle, &dc.handle); cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_KEYS, hdev, cp, sizeof(*cp));
dc.reason = 0x13; /* Remote User Terminated Connection */ if (!cmd) {
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); 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: unlock:
if (err < 0)
err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
sizeof(rp));
hci_dev_unlock_bh(hdev); hci_dev_unlock_bh(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
...@@ -1017,21 +1144,25 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -1017,21 +1144,25 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
cp = (void *) data; cp = (void *) data;
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { 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; goto failed;
} }
if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { 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; goto failed;
} }
...@@ -1040,7 +1171,8 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -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); conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
if (!conn) { 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; goto failed;
} }
...@@ -1064,11 +1196,18 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -1064,11 +1196,18 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
return err; return err;
} }
static u8 link_to_mgmt(u8 link_type) static u8 link_to_mgmt(u8 link_type, u8 addr_type)
{ {
switch (link_type) { switch (link_type) {
case LE_LINK: 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: case ACL_LINK:
return MGMT_ADDR_BREDR; return MGMT_ADDR_BREDR;
default: default:
...@@ -1090,7 +1229,8 @@ static int get_connections(struct sock *sk, u16 index) ...@@ -1090,7 +1229,8 @@ static int get_connections(struct sock *sk, u16 index)
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -1111,7 +1251,7 @@ static int get_connections(struct sock *sk, u16 index) ...@@ -1111,7 +1251,7 @@ static int get_connections(struct sock *sk, u16 index)
i = 0; i = 0;
list_for_each_entry(c, &hdev->conn_hash.list, list) { list_for_each_entry(c, &hdev->conn_hash.list, list) {
bacpy(&rp->addr[i].bdaddr, &c->dst); 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) if (rp->addr[i].type == MGMT_ADDR_INVALID)
continue; continue;
i++; i++;
...@@ -1164,22 +1304,26 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, ...@@ -1164,22 +1304,26 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
cp = (void *) data; cp = (void *) data;
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { 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; goto failed;
} }
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
if (!conn) { 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; goto failed;
} }
...@@ -1191,7 +1335,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, ...@@ -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); err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
if (err >= 0) if (err >= 0)
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
EINVAL); MGMT_STATUS_INVALID_PARAMS);
goto failed; goto failed;
} }
...@@ -1230,18 +1374,18 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, ...@@ -1230,18 +1374,18 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
if (len != sizeof(*cp)) if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
EINVAL); MGMT_STATUS_INVALID_PARAMS);
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) if (!hdev)
return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
ENODEV); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
ENETDOWN); MGMT_STATUS_NOT_POWERED);
goto failed; goto failed;
} }
...@@ -1265,11 +1409,13 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data, ...@@ -1265,11 +1409,13 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
cp = (void *) data; cp = (void *) data;
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -1307,7 +1453,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) ...@@ -1307,7 +1453,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
struct mgmt_rp_pair_device rp; struct mgmt_rp_pair_device rp;
struct hci_conn *conn = cmd->user_data; 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; rp.status = status;
cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp)); 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) ...@@ -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) static void pairing_complete_cb(struct hci_conn *conn, u8 status)
{ {
struct pending_cmd *cmd; struct pending_cmd *cmd;
struct hci_dev *hdev = conn->hdev;
BT_DBG("status %u", status); BT_DBG("status %u", status);
hci_dev_lock_bh(hdev);
cmd = find_pairing(conn); cmd = find_pairing(conn);
if (!cmd) if (!cmd)
BT_DBG("Unable to find a pending command"); BT_DBG("Unable to find a pending command");
else else
pairing_complete(cmd, status); pairing_complete(cmd, status);
hci_dev_unlock_bh(hdev);
} }
static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
{ {
struct hci_dev *hdev; struct hci_dev *hdev;
struct mgmt_cp_pair_device *cp; struct mgmt_cp_pair_device *cp;
struct mgmt_rp_pair_device rp;
struct pending_cmd *cmd; struct pending_cmd *cmd;
struct adv_entry *entry;
u8 sec_level, auth_type; u8 sec_level, auth_type;
struct hci_conn *conn; struct hci_conn *conn;
int err; int err;
...@@ -1355,11 +1497,13 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -1355,11 +1497,13 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
cp = (void *) data; cp = (void *) data;
if (len != sizeof(*cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -1369,22 +1513,29 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -1369,22 +1513,29 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
else else
auth_type = HCI_AT_DEDICATED_BONDING_MITM; auth_type = HCI_AT_DEDICATED_BONDING_MITM;
entry = hci_find_adv_entry(hdev, &cp->bdaddr); if (cp->addr.type == MGMT_ADDR_BREDR)
if (entry) conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level,
auth_type); auth_type);
else else
conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
auth_type); auth_type);
memset(&rp, 0, sizeof(rp));
bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
rp.addr.type = cp->addr.type;
if (IS_ERR(conn)) { 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; goto unlock;
} }
if (conn->connect_cfm_cb) { if (conn->connect_cfm_cb) {
hci_conn_put(conn); 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; goto unlock;
} }
...@@ -1396,7 +1547,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -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 */ /* 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->connect_cfm_cb = pairing_complete_cb;
conn->security_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) ...@@ -1417,56 +1568,138 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
return err; return err;
} }
static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data, static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
u16 len, int success) 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 pending_cmd *cmd;
struct hci_dev *hdev; struct hci_dev *hdev;
struct hci_conn *conn;
int err; 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); hdev = hci_dev_get(index);
if (!hdev) 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); hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, ENETDOWN); err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
goto failed; goto done;
} }
cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len); /*
* 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;
}
/* 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) { if (!cmd) {
err = -ENOMEM; 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) if (err < 0)
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
failed: done:
hci_dev_unlock_bh(hdev); hci_dev_unlock_bh(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; 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, static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
u16 len) u16 len)
{ {
...@@ -1479,11 +1712,13 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data, ...@@ -1479,11 +1712,13 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
BT_DBG(""); BT_DBG("");
if (len != sizeof(*mgmt_cp)) 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -1517,24 +1752,25 @@ static int read_local_oob_data(struct sock *sk, u16 index) ...@@ -1517,24 +1752,25 @@ static int read_local_oob_data(struct sock *sk, u16 index)
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) if (!hdev)
return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
ENODEV); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
ENETDOWN); MGMT_STATUS_NOT_POWERED);
goto unlock; goto unlock;
} }
if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) { if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
EOPNOTSUPP); MGMT_STATUS_NOT_SUPPORTED);
goto unlock; goto unlock;
} }
if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { 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; goto unlock;
} }
...@@ -1566,19 +1802,20 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data, ...@@ -1566,19 +1802,20 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
if (len != sizeof(*cp)) if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
EINVAL); MGMT_STATUS_INVALID_PARAMS);
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) if (!hdev)
return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
ENODEV); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash, err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
cp->randomizer); cp->randomizer);
if (err < 0) 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 else
err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL, err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
0); 0);
...@@ -1600,19 +1837,19 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, ...@@ -1600,19 +1837,19 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
if (len != sizeof(*cp)) if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
EINVAL); MGMT_STATUS_INVALID_PARAMS);
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) if (!hdev)
return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
ENODEV); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
err = hci_remove_remote_oob_data(hdev, &cp->bdaddr); err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
if (err < 0) if (err < 0)
err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
-err); MGMT_STATUS_INVALID_PARAMS);
else else
err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
NULL, 0); NULL, 0);
...@@ -1623,22 +1860,30 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, ...@@ -1623,22 +1860,30 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
return err; 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 pending_cmd *cmd;
struct hci_dev *hdev; struct hci_dev *hdev;
int err; int err;
BT_DBG("hci%u", index); 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); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { 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; goto failed;
} }
...@@ -1669,7 +1914,8 @@ static int stop_discovery(struct sock *sk, u16 index) ...@@ -1669,7 +1914,8 @@ static int stop_discovery(struct sock *sk, u16 index)
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) 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_bh(hdev);
...@@ -1701,18 +1947,19 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, ...@@ -1701,18 +1947,19 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
if (len != sizeof(*cp)) if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
EINVAL); MGMT_STATUS_INVALID_PARAMS);
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) if (!hdev)
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
ENODEV); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
err = hci_blacklist_add(hdev, &cp->bdaddr); err = hci_blacklist_add(hdev, &cp->bdaddr);
if (err < 0) 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 else
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
NULL, 0); NULL, 0);
...@@ -1734,19 +1981,20 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, ...@@ -1734,19 +1981,20 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
if (len != sizeof(*cp)) if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
EINVAL); MGMT_STATUS_INVALID_PARAMS);
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) if (!hdev)
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
ENODEV); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
err = hci_blacklist_del(hdev, &cp->bdaddr); err = hci_blacklist_del(hdev, &cp->bdaddr);
if (err < 0) 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 else
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
NULL, 0); NULL, 0);
...@@ -1770,12 +2018,12 @@ static int set_fast_connectable(struct sock *sk, u16 index, ...@@ -1770,12 +2018,12 @@ static int set_fast_connectable(struct sock *sk, u16 index,
if (len != sizeof(*cp)) if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
EINVAL); MGMT_STATUS_INVALID_PARAMS);
hdev = hci_dev_get(index); hdev = hci_dev_get(index);
if (!hdev) if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
ENODEV); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock(hdev); hci_dev_lock(hdev);
...@@ -1793,14 +2041,14 @@ static int set_fast_connectable(struct sock *sk, u16 index, ...@@ -1793,14 +2041,14 @@ static int set_fast_connectable(struct sock *sk, u16 index,
sizeof(acp), &acp); sizeof(acp), &acp);
if (err < 0) { if (err < 0) {
err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
-err); MGMT_STATUS_FAILED);
goto done; goto done;
} }
err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
if (err < 0) { if (err < 0) {
err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
-err); MGMT_STATUS_FAILED);
goto done; goto done;
} }
...@@ -1903,10 +2151,18 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) ...@@ -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); err = pair_device(sk, index, buf + sizeof(*hdr), len);
break; break;
case MGMT_OP_USER_CONFIRM_REPLY: 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; break;
case MGMT_OP_USER_CONFIRM_NEG_REPLY: 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; break;
case MGMT_OP_SET_LOCAL_NAME: case MGMT_OP_SET_LOCAL_NAME:
err = set_local_name(sk, index, buf + sizeof(*hdr), len); 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) ...@@ -1922,7 +2178,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
len); len);
break; break;
case MGMT_OP_START_DISCOVERY: case MGMT_OP_START_DISCOVERY:
err = start_discovery(sk, index); err = start_discovery(sk, index, buf + sizeof(*hdr), len);
break; break;
case MGMT_OP_STOP_DISCOVERY: case MGMT_OP_STOP_DISCOVERY:
err = stop_discovery(sk, index); err = stop_discovery(sk, index);
...@@ -1939,7 +2195,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) ...@@ -1939,7 +2195,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
break; break;
default: default:
BT_DBG("Unknown op %u", opcode); BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode, 0x01); err = cmd_status(sk, index, opcode,
MGMT_STATUS_UNKNOWN_COMMAND);
break; break;
} }
...@@ -2062,13 +2319,15 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable) ...@@ -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) int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
{ {
u8 mgmt_err = mgmt_status(status);
if (scan & SCAN_PAGE) if (scan & SCAN_PAGE)
mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
cmd_status_rsp, &status); cmd_status_rsp, &mgmt_err);
if (scan & SCAN_INQUIRY) if (scan & SCAN_INQUIRY)
mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
cmd_status_rsp, &status); cmd_status_rsp, &mgmt_err);
return 0; return 0;
} }
...@@ -2089,12 +2348,13 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, ...@@ -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); 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; struct mgmt_addr_info ev;
bacpy(&ev.bdaddr, bdaddr); 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); 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) ...@@ -2106,6 +2366,7 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
struct mgmt_rp_disconnect rp; struct mgmt_rp_disconnect rp;
bacpy(&rp.bdaddr, &cp->bdaddr); bacpy(&rp.bdaddr, &cp->bdaddr);
rp.status = 0;
cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); 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) ...@@ -2115,7 +2376,25 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_remove(cmd); 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 mgmt_addr_info ev;
struct sock *sk = NULL; struct sock *sk = NULL;
...@@ -2124,40 +2403,53 @@ int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) ...@@ -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); mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
bacpy(&ev.bdaddr, bdaddr); 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); err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
if (sk) if (sk)
sock_put(sk); sock_put(sk);
mgmt_pending_foreach(MGMT_OP_REMOVE_KEYS, hdev, remove_keys_rsp, NULL);
return err; 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; struct pending_cmd *cmd;
u8 mgmt_err = mgmt_status(status);
int err; int err;
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
if (!cmd) if (!cmd)
return -ENOENT; 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); mgmt_pending_remove(cmd);
return err; return err;
} }
int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 status) u8 addr_type, u8 status)
{ {
struct mgmt_ev_connect_failed ev; struct mgmt_ev_connect_failed ev;
bacpy(&ev.addr.bdaddr, bdaddr); bacpy(&ev.addr.bdaddr, bdaddr);
ev.addr.type = link_to_mgmt(type); ev.addr.type = link_to_mgmt(link_type, addr_type);
ev.status = status; ev.status = mgmt_status(status);
return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); 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, ...@@ -2185,7 +2477,7 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
return -ENOENT; return -ENOENT;
bacpy(&rp.bdaddr, bdaddr); 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, err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
sizeof(rp)); sizeof(rp));
...@@ -2207,7 +2499,7 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, ...@@ -2207,7 +2499,7 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
return -ENOENT; return -ENOENT;
bacpy(&rp.bdaddr, bdaddr); 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, err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
sizeof(rp)); sizeof(rp));
...@@ -2232,7 +2524,19 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, ...@@ -2232,7 +2524,19 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
NULL); 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) u8 status, u8 opcode)
{ {
struct pending_cmd *cmd; struct pending_cmd *cmd;
...@@ -2244,7 +2548,7 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, ...@@ -2244,7 +2548,7 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
return -ENOENT; return -ENOENT;
bacpy(&rp.bdaddr, bdaddr); bacpy(&rp.bdaddr, bdaddr);
rp.status = status; rp.status = mgmt_status(status);
err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp)); err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
...@@ -2255,23 +2559,37 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, ...@@ -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, int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status) u8 status)
{ {
return confirm_reply_complete(hdev, bdaddr, status, return user_pairing_resp_complete(hdev, bdaddr, status,
MGMT_OP_USER_CONFIRM_REPLY); MGMT_OP_USER_CONFIRM_REPLY);
} }
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status) 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); 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) int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)
{ {
struct mgmt_ev_auth_failed ev; struct mgmt_ev_auth_failed ev;
bacpy(&ev.bdaddr, bdaddr); bacpy(&ev.bdaddr, bdaddr);
ev.status = status; ev.status = mgmt_status(status);
return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); 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) ...@@ -2291,7 +2609,7 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
if (status) { if (status) {
err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
EIO); mgmt_status(status));
goto failed; goto failed;
} }
...@@ -2326,7 +2644,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, ...@@ -2326,7 +2644,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
if (status) { if (status) {
err = cmd_status(cmd->sk, hdev->id, err = cmd_status(cmd->sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_DATA, EIO); MGMT_OP_READ_LOCAL_OOB_DATA,
mgmt_status(status));
} else { } else {
struct mgmt_rp_read_local_oob_data rp; 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, ...@@ -2343,15 +2662,15 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
return err; return err;
} }
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 *dev_class, s8 rssi, u8 *eir) u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir)
{ {
struct mgmt_ev_device_found ev; struct mgmt_ev_device_found ev;
memset(&ev, 0, sizeof(ev)); memset(&ev, 0, sizeof(ev));
bacpy(&ev.addr.bdaddr, bdaddr); 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; ev.rssi = rssi;
if (eir) if (eir)
...@@ -2375,7 +2694,7 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name) ...@@ -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); 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; struct pending_cmd *cmd;
int err; int err;
...@@ -2384,6 +2703,21 @@ int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status) ...@@ -2384,6 +2703,21 @@ int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status)
if (!cmd) if (!cmd)
return -ENOENT; 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); err = cmd_status(cmd->sk, hdev->id, cmd->opcode, status);
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
......
...@@ -232,6 +232,18 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) ...@@ -232,6 +232,18 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
return 0; 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) static void confirm_work(struct work_struct *work)
{ {
struct smp_chan *smp = container_of(work, struct smp_chan, confirm); struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
...@@ -270,8 +282,7 @@ static void confirm_work(struct work_struct *work) ...@@ -270,8 +282,7 @@ static void confirm_work(struct work_struct *work)
return; return;
error: error:
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); smp_failure(conn, reason, 1);
smp_chan_destroy(conn);
} }
static void random_work(struct work_struct *work) static void random_work(struct work_struct *work)
...@@ -354,8 +365,7 @@ static void random_work(struct work_struct *work) ...@@ -354,8 +365,7 @@ static void random_work(struct work_struct *work)
return; return;
error: error:
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); smp_failure(conn, reason, 1);
smp_chan_destroy(conn);
} }
static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) 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) ...@@ -379,7 +389,15 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
void smp_chan_destroy(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); hci_conn_put(conn->hcon);
} }
...@@ -647,6 +665,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -647,6 +665,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
break; break;
case SMP_CMD_PAIRING_FAIL: case SMP_CMD_PAIRING_FAIL:
smp_failure(conn, skb->data[0], 0);
reason = 0; reason = 0;
err = -EPERM; err = -EPERM;
break; break;
...@@ -692,8 +711,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -692,8 +711,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
done: done:
if (reason) if (reason)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), smp_failure(conn, reason, 1);
&reason);
kfree_skb(skb); kfree_skb(skb);
return err; return err;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册