Commit 4e8402a3 authored by Marcel Holtmann's avatar Marcel Holtmann Committed by David S. Miller

[Bluetooth] Retrieve L2CAP features mask on connection setup

The Bluetooth 1.2 specification introduced a specific features mask
value to interoperate with newer versions of the specification. So far
this piece of information was never needed, but future extensions will
rely on it. This patch adds a generic way to retrieve this information
only once per connection setup.
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 861d6882
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
#define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MTU 672
#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
#define L2CAP_CONN_TIMEOUT (HZ * 40) #define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */
/* L2CAP socket address */ /* L2CAP socket address */
struct sockaddr_l2 { struct sockaddr_l2 {
...@@ -160,7 +161,6 @@ struct l2cap_disconn_rsp { ...@@ -160,7 +161,6 @@ struct l2cap_disconn_rsp {
struct l2cap_info_req { struct l2cap_info_req {
__le16 type; __le16 type;
__u8 data[0];
} __attribute__ ((packed)); } __attribute__ ((packed));
struct l2cap_info_rsp { struct l2cap_info_rsp {
...@@ -192,6 +192,13 @@ struct l2cap_conn { ...@@ -192,6 +192,13 @@ struct l2cap_conn {
unsigned int mtu; unsigned int mtu;
__u32 feat_mask;
__u8 info_state;
__u8 info_ident;
struct timer_list info_timer;
spinlock_t lock; spinlock_t lock;
struct sk_buff *rx_skb; struct sk_buff *rx_skb;
...@@ -202,6 +209,9 @@ struct l2cap_conn { ...@@ -202,6 +209,9 @@ struct l2cap_conn {
struct l2cap_chan_list chan_list; struct l2cap_chan_list chan_list;
}; };
#define L2CAP_INFO_CL_MTU_REQ_SENT 0x01
#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x02
/* ----- L2CAP channel and socket info ----- */ /* ----- L2CAP channel and socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
......
...@@ -258,7 +258,119 @@ static void l2cap_chan_del(struct sock *sk, int err) ...@@ -258,7 +258,119 @@ static void l2cap_chan_del(struct sock *sk, int err)
sk->sk_state_change(sk); sk->sk_state_change(sk);
} }
static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
{
u8 id;
/* Get next available identificator.
* 1 - 128 are used by kernel.
* 129 - 199 are reserved.
* 200 - 254 are used by utilities like l2ping, etc.
*/
spin_lock_bh(&conn->lock);
if (++conn->tx_ident > 128)
conn->tx_ident = 1;
id = conn->tx_ident;
spin_unlock_bh(&conn->lock);
return id;
}
static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data)
{
struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
BT_DBG("code 0x%2.2x", code);
if (!skb)
return -ENOMEM;
return hci_send_acl(conn->hcon, skb, 0);
}
/* ---- L2CAP connections ---- */ /* ---- L2CAP connections ---- */
static void l2cap_conn_start(struct l2cap_conn *conn)
{
struct l2cap_chan_list *l = &conn->chan_list;
struct sock *sk;
BT_DBG("conn %p", conn);
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
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,
L2CAP_CONN_REQ, sizeof(req), &req);
}
bh_unlock_sock(sk);
}
read_unlock(&l->lock);
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
BT_DBG("conn %p", conn);
if (conn->chan_list.head || !hlist_empty(&l2cap_sk_list.head)) {
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);
}
}
/* Notify sockets that we cannot guaranty reliability anymore */
static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
{
struct l2cap_chan_list *l = &conn->chan_list;
struct sock *sk;
BT_DBG("conn %p", conn);
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
sk->sk_err = err;
}
read_unlock(&l->lock);
}
static void l2cap_info_timeout(unsigned long arg)
{
struct l2cap_conn *conn = (void *) arg;
conn->info_ident = 0;
l2cap_conn_start(conn);
}
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
{ {
struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_conn *conn = hcon->l2cap_data;
...@@ -279,6 +391,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) ...@@ -279,6 +391,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->src = &hcon->hdev->bdaddr; conn->src = &hcon->hdev->bdaddr;
conn->dst = &hcon->dst; conn->dst = &hcon->dst;
conn->feat_mask = 0;
init_timer(&conn->info_timer);
conn->info_timer.function = l2cap_info_timeout;
conn->info_timer.data = (unsigned long) conn;
spin_lock_init(&conn->lock); spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock); rwlock_init(&conn->chan_list.lock);
...@@ -318,40 +436,6 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, stru ...@@ -318,40 +436,6 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, stru
write_unlock_bh(&l->lock); write_unlock_bh(&l->lock);
} }
static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
{
u8 id;
/* Get next available identificator.
* 1 - 128 are used by kernel.
* 129 - 199 are reserved.
* 200 - 254 are used by utilities like l2ping, etc.
*/
spin_lock_bh(&conn->lock);
if (++conn->tx_ident > 128)
conn->tx_ident = 1;
id = conn->tx_ident;
spin_unlock_bh(&conn->lock);
return id;
}
static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data)
{
struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
BT_DBG("code 0x%2.2x", code);
if (!skb)
return -ENOMEM;
return hci_send_acl(conn->hcon, skb, 0);
}
/* ---- Socket interface ---- */ /* ---- Socket interface ---- */
static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src)
{ {
...@@ -529,7 +613,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p ...@@ -529,7 +613,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p
INIT_LIST_HEAD(&bt_sk(sk)->accept_q); INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
sk->sk_destruct = l2cap_sock_destruct; sk->sk_destruct = l2cap_sock_destruct;
sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT);
sock_reset_flag(sk, SOCK_ZAPPED); sock_reset_flag(sk, SOCK_ZAPPED);
...@@ -649,6 +733,11 @@ static int l2cap_do_connect(struct sock *sk) ...@@ -649,6 +733,11 @@ static int l2cap_do_connect(struct sock *sk)
l2cap_sock_set_timer(sk, sk->sk_sndtimeo); l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
if (hcon->state == BT_CONNECTED) { 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) { if (sk->sk_type == SOCK_SEQPACKET) {
struct l2cap_conn_req req; struct l2cap_conn_req req;
l2cap_pi(sk)->ident = l2cap_get_ident(conn); l2cap_pi(sk)->ident = l2cap_get_ident(conn);
...@@ -1083,52 +1172,6 @@ static int l2cap_sock_release(struct socket *sock) ...@@ -1083,52 +1172,6 @@ static int l2cap_sock_release(struct socket *sock)
return err; return err;
} }
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan_list *l = &conn->chan_list;
struct sock *sk;
BT_DBG("conn %p", conn);
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
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, L2CAP_CONN_REQ, sizeof(req), &req);
}
bh_unlock_sock(sk);
}
read_unlock(&l->lock);
}
/* Notify sockets that we cannot guaranty reliability anymore */
static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
{
struct l2cap_chan_list *l = &conn->chan_list;
struct sock *sk;
BT_DBG("conn %p", conn);
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
sk->sk_err = err;
}
read_unlock(&l->lock);
}
static void l2cap_chan_ready(struct sock *sk) static void l2cap_chan_ready(struct sock *sk)
{ {
struct sock *parent = bt_sk(sk)->parent; struct sock *parent = bt_sk(sk)->parent;
...@@ -1399,6 +1442,23 @@ static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 fla ...@@ -1399,6 +1442,23 @@ static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 fla
return ptr - data; return ptr - data;
} }
static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_cmd_rej *rej = (struct l2cap_cmd_rej *) data;
if (rej->reason != 0x0000)
return 0;
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
cmd->ident == conn->info_ident) {
conn->info_ident = 0;
del_timer(&conn->info_timer);
l2cap_conn_start(conn);
}
return 0;
}
static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
{ {
struct l2cap_chan_list *list = &conn->chan_list; struct l2cap_chan_list *list = &conn->chan_list;
...@@ -1739,6 +1799,15 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm ...@@ -1739,6 +1799,15 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
BT_DBG("type 0x%4.4x result 0x%2.2x", type, result); BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
conn->info_ident = 0;
del_timer(&conn->info_timer);
if (type == L2CAP_IT_FEAT_MASK)
conn->feat_mask = __le32_to_cpu(get_unaligned((__le32 *) rsp->data));
l2cap_conn_start(conn);
return 0; return 0;
} }
...@@ -1768,7 +1837,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk ...@@ -1768,7 +1837,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk
switch (cmd.code) { switch (cmd.code) {
case L2CAP_COMMAND_REJ: case L2CAP_COMMAND_REJ:
/* FIXME: We should process this */ l2cap_command_rej(conn, &cmd, data);
break; break;
case L2CAP_CONN_REQ: case L2CAP_CONN_REQ:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment