summaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth')
-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
13 files changed, 1031 insertions, 261 deletions
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index d366423..4e59df5 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 1d98a1b..021172c0 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 f888026..ca8d052 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 aec6929..f5b21cb 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 6aef8f2..0e3db28 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))
conn->link_mode |= HCI_LM_AUTH;
@@ -687,30 +886,17 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
if (conn->type == ACL_LINK) {
struct hci_cp_read_remote_features cp;
cp.handle = ev->handle;
- hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, sizeof(cp), &cp);
- }
-
- /* Set link policy */
- if (conn->type == ACL_LINK && hdev->link_policy) {
- struct hci_cp_write_link_policy cp;
- cp.handle = ev->handle;
- cp.policy = cpu_to_le16(hdev->link_policy);
- hci_send_cmd(hdev, HCI_OP_WRITE_LINK_POLICY, sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
+ sizeof(cp), &cp);
}
/* Set packet type for incoming connection */
- if (!conn->out) {
+ if (!conn->out && hdev->hci_ver < 3) {
struct hci_cp_change_conn_ptype cp;
cp.handle = ev->handle;
- cp.pkt_type = (conn->type == ACL_LINK) ?
- cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
- cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
-
- hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), &cp);
- } else {
- /* Update disconnect timer */
- hci_conn_hold(conn);
- hci_conn_put(conn);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
+ hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE,
+ sizeof(cp), &cp);
}
} else
conn->state = BT_CLOSED;
@@ -730,9 +916,10 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
}
}
- hci_proto_connect_cfm(conn, ev->status);
- if (ev->status)
+ if (ev->status) {
+ hci_proto_connect_cfm(conn, ev->status);
hci_conn_del(conn);
+ }
unlock:
hci_dev_unlock(hdev);
@@ -752,10 +939,14 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
if (mask & HCI_LM_ACCEPT) {
/* Connection accepted */
+ struct inquiry_entry *ie;
struct hci_conn *conn;
hci_dev_lock(hdev);
+ if ((ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr)))
+ memcpy(ie->data.dev_class, ev->dev_class, 3);
+
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
if (!conn) {
if (!(conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr))) {
@@ -786,7 +977,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
struct hci_cp_accept_sync_conn_req cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
- 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);
@@ -822,6 +1013,9 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (conn) {
conn->state = BT_CLOSED;
+
+ hci_conn_del_sysfs(conn);
+
hci_proto_disconn_ind(conn, ev->reason);
hci_conn_del(conn);
}
@@ -845,15 +1039,29 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
- hci_auth_cfm(conn, ev->status);
+ if (conn->state == BT_CONFIG) {
+ if (!ev->status && hdev->ssp_mode > 0 &&
+ conn->ssp_mode > 0) {
+ struct hci_cp_set_conn_encrypt cp;
+ cp.handle = ev->handle;
+ cp.encrypt = 0x01;
+ hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT,
+ sizeof(cp), &cp);
+ } else {
+ conn->state = BT_CONNECTED;
+ hci_proto_connect_cfm(conn, ev->status);
+ hci_conn_put(conn);
+ }
+ } else
+ hci_auth_cfm(conn, ev->status);
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
if (!ev->status) {
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);
+ cp.handle = ev->handle;
+ cp.encrypt = 0x01;
+ hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT,
+ sizeof(cp), &cp);
} else {
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
hci_encrypt_cfm(conn, ev->status, 0x00);
@@ -883,15 +1091,24 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (conn) {
if (!ev->status) {
- if (ev->encrypt)
+ if (ev->encrypt) {
+ /* Encryption implies authentication */
+ conn->link_mode |= HCI_LM_AUTH;
conn->link_mode |= HCI_LM_ENCRYPT;
- else
+ } else
conn->link_mode &= ~HCI_LM_ENCRYPT;
}
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
- hci_encrypt_cfm(conn, ev->status, ev->encrypt);
+ if (conn->state == BT_CONFIG) {
+ if (!ev->status)
+ conn->state = BT_CONNECTED;
+
+ hci_proto_connect_cfm(conn, ev->status);
+ hci_conn_put(conn);
+ } else
+ hci_encrypt_cfm(conn, ev->status, ev->encrypt);
}
hci_dev_unlock(hdev);
@@ -926,14 +1143,29 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, ev->status);
- if (ev->status)
- return;
-
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
- if (conn)
- memcpy(conn->features, ev->features, 8);
+ if (conn) {
+ if (!ev->status)
+ memcpy(conn->features, ev->features, 8);
+
+ if (conn->state == BT_CONFIG) {
+ if (!ev->status && lmp_ssp_capable(hdev) &&
+ lmp_ssp_capable(conn)) {
+ struct hci_cp_read_remote_ext_features cp;
+ cp.handle = ev->handle;
+ cp.page = 0x01;
+ hci_send_cmd(hdev,
+ HCI_OP_READ_REMOTE_EXT_FEATURES,
+ sizeof(cp), &cp);
+ } else {
+ conn->state = BT_CONNECTED;
+ hci_proto_connect_cfm(conn, ev->status);
+ hci_conn_put(conn);
+ }
+ }
+ }
hci_dev_unlock(hdev);
}
@@ -974,10 +1206,22 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_role_discovery(hdev, skb);
break;
+ case HCI_OP_READ_LINK_POLICY:
+ hci_cc_read_link_policy(hdev, skb);
+ break;
+
case HCI_OP_WRITE_LINK_POLICY:
hci_cc_write_link_policy(hdev, skb);
break;
+ case HCI_OP_READ_DEF_LINK_POLICY:
+ hci_cc_read_def_link_policy(hdev, skb);
+ break;
+
+ case HCI_OP_WRITE_DEF_LINK_POLICY:
+ hci_cc_write_def_link_policy(hdev, skb);
+ break;
+
case HCI_OP_RESET:
hci_cc_reset(hdev, skb);
break;
@@ -1022,6 +1266,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_host_buffer_size(hdev, skb);
break;
+ case HCI_OP_READ_SSP_MODE:
+ hci_cc_read_ssp_mode(hdev, skb);
+ break;
+
+ case HCI_OP_WRITE_SSP_MODE:
+ hci_cc_write_ssp_mode(hdev, skb);
+ break;
+
case HCI_OP_READ_LOCAL_VERSION:
hci_cc_read_local_version(hdev, skb);
break;
@@ -1076,10 +1328,26 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_add_sco(hdev, ev->status);
break;
+ case HCI_OP_AUTH_REQUESTED:
+ hci_cs_auth_requested(hdev, ev->status);
+ break;
+
+ case HCI_OP_SET_CONN_ENCRYPT:
+ hci_cs_set_conn_encrypt(hdev, ev->status);
+ break;
+
case HCI_OP_REMOTE_NAME_REQ:
hci_cs_remote_name_req(hdev, ev->status);
break;
+ case HCI_OP_READ_REMOTE_FEATURES:
+ hci_cs_read_remote_features(hdev, ev->status);
+ break;
+
+ case HCI_OP_READ_REMOTE_EXT_FEATURES:
+ hci_cs_read_remote_ext_features(hdev, ev->status);
+ break;
+
case HCI_OP_SETUP_SYNC_CONN:
hci_cs_setup_sync_conn(hdev, ev->status);
break;
@@ -1235,6 +1503,22 @@ static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_dev_unlock(hdev);
}
+static inline void hci_pkt_type_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_pkt_type_change *ev = (void *) skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status %d", hdev->name, ev->status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+ if (conn && !ev->status)
+ conn->pkt_type = __le16_to_cpu(ev->pkt_type);
+
+ hci_dev_unlock(hdev);
+}
+
static inline void hci_pscan_rep_mode_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_pscan_rep_mode *ev = (void *) skb->data;
@@ -1275,6 +1559,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
memcpy(data.dev_class, info->dev_class, 3);
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
+ data.ssp_mode = 0x00;
info++;
hci_inquiry_cache_update(hdev, &data);
}
@@ -1289,6 +1574,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
memcpy(data.dev_class, info->dev_class, 3);
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
+ data.ssp_mode = 0x00;
info++;
hci_inquiry_cache_update(hdev, &data);
}
@@ -1299,7 +1585,43 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
+ struct hci_ev_remote_ext_features *ev = (void *) skb->data;
+ struct hci_conn *conn;
+
BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+ if (conn) {
+ if (!ev->status && ev->page == 0x01) {
+ struct inquiry_entry *ie;
+
+ if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst)))
+ ie->data.ssp_mode = (ev->features[0] & 0x01);
+
+ conn->ssp_mode = (ev->features[0] & 0x01);
+ }
+
+ if (conn->state == BT_CONFIG) {
+ if (!ev->status && hdev->ssp_mode > 0 &&
+ conn->ssp_mode > 0) {
+ if (conn->out) {
+ struct hci_cp_auth_requested cp;
+ cp.handle = ev->handle;
+ hci_send_cmd(hdev,
+ HCI_OP_AUTH_REQUESTED,
+ sizeof(cp), &cp);
+ }
+ } else {
+ conn->state = BT_CONNECTED;
+ hci_proto_connect_cfm(conn, ev->status);
+ hci_conn_put(conn);
+ }
+ }
+ }
+
+ hci_dev_unlock(hdev);
}
static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1312,12 +1634,22 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
- if (!conn)
- goto unlock;
+ if (!conn) {
+ if (ev->link_type == ESCO_LINK)
+ goto unlock;
+
+ conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr);
+ if (!conn)
+ goto unlock;
+
+ conn->type = SCO_LINK;
+ }
if (!ev->status) {
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
+
+ hci_conn_add_sysfs(conn);
} else
conn->state = BT_CLOSED;
@@ -1371,6 +1703,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
memcpy(data.dev_class, info->dev_class, 3);
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
+ data.ssp_mode = 0x01;
info++;
hci_inquiry_cache_update(hdev, &data);
}
@@ -1378,6 +1711,53 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
hci_dev_unlock(hdev);
}
+static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_io_capa_request *ev = (void *) skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+ if (conn)
+ hci_conn_hold(conn);
+
+ hci_dev_unlock(hdev);
+}
+
+static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_simple_pair_complete *ev = (void *) skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+ if (conn)
+ hci_conn_put(conn);
+
+ hci_dev_unlock(hdev);
+}
+
+static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_remote_host_features *ev = (void *) skb->data;
+ struct inquiry_entry *ie;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ if ((ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr)))
+ ie->data.ssp_mode = (ev->features[0] & 0x01);
+
+ hci_dev_unlock(hdev);
+}
+
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
@@ -1470,6 +1850,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_clock_offset_evt(hdev, skb);
break;
+ case HCI_EV_PKT_TYPE_CHANGE:
+ hci_pkt_type_change_evt(hdev, skb);
+ break;
+
case HCI_EV_PSCAN_REP_MODE:
hci_pscan_rep_mode_evt(hdev, skb);
break;
@@ -1498,6 +1882,18 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_extended_inquiry_result_evt(hdev, skb);
break;
+ case HCI_EV_IO_CAPA_REQUEST:
+ hci_io_capa_request_evt(hdev, skb);
+ break;
+
+ case HCI_EV_SIMPLE_PAIR_COMPLETE:
+ hci_simple_pair_complete_evt(hdev, skb);
+ break;
+
+ case HCI_EV_REMOTE_HOST_FEATURES:
+ hci_remote_host_features_evt(hdev, skb);
+ break;
+
default:
BT_DBG("%s event 0x%x", hdev->name, event);
break;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 747fabd..d62579b 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -193,19 +193,11 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign
return 0;
- case HCISETSECMGR:
- if (!capable(CAP_NET_ADMIN))
- return -EACCES;
-
- if (arg)
- set_bit(HCI_SECMGR, &hdev->flags);
- else
- clear_bit(HCI_SECMGR, &hdev->flags);
-
- return 0;
-
case HCIGETCONNINFO:
- return hci_get_conn_info(hdev, (void __user *)arg);
+ return hci_get_conn_info(hdev, (void __user *) arg);
+
+ case HCIGETAUTHINFO:
+ return hci_get_auth_info(hdev, (void __user *) arg);
default:
if (hdev->ioctl)
@@ -217,7 +209,7 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
- void __user *argp = (void __user *)arg;
+ void __user *argp = (void __user *) arg;
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 84360c1..844ca5f 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -113,11 +113,13 @@ static ssize_t show_inquiry_cache(struct device *dev, struct device_attribute *a
struct inquiry_data *data = &e->data;
bdaddr_t bdaddr;
baswap(&bdaddr, &data->bdaddr);
- n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %u\n",
+ n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
batostr(&bdaddr),
- data->pscan_rep_mode, data->pscan_period_mode, data->pscan_mode,
- data->dev_class[2], data->dev_class[1], data->dev_class[0],
- __le16_to_cpu(data->clock_offset), data->rssi, e->timestamp);
+ data->pscan_rep_mode, data->pscan_period_mode,
+ data->pscan_mode, data->dev_class[2],
+ data->dev_class[1], data->dev_class[0],
+ __le16_to_cpu(data->clock_offset),
+ data->rssi, data->ssp_mode, e->timestamp);
}
hci_dev_unlock_bh(hdev);
@@ -249,15 +251,28 @@ static ssize_t show_conn_address(struct device *dev, struct device_attribute *at
return sprintf(buf, "%s\n", batostr(&bdaddr));
}
+static ssize_t show_conn_features(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hci_conn *conn = dev_get_drvdata(dev);
+
+ return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ conn->features[0], conn->features[1],
+ conn->features[2], conn->features[3],
+ conn->features[4], conn->features[5],
+ conn->features[6], conn->features[7]);
+}
+
#define CONN_ATTR(_name,_mode,_show,_store) \
struct device_attribute conn_attr_##_name = __ATTR(_name,_mode,_show,_store)
static CONN_ATTR(type, S_IRUGO, show_conn_type, NULL);
static CONN_ATTR(address, S_IRUGO, show_conn_address, NULL);
+static CONN_ATTR(features, S_IRUGO, show_conn_features, NULL);
static struct device_attribute *conn_attrs[] = {
&conn_attr_type,
&conn_attr_address,
+ &conn_attr_features,
NULL
};
@@ -296,7 +311,6 @@ static void add_conn(struct work_struct *work)
void hci_conn_add_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
- bdaddr_t *ba = &conn->dst;
BT_DBG("conn %p", conn);
@@ -305,11 +319,8 @@ void hci_conn_add_sysfs(struct hci_conn *conn)
conn->dev.release = bt_release;
- snprintf(conn->dev.bus_id, BUS_ID_SIZE,
- "%s%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X",
- conn->type == ACL_LINK ? "acl" : "sco",
- ba->b[5], ba->b[4], ba->b[3],
- ba->b[2], ba->b[1], ba->b[0]);
+ snprintf(conn->dev.bus_id, BUS_ID_SIZE, "%s:%d",
+ hdev->name, conn->handle);
dev_set_drvdata(&conn->dev, conn);
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 519cdb9..96434d7 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -581,6 +581,12 @@ static int hidp_session(void *arg)
hid_free_device(session->hid);
}
+ /* Wakeup user-space polling for socket errors */
+ session->intr_sock->sk->sk_err = EUNATCH;
+ session->ctrl_sock->sk->sk_err = EUNATCH;
+
+ hidp_schedule(session);
+
fput(session->intr_sock->file);
wait_event_timeout(*(ctrl_sk->sk_sleep),
@@ -879,6 +885,10 @@ int hidp_del_connection(struct hidp_conndel_req *req)
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
+ /* Wakeup user-space polling for socket errors */
+ session->intr_sock->sk->sk_err = EUNATCH;
+ session->ctrl_sock->sk->sk_err = EUNATCH;
+
/* Kill session thread */
atomic_inc(&session->terminate);
hidp_schedule(session);
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 6e180d2..c123985 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -55,7 +55,7 @@
#define BT_DBG(D...)
#endif
-#define VERSION "2.9"
+#define VERSION "2.10"
static u32 l2cap_feat_mask = 0x0000;
@@ -76,11 +76,21 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
static void l2cap_sock_timeout(unsigned long arg)
{
struct sock *sk = (struct sock *) arg;
+ int reason;
BT_DBG("sock %p state %d", sk, sk->sk_state);
bh_lock_sock(sk);
- __l2cap_sock_close(sk, ETIMEDOUT);
+
+ if (sk->sk_state == BT_CONNECT &&
+ (l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH |
+ L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)))
+ reason = ECONNREFUSED;
+ else
+ reason = ETIMEDOUT;
+
+ __l2cap_sock_close(sk, reason);
+
bh_unlock_sock(sk);
l2cap_sock_kill(sk);
@@ -240,7 +250,7 @@ static void l2cap_chan_del(struct sock *sk, int err)
hci_conn_put(conn->hcon);
}
- sk->sk_state = BT_CLOSED;
+ sk->sk_state = BT_CLOSED;
sock_set_flag(sk, SOCK_ZAPPED);
if (err)
@@ -253,6 +263,21 @@ static void l2cap_chan_del(struct sock *sk, int err)
sk->sk_state_change(sk);
}
+/* Service level security */
+static inline int l2cap_check_link_mode(struct sock *sk)
+{
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+
+ if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
+ (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE))
+ return hci_conn_encrypt(conn->hcon);
+
+ if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH)
+ return hci_conn_auth(conn->hcon);
+
+ return 1;
+}
+
static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
{
u8 id;
@@ -287,6 +312,36 @@ static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16
return hci_send_acl(conn->hcon, skb, 0);
}
+static void l2cap_do_start(struct sock *sk)
+{
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+ if (l2cap_check_link_mode(sk)) {
+ struct l2cap_conn_req req;
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ req.psm = l2cap_pi(sk)->psm;
+
+ l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_REQ, sizeof(req), &req);
+ }
+ } else {
+ struct l2cap_info_req req;
+ req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
+ conn->info_ident = l2cap_get_ident(conn);
+
+ mod_timer(&conn->info_timer, jiffies +
+ msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+
+ l2cap_send_cmd(conn, conn->info_ident,
+ L2CAP_INFO_REQ, sizeof(req), &req);
+ }
+}
+
/* ---- L2CAP connections ---- */
static void l2cap_conn_start(struct l2cap_conn *conn)
{
@@ -301,16 +356,37 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
bh_lock_sock(sk);
if (sk->sk_type != SOCK_SEQPACKET) {
- l2cap_sock_clear_timer(sk);
- sk->sk_state = BT_CONNECTED;
- sk->sk_state_change(sk);
- } else if (sk->sk_state == BT_CONNECT) {
- struct l2cap_conn_req req;
- l2cap_pi(sk)->ident = l2cap_get_ident(conn);
- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
- req.psm = l2cap_pi(sk)->psm;
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ bh_unlock_sock(sk);
+ continue;
+ }
+
+ if (sk->sk_state == BT_CONNECT) {
+ if (l2cap_check_link_mode(sk)) {
+ struct l2cap_conn_req req;
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ req.psm = l2cap_pi(sk)->psm;
+
+ l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_REQ, sizeof(req), &req);
+ }
+ } else if (sk->sk_state == BT_CONNECT2) {
+ struct l2cap_conn_rsp rsp;
+ rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+
+ if (l2cap_check_link_mode(sk)) {
+ sk->sk_state = BT_CONFIG;
+ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+ } else {
+ rsp.result = cpu_to_le16(L2CAP_CR_PEND);
+ rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
+ }
+
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_RSP, sizeof(rsp), &rsp);
}
bh_unlock_sock(sk);
@@ -321,22 +397,27 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
- BT_DBG("conn %p", conn);
+ struct l2cap_chan_list *l = &conn->chan_list;
+ struct sock *sk;
- if (conn->chan_list.head || !hlist_empty(&l2cap_sk_list.head)) {
- struct l2cap_info_req req;
+ BT_DBG("conn %p", conn);
- req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+ read_lock(&l->lock);
- conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
- conn->info_ident = l2cap_get_ident(conn);
+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+ bh_lock_sock(sk);
- mod_timer(&conn->info_timer,
- jiffies + msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+ if (sk->sk_type != SOCK_SEQPACKET) {
+ l2cap_sock_clear_timer(sk);
+ sk->sk_state = BT_CONNECTED;
+ sk->sk_state_change(sk);
+ } else if (sk->sk_state == BT_CONNECT)
+ l2cap_do_start(sk);
- l2cap_send_cmd(conn, conn->info_ident,
- L2CAP_INFO_REQ, sizeof(req), &req);
+ bh_unlock_sock(sk);
}
+
+ read_unlock(&l->lock);
}
/* Notify sockets that we cannot guaranty reliability anymore */
@@ -388,7 +469,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->feat_mask = 0;
- setup_timer(&conn->info_timer, l2cap_info_timeout, (unsigned long)conn);
+ setup_timer(&conn->info_timer, l2cap_info_timeout,
+ (unsigned long) conn);
spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock);
@@ -500,7 +582,7 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
while ((sk = bt_accept_dequeue(parent, NULL)))
l2cap_sock_close(sk);
- parent->sk_state = BT_CLOSED;
+ parent->sk_state = BT_CLOSED;
sock_set_flag(parent, SOCK_ZAPPED);
}
@@ -543,9 +625,8 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);
- } else {
+ } else
l2cap_chan_del(sk, reason);
- }
break;
case BT_CONNECT:
@@ -614,9 +695,9 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = proto;
- sk->sk_state = BT_OPEN;
+ sk->sk_state = BT_OPEN;
- setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long)sk);
+ setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);
bt_sock_link(&l2cap_sk_list, sk);
return sk;
@@ -729,22 +810,11 @@ static int l2cap_do_connect(struct sock *sk)
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
if (hcon->state == BT_CONNECTED) {
- if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) {
- l2cap_conn_ready(conn);
- goto done;
- }
-
- if (sk->sk_type == SOCK_SEQPACKET) {
- struct l2cap_conn_req req;
- l2cap_pi(sk)->ident = l2cap_get_ident(conn);
- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
- req.psm = l2cap_pi(sk)->psm;
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_REQ, sizeof(req), &req);
- } else {
+ if (sk->sk_type != SOCK_SEQPACKET) {
l2cap_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED;
- }
+ } else
+ l2cap_do_start(sk);
}
done:
@@ -1145,7 +1215,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
__l2cap_sock_close(sk, 0);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
- err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
+ err = bt_sock_wait_state(sk, BT_CLOSED,
+ sk->sk_lingertime);
}
release_sock(sk);
return err;
@@ -1189,6 +1260,11 @@ static void l2cap_chan_ready(struct sock *sk)
*/
parent->sk_data_ready(parent, 0);
}
+
+ if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) {
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ hci_conn_change_link_key(conn->hcon);
+ }
}
/* Copy frame to all raw sockets on that connection */
@@ -1477,7 +1553,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
struct sock *sk, *parent;
- int result = 0, status = 0;
+ int result, status = 0;
u16 dcid = 0, scid = __le16_to_cpu(req->scid);
__le16 psm = req->psm;
@@ -1526,25 +1602,24 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
- /* Service level security */
- result = L2CAP_CR_PEND;
- status = L2CAP_CS_AUTHEN_PEND;
- sk->sk_state = BT_CONNECT2;
l2cap_pi(sk)->ident = cmd->ident;
- if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
- (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) {
- if (!hci_conn_encrypt(conn->hcon))
- goto done;
- } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
- if (!hci_conn_auth(conn->hcon))
- goto done;
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+ if (l2cap_check_link_mode(sk)) {
+ sk->sk_state = BT_CONFIG;
+ result = L2CAP_CR_SUCCESS;
+ status = L2CAP_CS_NO_INFO;
+ } else {
+ sk->sk_state = BT_CONNECT2;
+ result = L2CAP_CR_PEND;
+ status = L2CAP_CS_AUTHEN_PEND;
+ }
+ } else {
+ sk->sk_state = BT_CONNECT2;
+ result = L2CAP_CR_PEND;
+ status = L2CAP_CS_NO_INFO;
}
- sk->sk_state = BT_CONFIG;
- result = status = 0;
-
-done:
write_unlock_bh(&list->lock);
response:
@@ -1556,6 +1631,21 @@ sendresp:
rsp.result = cpu_to_le16(result);
rsp.status = cpu_to_le16(status);
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+
+ if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
+ struct l2cap_info_req info;
+ info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
+ conn->info_ident = l2cap_get_ident(conn);
+
+ mod_timer(&conn->info_timer, jiffies +
+ msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+
+ l2cap_send_cmd(conn, conn->info_ident,
+ L2CAP_INFO_REQ, sizeof(info), &info);
+ }
+
return 0;
}
@@ -1664,9 +1754,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
}
if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
- u8 req[64];
+ u8 buf[64];
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(sk, req), req);
+ l2cap_build_conf_req(sk, buf), buf);
}
unlock:
@@ -1708,7 +1798,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
default:
sk->sk_state = BT_DISCONN;
- sk->sk_err = ECONNRESET;
+ sk->sk_err = ECONNRESET;
l2cap_sock_set_timer(sk, HZ * 5);
{
struct l2cap_disconn_req req;
@@ -2080,10 +2170,8 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason)
static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
{
struct l2cap_chan_list *l;
- struct l2cap_conn *conn = conn = hcon->l2cap_data;
- struct l2cap_conn_rsp rsp;
+ struct l2cap_conn *conn = hcon->l2cap_data;
struct sock *sk;
- int result;
if (!conn)
return 0;
@@ -2095,45 +2183,65 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+
bh_lock_sock(sk);
- if (sk->sk_state != BT_CONNECT2 ||
- (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
- (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) {
+ if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
+ !(hcon->link_mode & HCI_LM_ENCRYPT) &&
+ !status) {
bh_unlock_sock(sk);
continue;
}
- if (!status) {
- sk->sk_state = BT_CONFIG;
- result = 0;
- } else {
- sk->sk_state = BT_DISCONN;
- l2cap_sock_set_timer(sk, HZ/10);
- result = L2CAP_CR_SEC_BLOCK;
- }
+ if (sk->sk_state == BT_CONNECT) {
+ if (!status) {
+ struct l2cap_conn_req req;
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ req.psm = l2cap_pi(sk)->psm;
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
- rsp.result = cpu_to_le16(result);
- rsp.status = cpu_to_le16(0);
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_REQ, sizeof(req), &req);
+ } else {
+ l2cap_sock_clear_timer(sk);
+ l2cap_sock_set_timer(sk, HZ / 10);
+ }
+ } else if (sk->sk_state == BT_CONNECT2) {
+ struct l2cap_conn_rsp rsp;
+ __u16 result;
+
+ if (!status) {
+ sk->sk_state = BT_CONFIG;
+ result = L2CAP_CR_SUCCESS;
+ } else {
+ sk->sk_state = BT_DISCONN;
+ l2cap_sock_set_timer(sk, HZ / 10);
+ result = L2CAP_CR_SEC_BLOCK;
+ }
+
+ rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+ rsp.result = cpu_to_le16(result);
+ rsp.status = cpu_to_le16(0);
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ }
bh_unlock_sock(sk);
}
read_unlock(&l->lock);
+
return 0;
}
-static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status)
+static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
{
struct l2cap_chan_list *l;
struct l2cap_conn *conn = hcon->l2cap_data;
- struct l2cap_conn_rsp rsp;
struct sock *sk;
- int result;
if (!conn)
return 0;
@@ -2145,36 +2253,59 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status)
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+
bh_lock_sock(sk);
- if (sk->sk_state != BT_CONNECT2) {
+ if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
+ (sk->sk_state == BT_CONNECTED ||
+ sk->sk_state == BT_CONFIG) &&
+ !status && encrypt == 0x00) {
+ __l2cap_sock_close(sk, ECONNREFUSED);
bh_unlock_sock(sk);
continue;
}
- if (!status) {
- sk->sk_state = BT_CONFIG;
- result = 0;
- } else {
- sk->sk_state = BT_DISCONN;
- l2cap_sock_set_timer(sk, HZ/10);
- result = L2CAP_CR_SEC_BLOCK;
- }
+ if (sk->sk_state == BT_CONNECT) {
+ if (!status) {
+ struct l2cap_conn_req req;
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ req.psm = l2cap_pi(sk)->psm;
+
+ l2cap_pi(sk)->ident = l2cap_get_ident(conn);
- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
- rsp.result = cpu_to_le16(result);
- rsp.status = cpu_to_le16(0);
- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_REQ, sizeof(req), &req);
+ } else {
+ l2cap_sock_clear_timer(sk);
+ l2cap_sock_set_timer(sk, HZ / 10);
+ }
+ } else if (sk->sk_state == BT_CONNECT2) {
+ struct l2cap_conn_rsp rsp;
+ __u16 result;
+
+ if (!status) {
+ sk->sk_state = BT_CONFIG;
+ result = L2CAP_CR_SUCCESS;
+ } else {
+ sk->sk_state = BT_DISCONN;
+ l2cap_sock_set_timer(sk, HZ / 10);
+ result = L2CAP_CR_SEC_BLOCK;
+ }
- if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)
- hci_conn_change_link_key(hcon);
+ rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+ rsp.result = cpu_to_le16(result);
+ rsp.status = cpu_to_le16(0);
+ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+ L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ }
bh_unlock_sock(sk);
}
read_unlock(&l->lock);
+
return 0;
}
@@ -2301,9 +2432,9 @@ static const struct proto_ops l2cap_sock_ops = {
.sendmsg = l2cap_sock_sendmsg,
.recvmsg = bt_sock_recvmsg,
.poll = bt_sock_poll,
+ .ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
- .ioctl = sock_no_ioctl,
.shutdown = l2cap_sock_shutdown,
.setsockopt = l2cap_sock_setsockopt,
.getsockopt = l2cap_sock_getsockopt
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index b4fb84e..6cfc7ba 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -51,7 +51,7 @@
#define BT_DBG(D...)
#endif
-#define VERSION "1.8"
+#define VERSION "1.10"
static int disable_cfc = 0;
static int channel_mtu = -1;
@@ -228,6 +228,21 @@ static int rfcomm_l2sock_create(struct socket **sock)
return err;
}
+static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
+{
+ struct sock *sk = d->session->sock->sk;
+
+ if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
+ if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
+ return 1;
+ } else if (d->link_mode & RFCOMM_LM_AUTH) {
+ if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
+ return 1;
+ }
+
+ return 0;
+}
+
/* ---- RFCOMM DLCs ---- */
static void rfcomm_dlc_timeout(unsigned long arg)
{
@@ -369,15 +384,23 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
d->addr = __addr(s->initiator, dlci);
d->priority = 7;
- d->state = BT_CONFIG;
+ d->state = BT_CONFIG;
rfcomm_dlc_link(s, d);
+ d->out = 1;
+
d->mtu = s->mtu;
d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
- if (s->state == BT_CONNECTED)
- rfcomm_send_pn(s, 1, d);
+ if (s->state == BT_CONNECTED) {
+ if (rfcomm_check_link_mode(d))
+ set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+ else
+ rfcomm_send_pn(s, 1, d);
+ }
+
rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
+
return 0;
}
@@ -1144,21 +1167,6 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
return 0;
}
-static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
-{
- struct sock *sk = d->session->sock->sk;
-
- if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
- if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
- return 1;
- } else if (d->link_mode & RFCOMM_LM_AUTH) {
- if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
- return 1;
- }
-
- return 0;
-}
-
static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
{
struct sock *sk = d->session->sock->sk;
@@ -1203,10 +1211,8 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
if (rfcomm_check_link_mode(d)) {
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
- return 0;
- }
-
- rfcomm_dlc_accept(d);
+ } else
+ rfcomm_dlc_accept(d);
}
return 0;
}
@@ -1221,10 +1227,8 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
if (rfcomm_check_link_mode(d)) {
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
- return 0;
- }
-
- rfcomm_dlc_accept(d);
+ } else
+ rfcomm_dlc_accept(d);
} else {
rfcomm_send_dm(s, dlci);
}
@@ -1457,8 +1461,12 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
rfcomm_dlc_lock(d);
+
+ d->remote_v24_sig = msc->v24_sig;
+
if (d->modem_status)
d->modem_status(d, msc->v24_sig);
+
rfcomm_dlc_unlock(d);
rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
@@ -1634,7 +1642,11 @@ static void rfcomm_process_connect(struct rfcomm_session *s)
d = list_entry(p, struct rfcomm_dlc, list);
if (d->state == BT_CONFIG) {
d->mtu = s->mtu;
- rfcomm_send_pn(s, 1, d);
+ if (rfcomm_check_link_mode(d)) {
+ set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+ rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+ } else
+ rfcomm_send_pn(s, 1, d);
}
}
}
@@ -1707,7 +1719,11 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) {
rfcomm_dlc_clear_timer(d);
- rfcomm_dlc_accept(d);
+ if (d->out) {
+ rfcomm_send_pn(s, 1, d);
+ rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
+ } else
+ rfcomm_dlc_accept(d);
if (d->link_mode & RFCOMM_LM_SECURE) {
struct sock *sk = s->sock->sk;
hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
@@ -1715,7 +1731,10 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
continue;
} else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
rfcomm_dlc_clear_timer(d);
- rfcomm_send_dm(s, d->dlci);
+ if (!d->out)
+ rfcomm_send_dm(s, d->dlci);
+ else
+ d->state = BT_CLOSED;
__rfcomm_dlc_close(d, ECONNREFUSED);
continue;
}
@@ -1724,7 +1743,7 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
continue;
if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) &&
- d->mscex == RFCOMM_MSCEX_OK)
+ d->mscex == RFCOMM_MSCEX_OK)
rfcomm_process_tx(d);
}
}
@@ -1952,7 +1971,8 @@ static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
- if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE))
+ if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
+ !(conn->link_mode & HCI_LM_ENCRYPT) && !status)
continue;
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
@@ -1986,6 +2006,14 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
+ if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
+ (d->state == BT_CONNECTED ||
+ d->state == BT_CONFIG) &&
+ !status && encrypt == 0x00) {
+ __rfcomm_dlc_close(d, ECONNREFUSED);
+ continue;
+ }
+
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
continue;
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index c905448..8a972b6 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -307,13 +307,13 @@ static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int
sk->sk_destruct = rfcomm_sock_destruct;
sk->sk_sndtimeo = RFCOMM_CONN_TIMEOUT;
- sk->sk_sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
- sk->sk_rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
+ sk->sk_sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
+ sk->sk_rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = proto;
- sk->sk_state = BT_OPEN;
+ sk->sk_state = BT_OPEN;
bt_sock_link(&rfcomm_sk_list, sk);
@@ -411,6 +411,8 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr);
rfcomm_pi(sk)->channel = sa->rc_channel;
+ d->link_mode = rfcomm_pi(sk)->link_mode;
+
err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
if (!err)
err = bt_sock_wait_state(sk, BT_CONNECTED,
@@ -686,6 +688,8 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
copied += chunk;
size -= chunk;
+ sock_recv_timestamp(msg, sk, skb);
+
if (!(flags & MSG_PEEK)) {
atomic_sub(chunk, &sk->sk_rmem_alloc);
@@ -791,15 +795,20 @@ static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned lon
struct sock *sk = sock->sk;
int err;
- lock_sock(sk);
+ BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg);
+
+ err = bt_sock_ioctl(sock, cmd, arg);
+ if (err == -ENOIOCTLCMD) {
#ifdef CONFIG_BT_RFCOMM_TTY
- err = rfcomm_dev_ioctl(sk, cmd, (void __user *)arg);
+ lock_sock(sk);
+ err = rfcomm_dev_ioctl(sk, cmd, (void __user *) arg);
+ release_sock(sk);
#else
- err = -EOPNOTSUPP;
+ err = -EOPNOTSUPP;
#endif
+ }
- release_sock(sk);
return err;
}
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index be84f4f..5d16357 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -75,6 +75,8 @@ struct rfcomm_dev {
struct device *tty_dev;
atomic_t wmem_alloc;
+
+ struct sk_buff_head pending;
};
static LIST_HEAD(rfcomm_dev_list);
@@ -262,13 +264,34 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
init_waitqueue_head(&dev->wait);
tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
+ skb_queue_head_init(&dev->pending);
+
rfcomm_dlc_lock(dlc);
+
+ if (req->flags & (1 << RFCOMM_REUSE_DLC)) {
+ struct sock *sk = dlc->owner;
+ struct sk_buff *skb;
+
+ BUG_ON(!sk);
+
+ rfcomm_dlc_throttle(dlc);
+
+ while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+ skb_orphan(skb);
+ skb_queue_tail(&dev->pending, skb);
+ atomic_sub(skb->len, &sk->sk_rmem_alloc);
+ }
+ }
+
dlc->data_ready = rfcomm_dev_data_ready;
dlc->state_change = rfcomm_dev_state_change;
dlc->modem_status = rfcomm_dev_modem_status;
dlc->owner = dev;
dev->dlc = dlc;
+
+ rfcomm_dev_modem_status(dlc, dlc->remote_v24_sig);
+
rfcomm_dlc_unlock(dlc);
/* It's safe to call __module_get() here because socket already
@@ -537,11 +560,16 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
struct rfcomm_dev *dev = dlc->owner;
struct tty_struct *tty;
- if (!dev || !(tty = dev->tty)) {
+ if (!dev) {
kfree_skb(skb);
return;
}
+ if (!(tty = dev->tty) || !skb_queue_empty(&dev->pending)) {
+ skb_queue_tail(&dev->pending, skb);
+ return;
+ }
+
BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
tty_insert_flip_string(tty, skb->data, skb->len);
@@ -625,6 +653,30 @@ static void rfcomm_tty_wakeup(unsigned long arg)
#endif
}
+static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev)
+{
+ struct tty_struct *tty = dev->tty;
+ struct sk_buff *skb;
+ int inserted = 0;
+
+ if (!tty)
+ return;
+
+ BT_DBG("dev %p tty %p", dev, tty);
+
+ rfcomm_dlc_lock(dev->dlc);
+
+ while ((skb = skb_dequeue(&dev->pending))) {
+ inserted += tty_insert_flip_string(tty, skb->data, skb->len);
+ kfree_skb(skb);
+ }
+
+ rfcomm_dlc_unlock(dev->dlc);
+
+ if (inserted > 0)
+ tty_flip_buffer_push(tty);
+}
+
static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
{
DECLARE_WAITQUEUE(wait, current);
@@ -689,6 +741,10 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
if (err == 0)
device_move(dev->tty_dev, rfcomm_get_device(dev));
+ rfcomm_tty_copy_pending(dev);
+
+ rfcomm_dlc_unthrottle(dev->dlc);
+
return err;
}
@@ -1121,6 +1177,7 @@ int rfcomm_init_ttys(void)
rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
rfcomm_tty_driver->init_termios = tty_std_termios;
rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
if (tty_register_driver(rfcomm_tty_driver)) {
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index b0d487e..8cda498 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -53,7 +53,9 @@
#define BT_DBG(D...)
#endif
-#define VERSION "0.5"
+#define VERSION "0.6"
+
+static int disable_esco = 0;
static const struct proto_ops sco_sock_ops;
@@ -193,7 +195,10 @@ static int sco_connect(struct sock *sk)
err = -ENOMEM;
- type = lmp_esco_capable(hdev) ? ESCO_LINK : SCO_LINK;
+ if (lmp_esco_capable(hdev) && !disable_esco)
+ type = ESCO_LINK;
+ else
+ type = SCO_LINK;
hcon = hci_connect(hdev, type, dst);
if (!hcon)
@@ -921,7 +926,7 @@ static const struct proto_ops sco_sock_ops = {
.sendmsg = sco_sock_sendmsg,
.recvmsg = bt_sock_recvmsg,
.poll = bt_sock_poll,
- .ioctl = sock_no_ioctl,
+ .ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.shutdown = sock_no_shutdown,
@@ -994,6 +999,9 @@ static void __exit sco_exit(void)
module_init(sco_init);
module_exit(sco_exit);
+module_param(disable_esco, bool, 0644);
+MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation");
+
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
MODULE_VERSION(VERSION);
OpenPOWER on IntegriCloud