|
@@ -55,7 +55,7 @@
|
|
|
#define BT_DBG(D...)
|
|
|
#endif
|
|
|
|
|
|
-#define VERSION "2.9"
|
|
|
+#define VERSION "2.10"
|
|
|
|
|
|
static u32 l2cap_feat_mask = 0x0000;
|
|
|
|
|
@@ -253,6 +253,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 +302,34 @@ 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) {
|
|
|
+ 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 +344,35 @@ 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) {
|
|
|
+ bh_unlock_sock(sk);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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_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 +383,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 */
|
|
@@ -729,22 +796,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:
|
|
@@ -1477,7 +1533,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 +1582,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 +1611,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 +1734,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:
|