summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/compat_ioctl.c1
-rw-r--r--include/net/bluetooth/bluetooth.h1
-rw-r--r--include/net/bluetooth/hci.h68
-rw-r--r--include/net/bluetooth/hci_core.h19
-rw-r--r--include/net/bluetooth/rfcomm.h2
-rw-r--r--net/bluetooth/af_bluetooth.c53
-rw-r--r--net/bluetooth/bnep/core.c5
-rw-r--r--net/bluetooth/hci_conn.c96
-rw-r--r--net/bluetooth/hci_core.c42
-rw-r--r--net/bluetooth/hci_event.c512
-rw-r--r--net/bluetooth/hci_sock.c18
-rw-r--r--net/bluetooth/hci_sysfs.c31
-rw-r--r--net/bluetooth/hidp/core.c10
-rw-r--r--net/bluetooth/l2cap.c337
-rw-r--r--net/bluetooth/rfcomm/core.c92
-rw-r--r--net/bluetooth/rfcomm/sock.c23
-rw-r--r--net/bluetooth/rfcomm/tty.c59
-rw-r--r--net/bluetooth/sco.c14
18 files changed, 1112 insertions, 271 deletions
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index ddefb8851a98..7b3a03c7c6a9 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -2346,6 +2346,7 @@ COMPATIBLE_IOCTL(HCIGETDEVLIST)
COMPATIBLE_IOCTL(HCIGETDEVINFO)
COMPATIBLE_IOCTL(HCIGETCONNLIST)
COMPATIBLE_IOCTL(HCIGETCONNINFO)
+COMPATIBLE_IOCTL(HCIGETAUTHINFO)
COMPATIBLE_IOCTL(HCISETRAW)
COMPATIBLE_IOCTL(HCISETSCAN)
COMPATIBLE_IOCTL(HCISETAUTH)
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 750648df13f4..6f8418bf4241 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -121,6 +121,7 @@ void bt_sock_link(struct bt_sock_list *l, struct sock *s);
void bt_sock_unlink(struct bt_sock_list *l, struct sock *s);
int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags);
uint bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait);
+int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
void bt_accept_enqueue(struct sock *parent, struct sock *sk);
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index a8a9eb6af966..3cc294919312 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -72,8 +72,6 @@ enum {
HCI_INQUIRY,
HCI_RAW,
-
- HCI_SECMGR
};
/* HCI ioctl defines */
@@ -86,6 +84,7 @@ enum {
#define HCIGETDEVINFO _IOR('H', 211, int)
#define HCIGETCONNLIST _IOR('H', 212, int)
#define HCIGETCONNINFO _IOR('H', 213, int)
+#define HCIGETAUTHINFO _IOR('H', 215, int)
#define HCISETRAW _IOW('H', 220, int)
#define HCISETSCAN _IOW('H', 221, int)
@@ -97,8 +96,6 @@ enum {
#define HCISETACLMTU _IOW('H', 227, int)
#define HCISETSCOMTU _IOW('H', 228, int)
-#define HCISETSECMGR _IOW('H', 230, int)
-
#define HCIINQUIRY _IOR('H', 240, int)
/* HCI timeouts */
@@ -137,6 +134,8 @@ enum {
#define ESCO_EV4 0x0010
#define ESCO_EV5 0x0020
+#define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3)
+
/* ACL flags */
#define ACL_CONT 0x01
#define ACL_START 0x02
@@ -178,6 +177,8 @@ enum {
#define LMP_SNIFF_SUBR 0x02
+#define LMP_SIMPLE_PAIR 0x08
+
/* Connection modes */
#define HCI_CM_ACTIVE 0x0000
#define HCI_CM_HOLD 0x0001
@@ -199,6 +200,14 @@ enum {
#define HCI_LM_RELIABLE 0x0010
#define HCI_LM_SECURE 0x0020
+/* Authentication types */
+#define HCI_AT_NO_BONDING 0x00
+#define HCI_AT_NO_BONDING_MITM 0x01
+#define HCI_AT_DEDICATED_BONDING 0x02
+#define HCI_AT_DEDICATED_BONDING_MITM 0x03
+#define HCI_AT_GENERAL_BONDING 0x04
+#define HCI_AT_GENERAL_BONDING_MITM 0x05
+
/* ----- HCI Commands ---- */
#define HCI_OP_INQUIRY 0x0401
struct hci_cp_inquiry {
@@ -402,6 +411,17 @@ struct hci_rp_write_link_policy {
__le16 handle;
} __attribute__ ((packed));
+#define HCI_OP_READ_DEF_LINK_POLICY 0x080e
+struct hci_rp_read_def_link_policy {
+ __u8 status;
+ __le16 policy;
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_DEF_LINK_POLICY 0x080f
+struct hci_cp_write_def_link_policy {
+ __le16 policy;
+} __attribute__ ((packed));
+
#define HCI_OP_SNIFF_SUBRATE 0x0811
struct hci_cp_sniff_subrate {
__le16 handle;
@@ -501,6 +521,17 @@ struct hci_cp_host_buffer_size {
__le16 sco_max_pkt;
} __attribute__ ((packed));
+#define HCI_OP_READ_SSP_MODE 0x0c55
+struct hci_rp_read_ssp_mode {
+ __u8 status;
+ __u8 mode;
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_SSP_MODE 0x0c56
+struct hci_cp_write_ssp_mode {
+ __u8 mode;
+} __attribute__ ((packed));
+
#define HCI_OP_READ_LOCAL_VERSION 0x1001
struct hci_rp_read_local_version {
__u8 status;
@@ -696,6 +727,13 @@ struct hci_ev_clock_offset {
__le16 clock_offset;
} __attribute__ ((packed));
+#define HCI_EV_PKT_TYPE_CHANGE 0x1d
+struct hci_ev_pkt_type_change {
+ __u8 status;
+ __le16 handle;
+ __le16 pkt_type;
+} __attribute__ ((packed));
+
#define HCI_EV_PSCAN_REP_MODE 0x20
struct hci_ev_pscan_rep_mode {
bdaddr_t bdaddr;
@@ -774,6 +812,23 @@ struct extended_inquiry_info {
__u8 data[240];
} __attribute__ ((packed));
+#define HCI_EV_IO_CAPA_REQUEST 0x31
+struct hci_ev_io_capa_request {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed));
+
+#define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36
+struct hci_ev_simple_pair_complete {
+ __u8 status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed));
+
+#define HCI_EV_REMOTE_HOST_FEATURES 0x3d
+struct hci_ev_remote_host_features {
+ bdaddr_t bdaddr;
+ __u8 features[8];
+} __attribute__ ((packed));
+
/* Internal events generated by Bluetooth stack */
#define HCI_EV_STACK_INTERNAL 0xfd
struct hci_ev_stack_internal {
@@ -951,6 +1006,11 @@ struct hci_conn_info_req {
struct hci_conn_info conn_info[0];
};
+struct hci_auth_info_req {
+ bdaddr_t bdaddr;
+ __u8 type;
+};
+
struct hci_inquiry_req {
__u16 dev_id;
__u16 flags;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ea13baa3851b..cbf751094688 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -40,6 +40,7 @@ struct inquiry_data {
__u8 dev_class[3];
__le16 clock_offset;
__s8 rssi;
+ __u8 ssp_mode;
};
struct inquiry_entry {
@@ -75,6 +76,7 @@ struct hci_dev {
__u8 dev_class[3];
__u8 features[8];
__u8 commands[64];
+ __u8 ssp_mode;
__u8 hci_ver;
__u16 hci_rev;
__u16 manufacturer;
@@ -161,9 +163,12 @@ struct hci_conn {
__u8 attempt;
__u8 dev_class[3];
__u8 features[8];
+ __u8 ssp_mode;
__u16 interval;
+ __u16 pkt_type;
__u16 link_policy;
__u32 link_mode;
+ __u8 auth_type;
__u8 power_save;
unsigned long pend;
@@ -344,7 +349,7 @@ static inline void hci_conn_put(struct hci_conn *conn)
if (conn->state == BT_CONNECTED) {
timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT);
if (!conn->out)
- timeo *= 2;
+ timeo *= 5;
} else
timeo = msecs_to_jiffies(10);
} else
@@ -418,6 +423,7 @@ int hci_get_dev_list(void __user *arg);
int hci_get_dev_info(void __user *arg);
int hci_get_conn_list(void __user *arg);
int hci_get_conn_info(struct hci_dev *hdev, void __user *arg);
+int hci_get_auth_info(struct hci_dev *hdev, void __user *arg);
int hci_inquiry(void __user *arg);
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
@@ -459,6 +465,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF)
#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
#define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO)
+#define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR)
/* ----- HCI protocols ----- */
struct hci_proto {
@@ -474,7 +481,7 @@ struct hci_proto {
int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb);
int (*auth_cfm) (struct hci_conn *conn, __u8 status);
- int (*encrypt_cfm) (struct hci_conn *conn, __u8 status);
+ int (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
};
static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
@@ -532,17 +539,17 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
hp->auth_cfm(conn, status);
}
-static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status)
+static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt)
{
register struct hci_proto *hp;
hp = hci_proto[HCI_PROTO_L2CAP];
if (hp && hp->encrypt_cfm)
- hp->encrypt_cfm(conn, status);
+ hp->encrypt_cfm(conn, status, encrypt);
hp = hci_proto[HCI_PROTO_SCO];
if (hp && hp->encrypt_cfm)
- hp->encrypt_cfm(conn, status);
+ hp->encrypt_cfm(conn, status, encrypt);
}
int hci_register_proto(struct hci_proto *hproto);
@@ -579,7 +586,7 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encr
{
struct list_head *p;
- hci_proto_encrypt_cfm(conn, status);
+ hci_proto_encrypt_cfm(conn, status, encrypt);
read_lock_bh(&hci_cb_list_lock);
list_for_each(p, &hci_cb_list) {
diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
index 98ec7a320689..4dc8d92a4638 100644
--- a/include/net/bluetooth/rfcomm.h
+++ b/include/net/bluetooth/rfcomm.h
@@ -180,7 +180,9 @@ struct rfcomm_dlc {
u8 addr;
u8 priority;
u8 v24_sig;
+ u8 remote_v24_sig;
u8 mscex;
+ u8 out;
u32 link_mode;
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index d366423c8392..4e59df5f8e05 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -36,6 +36,7 @@
#include <linux/init.h>
#include <linux/poll.h>
#include <net/sock.h>
+#include <asm/ioctls.h>
#if defined(CONFIG_KMOD)
#include <linux/kmod.h>
@@ -48,7 +49,7 @@
#define BT_DBG(D...)
#endif
-#define VERSION "2.11"
+#define VERSION "2.12"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
@@ -266,6 +267,8 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
skb_reset_transport_header(skb);
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ if (err == 0)
+ sock_recv_timestamp(msg, sk, skb);
skb_free_datagram(sk, skb);
@@ -329,6 +332,54 @@ unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *w
}
EXPORT_SYMBOL(bt_sock_poll);
+int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ long amount;
+ int err;
+
+ BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg);
+
+ switch (cmd) {
+ case TIOCOUTQ:
+ if (sk->sk_state == BT_LISTEN)
+ return -EINVAL;
+
+ amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
+ if (amount < 0)
+ amount = 0;
+ err = put_user(amount, (int __user *) arg);
+ break;
+
+ case TIOCINQ:
+ if (sk->sk_state == BT_LISTEN)
+ return -EINVAL;
+
+ lock_sock(sk);
+ skb = skb_peek(&sk->sk_receive_queue);
+ amount = skb ? skb->len : 0;
+ release_sock(sk);
+ err = put_user(amount, (int __user *) arg);
+ break;
+
+ case SIOCGSTAMP:
+ err = sock_get_timestamp(sk, (struct timeval __user *) arg);
+ break;
+
+ case SIOCGSTAMPNS:
+ err = sock_get_timestampns(sk, (struct timespec __user *) arg);
+ break;
+
+ default:
+ err = -ENOIOCTLCMD;
+ break;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(bt_sock_ioctl);
+
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
{
DECLARE_WAITQUEUE(wait, current);
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 1d98a1b80da7..021172c0e666 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -503,6 +503,11 @@ static int bnep_session(void *arg)
/* Delete network device */
unregister_netdev(dev);
+ /* Wakeup user-space polling for socket errors */
+ s->sock->sk->sk_err = EUNATCH;
+
+ wake_up_interruptible(s->sock->sk->sk_sleep);
+
/* Release the socket */
fput(s->sock->file);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index f8880261da0e..ca8d05245ca0 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -59,24 +59,31 @@ void hci_acl_connect(struct hci_conn *conn)
BT_DBG("%p", conn);
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = 1;
+
conn->link_mode = HCI_LM_MASTER;
conn->attempt++;
+ conn->link_policy = hdev->link_policy;
+
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, &conn->dst);
cp.pscan_rep_mode = 0x02;
- if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst)) &&
- inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
- cp.pscan_rep_mode = ie->data.pscan_rep_mode;
- cp.pscan_mode = ie->data.pscan_mode;
- cp.clock_offset = ie->data.clock_offset | cpu_to_le16(0x8000);
+ if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst))) {
+ if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
+ cp.pscan_rep_mode = ie->data.pscan_rep_mode;
+ cp.pscan_mode = ie->data.pscan_mode;
+ cp.clock_offset = ie->data.clock_offset |
+ cpu_to_le16(0x8000);
+ }
+
memcpy(conn->dev_class, ie->data.dev_class, 3);
+ conn->ssp_mode = ie->data.ssp_mode;
}
- cp.pkt_type = cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
cp.role_switch = 0x01;
else
@@ -122,7 +129,7 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle)
conn->out = 1;
cp.handle = cpu_to_le16(handle);
- cp.pkt_type = cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp);
}
@@ -138,7 +145,7 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
conn->out = 1;
cp.handle = cpu_to_le16(handle);
- cp.pkt_type = cpu_to_le16(hdev->esco_type);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
@@ -163,11 +170,13 @@ static void hci_conn_timeout(unsigned long arg)
switch (conn->state) {
case BT_CONNECT:
+ case BT_CONNECT2:
if (conn->type == ACL_LINK)
hci_acl_connect_cancel(conn);
else
hci_acl_disconn(conn, 0x13);
break;
+ case BT_CONFIG:
case BT_CONNECTED:
hci_acl_disconn(conn, 0x13);
break;
@@ -199,13 +208,28 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
return NULL;
bacpy(&conn->dst, dst);
- conn->hdev = hdev;
- conn->type = type;
- conn->mode = HCI_CM_ACTIVE;
- conn->state = BT_OPEN;
+ conn->hdev = hdev;
+ conn->type = type;
+ conn->mode = HCI_CM_ACTIVE;
+ conn->state = BT_OPEN;
conn->power_save = 1;
+ switch (type) {
+ case ACL_LINK:
+ conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
+ break;
+ case SCO_LINK:
+ if (lmp_esco_capable(hdev))
+ conn->pkt_type = hdev->esco_type & SCO_ESCO_MASK;
+ else
+ conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
+ break;
+ case ESCO_LINK:
+ conn->pkt_type = hdev->esco_type;
+ break;
+ }
+
skb_queue_head_init(&conn->data_q);
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
@@ -221,8 +245,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
- hci_conn_add_sysfs(conn);
-
tasklet_enable(&hdev->tx_task);
return conn;
@@ -254,12 +276,14 @@ int hci_conn_del(struct hci_conn *conn)
}
tasklet_disable(&hdev->tx_task);
+
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
+
tasklet_enable(&hdev->tx_task);
+
skb_queue_purge(&conn->data_q);
- hci_conn_del_sysfs(conn);
return 0;
}
@@ -355,13 +379,21 @@ int hci_conn_auth(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
+ if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) {
+ if (!(conn->auth_type & 0x01)) {
+ conn->auth_type = HCI_AT_GENERAL_BONDING_MITM;
+ conn->link_mode &= ~HCI_LM_AUTH;
+ }
+ }
+
if (conn->link_mode & HCI_LM_AUTH)
return 1;
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_auth_requested cp;
cp.handle = cpu_to_le16(conn->handle);
- hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
+ hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
+ sizeof(cp), &cp);
}
return 0;
}
@@ -373,7 +405,7 @@ int hci_conn_encrypt(struct hci_conn *conn)
BT_DBG("conn %p", conn);
if (conn->link_mode & HCI_LM_ENCRYPT)
- return 1;
+ return hci_conn_auth(conn);
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
return 0;
@@ -382,7 +414,8 @@ int hci_conn_encrypt(struct hci_conn *conn)
struct hci_cp_set_conn_encrypt cp;
cp.handle = cpu_to_le16(conn->handle);
cp.encrypt = 1;
- hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp);
+ hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT,
+ sizeof(cp), &cp);
}
return 0;
}
@@ -396,7 +429,8 @@ int hci_conn_change_link_key(struct hci_conn *conn)
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_change_conn_link_key cp;
cp.handle = cpu_to_le16(conn->handle);
- hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY, sizeof(cp), &cp);
+ hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY,
+ sizeof(cp), &cp);
}
return 0;
}
@@ -498,6 +532,8 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
c->state = BT_CLOSED;
+ hci_conn_del_sysfs(c);
+
hci_proto_disconn_ind(c, 0x16);
hci_conn_del(c);
}
@@ -600,3 +636,23 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0;
}
+
+int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)
+{
+ struct hci_auth_info_req req;
+ struct hci_conn *conn;
+
+ if (copy_from_user(&req, arg, sizeof(req)))
+ return -EFAULT;
+
+ hci_dev_lock_bh(hdev);
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr);
+ if (conn)
+ req.type = conn->auth_type;
+ hci_dev_unlock_bh(hdev);
+
+ if (!conn)
+ return -ENOENT;
+
+ return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
+}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index aec6929f5c16..f5b21cb93699 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -279,10 +279,20 @@ static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt)
BT_DBG("%s %x", hdev->name, encrypt);
- /* Authentication */
+ /* Encryption */
hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt);
}
+static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
+{
+ __le16 policy = cpu_to_le16(opt);
+
+ BT_DBG("%s %x", hdev->name, opt);
+
+ /* Default link policy */
+ hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
+}
+
/* Get HCI device by index.
* Device is held on return. */
struct hci_dev *hci_dev_get(int index)
@@ -694,32 +704,35 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
msecs_to_jiffies(HCI_INIT_TIMEOUT));
break;
- case HCISETPTYPE:
- hdev->pkt_type = (__u16) dr.dev_opt;
- break;
-
case HCISETLINKPOL:
- hdev->link_policy = (__u16) dr.dev_opt;
+ err = hci_request(hdev, hci_linkpol_req, dr.dev_opt,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
break;
case HCISETLINKMODE:
- hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT);
+ hdev->link_mode = ((__u16) dr.dev_opt) &
+ (HCI_LM_MASTER | HCI_LM_ACCEPT);
+ break;
+
+ case HCISETPTYPE:
+ hdev->pkt_type = (__u16) dr.dev_opt;
break;
case HCISETACLMTU:
- hdev->acl_mtu = *((__u16 *)&dr.dev_opt + 1);
- hdev->acl_pkts = *((__u16 *)&dr.dev_opt + 0);
+ hdev->acl_mtu = *((__u16 *) &dr.dev_opt + 1);
+ hdev->acl_pkts = *((__u16 *) &dr.dev_opt + 0);
break;
case HCISETSCOMTU:
- hdev->sco_mtu = *((__u16 *)&dr.dev_opt + 1);
- hdev->sco_pkts = *((__u16 *)&dr.dev_opt + 0);
+ hdev->sco_mtu = *((__u16 *) &dr.dev_opt + 1);
+ hdev->sco_pkts = *((__u16 *) &dr.dev_opt + 0);
break;
default:
err = -EINVAL;
break;
}
+
hci_dev_put(hdev);
return err;
}
@@ -1270,9 +1283,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
- if (c->type != type || c->state != BT_CONNECTED
- || skb_queue_empty(&c->data_q))
+ if (c->type != type || skb_queue_empty(&c->data_q))
+ continue;
+
+ if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
continue;
+
num++;
if (c->sent < min) {
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 6aef8f24e581..0e3db289f4be 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -110,6 +110,25 @@ static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_unlock(hdev);
}
+static void hci_cc_read_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_rp_read_link_policy *rp = (void *) skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+ if (conn)
+ conn->link_policy = __le16_to_cpu(rp->policy);
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_write_link_policy *rp = (void *) skb->data;
@@ -128,13 +147,41 @@ static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
- if (conn) {
+ if (conn)
conn->link_policy = get_unaligned_le16(sent + 2);
- }
hci_dev_unlock(hdev);
}
+static void hci_cc_read_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_rp_read_def_link_policy *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hdev->link_policy = __le16_to_cpu(rp->policy);
+}
+
+static void hci_cc_write_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+ void *sent;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_LINK_POLICY);
+ if (!sent)
+ return;
+
+ if (!status)
+ hdev->link_policy = get_unaligned_le16(sent);
+
+ hci_req_complete(hdev, status);
+}
+
static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -151,12 +198,14 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
+ if (status)
+ return;
+
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME);
if (!sent)
return;
- if (!status)
- memcpy(hdev->dev_name, sent, 248);
+ memcpy(hdev->dev_name, sent, 248);
}
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -266,12 +315,14 @@ static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
+ if (status)
+ return;
+
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV);
if (!sent)
return;
- if (!status)
- memcpy(hdev->dev_class, sent, 3);
+ memcpy(hdev->dev_class, sent, 3);
}
static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
@@ -286,7 +337,7 @@ static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
setting = __le16_to_cpu(rp->voice_setting);
- if (hdev->voice_setting == setting )
+ if (hdev->voice_setting == setting)
return;
hdev->voice_setting = setting;
@@ -303,28 +354,31 @@ static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
+ __u16 setting;
void *sent;
BT_DBG("%s status 0x%x", hdev->name, status);
+ if (status)
+ return;
+
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_VOICE_SETTING);
if (!sent)
return;
- if (!status) {
- __u16 setting = get_unaligned_le16(sent);
+ setting = get_unaligned_le16(sent);
- if (hdev->voice_setting != setting) {
- hdev->voice_setting = setting;
+ if (hdev->voice_setting == setting)
+ return;
- BT_DBG("%s voice setting 0x%04x", hdev->name, setting);
+ hdev->voice_setting = setting;
- if (hdev->notify) {
- tasklet_disable(&hdev->tx_task);
- hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
- tasklet_enable(&hdev->tx_task);
- }
- }
+ BT_DBG("%s voice setting 0x%04x", hdev->name, setting);
+
+ if (hdev->notify) {
+ tasklet_disable(&hdev->tx_task);
+ hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
+ tasklet_enable(&hdev->tx_task);
}
}
@@ -337,6 +391,35 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, status);
}
+static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_rp_read_ssp_mode *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hdev->ssp_mode = rp->mode;
+}
+
+static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+ void *sent;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ if (status)
+ return;
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE);
+ if (!sent)
+ return;
+
+ hdev->ssp_mode = *((__u8 *) sent);
+}
+
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_read_local_version *rp = (void *) skb->data;
@@ -347,8 +430,8 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
return;
hdev->hci_ver = rp->hci_ver;
- hdev->hci_rev = btohs(rp->hci_rev);
- hdev->manufacturer = btohs(rp->manufacturer);
+ hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
+ hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name,
hdev->manufacturer,
@@ -536,11 +619,119 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status)
hci_dev_unlock(hdev);
}
+static void hci_cs_auth_requested(struct hci_dev *hdev, __u8 status)
+{
+ struct hci_cp_auth_requested *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_AUTH_REQUESTED);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ if (conn->state == BT_CONFIG) {
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_put(conn);
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status)
+{
+ struct hci_cp_set_conn_encrypt *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_SET_CONN_ENCRYPT);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ if (conn->state == BT_CONFIG) {
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_put(conn);
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%x", hdev->name, status);
}
+static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status)
+{
+ struct hci_cp_read_remote_features *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_FEATURES);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ if (conn->state == BT_CONFIG) {
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_put(conn);
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status)
+{
+ struct hci_cp_read_remote_ext_features *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ if (conn->state == BT_CONFIG) {
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_put(conn);
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
{
struct hci_cp_setup_sync_conn *cp;
@@ -653,6 +844,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
memcpy(data.dev_class, info->dev_class, 3);
data.clock_offset = info->clock_offset;
data.rssi = 0x00;
+ data.ssp_mode = 0x00;
info++;
hci_inquiry_cache_update(hdev, &data);
}
@@ -675,7 +867,14 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
if (!ev->status) {
conn->handle = __le16_to_cpu(ev->handle);
- conn->state = BT_CONNECTED;
+
+ if (conn->type == ACL_LINK) {
+ conn->state = BT_CONFIG;
+ hci_conn_hold(conn);
+ } else
+ conn->state = BT_CONNECTED;
+
+ hci_conn_add_sysfs(conn);
if (test_bit(HCI_AUTH, &hdev->flags))