diff options
author | David S. Miller <davem@davemloft.net> | 2008-07-19 00:30:39 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-07-19 00:30:39 -0700 |
commit | 407d819cf0fd54c6fc1138a509225696aecafd15 (patch) | |
tree | b653a5c8c09f7c316f5f98947be262e27a4ca33a /net | |
parent | 7abbcd6a4c8d6179121f2915a761b1133bf1cd99 (diff) | |
parent | b1235d79611e78a07629b4cbe53291c9cffd1834 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/holtmann/bluetooth-2.6
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/af_bluetooth.c | 53 | ||||
-rw-r--r-- | net/bluetooth/bnep/core.c | 5 | ||||
-rw-r--r-- | net/bluetooth/hci_conn.c | 96 | ||||
-rw-r--r-- | net/bluetooth/hci_core.c | 42 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 512 | ||||
-rw-r--r-- | net/bluetooth/hci_sock.c | 18 | ||||
-rw-r--r-- | net/bluetooth/hci_sysfs.c | 31 | ||||
-rw-r--r-- | net/bluetooth/hidp/core.c | 10 | ||||
-rw-r--r-- | net/bluetooth/l2cap.c | 337 | ||||
-rw-r--r-- | net/bluetooth/rfcomm/core.c | 92 | ||||
-rw-r--r-- | net/bluetooth/rfcomm/sock.c | 23 | ||||
-rw-r--r-- | net/bluetooth/rfcomm/tty.c | 59 | ||||
-rw-r--r-- | net/bluetooth/sco.c | 14 |
13 files changed, 1031 insertions, 261 deletions
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index d366423c839..4e59df5f8e0 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 1d98a1b80da..021172c0e66 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 f8880261da0..ca8d05245ca 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 aec6929f5c1..f5b21cb9369 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 6aef8f24e58..0e3db289f4b 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 747fabd735d..d62579b6795 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 84360c117d4..844ca5f1b2d 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 519cdb920f9..96434d774c8 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 6e180d25550..c1239852834 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 b4fb84e398e..6cfc7ba611b 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 c9054487670..8a972b6ba85 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 be84f4fc147..5d163571d3f 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 b0d487e2db2..8cda4987486 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); |