2009-07-04 18:06:24

by Gustavo F. Padovan

[permalink] [raw]
Subject: [PATCH] Bluetooth: add support to configure ERTM and Streaming mode

Add code to config_req and config_rsp to configure ERTM and Streaming
mode. If the remote devices specifies ERTM or Streaming Mode, we propose
the same mode received. Otherwise we propose ERTM or Basic Mode.
And if we are a state 2 device the remote should propose the same mode
that we sent, if not, the channel is disconnected.

Signed-off-by: Gustavo F. Padovan <[email protected]>
---
include/net/bluetooth/l2cap.h | 28 +++-
net/bluetooth/l2cap.c | 268 ++++++++++++++++++++++++++++++++++++-----
2 files changed, 258 insertions(+), 38 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 06b072f..6fc7698 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -27,8 +27,9 @@

/* L2CAP defaults */
#define L2CAP_DEFAULT_MTU 672
+#define L2CAP_DEFAULT_MIN_MTU 48
#define L2CAP_DEFAULT_FLUSH_TO 0xffff
-#define L2CAP_DEFAULT_RX_WINDOW 1
+#define L2CAP_DEFAULT_TX_WINDOW 1
#define L2CAP_DEFAULT_MAX_RECEIVE 1
#define L2CAP_DEFAULT_RETRANS_TO 300 /* 300 milliseconds */
#define L2CAP_DEFAULT_MONITOR_TO 1000 /* 1 second */
@@ -272,6 +273,9 @@ struct l2cap_pinfo {
__u16 omtu;
__u16 flush_to;
__u8 mode;
+ __u8 num_conf_req;
+ __u8 num_conf_rsp;
+
__u8 fcs;
__u8 sec_level;
__u8 role_switch;
@@ -280,10 +284,15 @@ struct l2cap_pinfo {
__u8 conf_req[64];
__u8 conf_len;
__u8 conf_state;
- __u8 conf_retry;

__u8 ident;

+ __u8 remote_tx_win;
+ __u8 remote_max_tx;
+ __u16 retrans_timeout;
+ __u16 monitor_timeout;
+ __u16 max_pdu_size;
+
__le16 sport;

struct l2cap_conn *conn;
@@ -291,12 +300,17 @@ struct l2cap_pinfo {
struct sock *prev_c;
};

-#define L2CAP_CONF_REQ_SENT 0x01
-#define L2CAP_CONF_INPUT_DONE 0x02
-#define L2CAP_CONF_OUTPUT_DONE 0x04
-#define L2CAP_CONF_CONNECT_PEND 0x80
+#define L2CAP_CONF_REQ_SENT 0x01
+#define L2CAP_CONF_INPUT_DONE 0x02
+#define L2CAP_CONF_OUTPUT_DONE 0x04
+#define L2CAP_CONF_MTU_DONE 0x08
+#define L2CAP_CONF_MODE_DONE 0x10
+#define L2CAP_CONF_CONNECT_PEND 0x20
+#define L2CAP_CONF_STATE2_DEVICE 0x80
+
+#define L2CAP_CONF_MAX_CONF_REQ 2
+#define L2CAP_CONF_MAX_CONF_RSP 2

-#define L2CAP_CONF_MAX_RETRIES 2

void l2cap_load(void);

diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 7ce1a24..da8a76d 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -966,6 +966,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_ERTM:
+ case L2CAP_MODE_STREAMING:
if (enable_ertm)
break;
/* fall through */
@@ -1029,6 +1030,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_ERTM:
+ case L2CAP_MODE_STREAMING:
if (enable_ertm)
break;
/* fall through */
@@ -1739,15 +1741,65 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
*ptr += L2CAP_CONF_OPT_SIZE + len;
}

+static int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
+{
+ u32 local_feat_mask = l2cap_feat_mask;
+ if (enable_ertm)
+ local_feat_mask |= L2CAP_FEAT_ERTM;
+
+ switch (mode) {
+ case L2CAP_MODE_ERTM:
+ return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask;
+ case L2CAP_MODE_STREAMING:
+ return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask;
+ default:
+ return 0x00;
+ }
+}
+
+static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
+{
+ switch (mode) {
+ case L2CAP_MODE_STREAMING:
+ case L2CAP_MODE_ERTM:
+ if (l2cap_mode_supported(mode, remote_feat_mask))
+ return mode;
+ /* fall through */
+ default:
+ return L2CAP_MODE_BASIC;
+ }
+}
+
static int l2cap_build_conf_req(struct sock *sk, void *data)
{
struct l2cap_pinfo *pi = l2cap_pi(sk);
struct l2cap_conf_req *req = data;
- struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
+ struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_ERTM };
void *ptr = req->data;

BT_DBG("sk %p", sk);

+ if (pi->num_conf_req || pi->num_conf_rsp)
+ goto done;
+
+ switch (pi->mode) {
+ case L2CAP_MODE_STREAMING:
+ case L2CAP_MODE_ERTM:
+ pi->conf_state |= L2CAP_CONF_STATE2_DEVICE;
+ if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) {
+ struct l2cap_disconn_req req;
+ req.dcid = cpu_to_le16(pi->dcid);
+ req.scid = cpu_to_le16(pi->scid);
+ l2cap_send_cmd(pi->conn, l2cap_get_ident(pi->conn),
+ L2CAP_DISCONN_REQ, sizeof(req), &req);
+ }
+ break;
+ default:
+ pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask);
+ break;
+ }
+
+done:
switch (pi->mode) {
case L2CAP_MODE_BASIC:
if (pi->imtu != L2CAP_DEFAULT_MTU)
@@ -1756,14 +1808,26 @@ static int l2cap_build_conf_req(struct sock *sk, void *data)

case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM;
- rfc.txwin_size = L2CAP_DEFAULT_RX_WINDOW;
+ rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
rfc.max_transmit = L2CAP_DEFAULT_MAX_RECEIVE;
- rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
- rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+ rfc.retrans_timeout = 0;
+ rfc.monitor_timeout = 0;
rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU);

- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ rfc.mode = L2CAP_MODE_STREAMING;
+ rfc.txwin_size = 0;
+ rfc.max_transmit = 0;
+ rfc.retrans_timeout = 0;
+ rfc.monitor_timeout = 0;
+ rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU);
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
break;
}

@@ -1825,30 +1889,83 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
}
}

+ if (pi->num_conf_rsp || pi->num_conf_req)
+ goto done;
+
+ switch (pi->mode) {
+ case L2CAP_MODE_STREAMING:
+ case L2CAP_MODE_ERTM:
+ pi->conf_state |= L2CAP_CONF_STATE2_DEVICE;
+ if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask))
+ return -ECONNREFUSED;
+ break;
+ default:
+ pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask);
+ break;
+ }
+
+done:
+ if (pi->mode != rfc.mode) {
+ result = L2CAP_CONF_UNACCEPT;
+ rfc.mode = pi->mode;
+
+ if (pi->num_conf_rsp == 1)
+ return -ECONNREFUSED;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+ }
+
+
if (result == L2CAP_CONF_SUCCESS) {
/* Configure output options and let the other side know
* which ones we don't like. */

- if (rfc.mode == L2CAP_MODE_BASIC) {
- if (mtu < pi->omtu)
- result = L2CAP_CONF_UNACCEPT;
- else {
- pi->omtu = mtu;
- pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;
- }
+ if (mtu < L2CAP_DEFAULT_MIN_MTU)
+ result = L2CAP_CONF_UNACCEPT;
+ else {
+ pi->omtu = mtu;
+ pi->conf_state |= L2CAP_CONF_MTU_DONE;
+ }
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);

- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
- } else {
+ switch (rfc.mode) {
+ case L2CAP_MODE_BASIC:
+ pi->fcs = L2CAP_FCS_NONE;
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ break;
+
+ case L2CAP_MODE_ERTM:
+ pi->remote_tx_win = rfc.txwin_size;
+ pi->remote_max_tx = rfc.max_transmit;
+ pi->max_pdu_size = rfc.max_pdu_size;
+
+ rfc.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ rfc.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ pi->remote_tx_win = rfc.txwin_size;
+ pi->max_pdu_size = rfc.max_pdu_size;
+
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ break;
+
+ default:
result = L2CAP_CONF_UNACCEPT;

memset(&rfc, 0, sizeof(rfc));
- rfc.mode = L2CAP_MODE_BASIC;
-
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ rfc.mode = pi->mode;
}
- }

+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+
+ if (result == L2CAP_CONF_SUCCESS)
+ pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+ }
rsp->scid = cpu_to_le16(pi->dcid);
rsp->result = cpu_to_le16(result);
rsp->flags = cpu_to_le16(0x0000);
@@ -1856,6 +1973,73 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
return ptr - data;
}

+static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, u16 *result)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct l2cap_conf_req *req = data;
+ void *ptr = req->data;
+ int type, olen;
+ unsigned long val;
+ struct l2cap_conf_rfc rfc;
+
+ BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data);
+
+ while (len >= L2CAP_CONF_OPT_SIZE) {
+ len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
+
+ switch (type) {
+ case L2CAP_CONF_MTU:
+ if (val < L2CAP_DEFAULT_MIN_MTU) {
+ *result = L2CAP_CONF_UNACCEPT;
+ pi->omtu = L2CAP_DEFAULT_MIN_MTU;
+ } else
+ pi->omtu = val;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
+ break;
+
+ case L2CAP_CONF_FLUSH_TO:
+ pi->flush_to = val;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2,
+ pi->flush_to);
+ break;
+
+ case L2CAP_CONF_RFC:
+ if (olen == sizeof(rfc))
+ memcpy(&rfc, (void *)val, olen);
+
+ if (rfc.mode != pi->mode &&
+ (pi->conf_state & L2CAP_CONF_STATE2_DEVICE))
+ return -ECONNREFUSED;
+
+ pi->mode = rfc.mode;
+ pi->fcs = 0;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+ break;
+ }
+ }
+
+ if (*result == L2CAP_CONF_SUCCESS) {
+ switch (rfc.mode) {
+ case L2CAP_MODE_ERTM:
+ pi->remote_tx_win = rfc.txwin_size;
+ pi->retrans_timeout = rfc.retrans_timeout;
+ pi->monitor_timeout = rfc.monitor_timeout;
+ pi->max_pdu_size = le16_to_cpu(rfc.max_pdu_size);
+ break;
+ case L2CAP_MODE_STREAMING:
+ pi->max_pdu_size = le16_to_cpu(rfc.max_pdu_size);
+ break;
+ }
+ }
+
+ req->dcid = cpu_to_le16(pi->dcid);
+ req->flags = cpu_to_le16(0x0000);
+
+ return ptr - data;
+}
+
static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags)
{
struct l2cap_conf_rsp *rsp = data;
@@ -2042,6 +2226,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd

l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(sk, req), req);
+ l2cap_pi(sk)->num_conf_req++;
break;

case L2CAP_CR_PEND:
@@ -2100,10 +2285,17 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr

/* Complete config. */
len = l2cap_parse_conf_req(sk, rsp);
- if (len < 0)
+ if (len < 0) {
+ struct l2cap_disconn_req req;
+ req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ l2cap_send_cmd(conn, l2cap_get_ident(conn),
+ L2CAP_DISCONN_REQ, sizeof(req), &req);
goto unlock;
+ }

l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
+ l2cap_pi(sk)->num_conf_rsp++;

/* Reset config buffer. */
l2cap_pi(sk)->conf_len = 0;
@@ -2121,6 +2313,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
u8 buf[64];
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(sk, buf), buf);
+ l2cap_pi(sk)->num_conf_req++;
}

unlock:
@@ -2150,16 +2343,29 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
break;

case L2CAP_CONF_UNACCEPT:
- if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {
- char req[128];
- /* It does not make sense to adjust L2CAP parameters
- * that are currently defined in the spec. We simply
- * resend config request that we sent earlier. It is
- * stupid, but it helps qualification testing which
- * expects at least some response from us. */
- l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(sk, req), req);
- goto done;
+ if (l2cap_pi(sk)->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
+ int len = cmd->len - sizeof(*rsp);
+ char req[64];
+
+ /* throw out any old conf requests we stored */
+ result = L2CAP_CONF_SUCCESS;
+ len = l2cap_parse_conf_rsp(sk, rsp->data, len, req,
+ &result);
+ if (len < 0) {
+ struct l2cap_disconn_req req;
+ req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ l2cap_send_cmd(conn, l2cap_get_ident(conn),
+ L2CAP_DISCONN_REQ, sizeof(req), &req);
+ goto done;
+ }
+
+ l2cap_send_cmd(conn, l2cap_get_ident(conn),
+ L2CAP_CONF_REQ, len, req);
+ l2cap_pi(sk)->num_conf_req++;
+ if (result != L2CAP_CONF_SUCCESS)
+ goto done;
+ break;
}

default:
--
1.6.0.6


2009-07-04 08:28:53

by Gustavo F. Padovan

[permalink] [raw]
Subject: Re: [PATCH] Bluetooth: add support to configure ERTM and Streaming mode

Hi Marcel,

On Sat, Jul 4, 2009 at 4:41 AM, Gustavo F.
Padovan<[email protected]> wrote:
> Add code to config_req and config_rsp to configure ERTM and Streaming
> mode. If the remote devices specifies ERTM or Streaming Mode, we propose
> the same mode received. Otherwise we propose ERTM or Basic Mode.
> And if we are a state 2 device the remote should propose the same mode
> that we sent, if not, the channel is disconnected.
>
> Signed-off-by: Gustavo F. Padovan <[email protected]>
> ---
> =A0include/net/bluetooth/l2cap.h | =A0 28 +++-
> =A0net/bluetooth/l2cap.c =A0 =A0 =A0 =A0 | =A0284 +++++++++++++++++++++++=
++++++++++++------
> =A02 files changed, 268 insertions(+), 44 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.=
h
> index 06b072f..6fc7698 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -27,8 +27,9 @@
>
> =A0/* L2CAP defaults */
> =A0#define L2CAP_DEFAULT_MTU =A0 =A0 =A0 =A0 =A0 =A0 =A0672
> +#define L2CAP_DEFAULT_MIN_MTU =A0 =A0 =A0 =A0 =A048
> =A0#define L2CAP_DEFAULT_FLUSH_TO =A0 =A0 =A0 =A0 0xffff
> -#define L2CAP_DEFAULT_RX_WINDOW =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A01
> +#define L2CAP_DEFAULT_TX_WINDOW =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A01
> =A0#define L2CAP_DEFAULT_MAX_RECEIVE =A0 =A0 =A01
> =A0#define L2CAP_DEFAULT_RETRANS_TO =A0 =A0 =A0 300 =A0 =A0/* 300 millise=
conds */
> =A0#define L2CAP_DEFAULT_MONITOR_TO =A0 =A0 =A0 1000 =A0 /* 1 second */
> @@ -272,6 +273,9 @@ struct l2cap_pinfo {
> =A0 =A0 =A0 =A0__u16 =A0 =A0 =A0 =A0 =A0 omtu;
> =A0 =A0 =A0 =A0__u16 =A0 =A0 =A0 =A0 =A0 flush_to;
> =A0 =A0 =A0 =A0__u8 =A0 =A0 =A0 =A0 =A0 =A0mode;
> + =A0 =A0 =A0 __u8 =A0 =A0 =A0 =A0 =A0 =A0num_conf_req;
> + =A0 =A0 =A0 __u8 =A0 =A0 =A0 =A0 =A0 =A0num_conf_rsp;
> +
> =A0 =A0 =A0 =A0__u8 =A0 =A0 =A0 =A0 =A0 =A0fcs;
> =A0 =A0 =A0 =A0__u8 =A0 =A0 =A0 =A0 =A0 =A0sec_level;
> =A0 =A0 =A0 =A0__u8 =A0 =A0 =A0 =A0 =A0 =A0role_switch;
> @@ -280,10 +284,15 @@ struct l2cap_pinfo {
> =A0 =A0 =A0 =A0__u8 =A0 =A0 =A0 =A0 =A0 =A0conf_req[64];
> =A0 =A0 =A0 =A0__u8 =A0 =A0 =A0 =A0 =A0 =A0conf_len;
> =A0 =A0 =A0 =A0__u8 =A0 =A0 =A0 =A0 =A0 =A0conf_state;
> - =A0 =A0 =A0 __u8 =A0 =A0 =A0 =A0 =A0 =A0conf_retry;
>
> =A0 =A0 =A0 =A0__u8 =A0 =A0 =A0 =A0 =A0 =A0ident;
>
> + =A0 =A0 =A0 __u8 =A0 =A0 =A0 =A0 =A0 =A0remote_tx_win;
> + =A0 =A0 =A0 __u8 =A0 =A0 =A0 =A0 =A0 =A0remote_max_tx;
> + =A0 =A0 =A0 __u16 =A0 =A0 =A0 =A0 =A0 retrans_timeout;
> + =A0 =A0 =A0 __u16 =A0 =A0 =A0 =A0 =A0 monitor_timeout;
> + =A0 =A0 =A0 __u16 =A0 =A0 =A0 =A0 =A0 max_pdu_size;
> +
> =A0 =A0 =A0 =A0__le16 =A0 =A0 =A0 =A0 =A0sport;
>
> =A0 =A0 =A0 =A0struct l2cap_conn =A0 =A0 =A0 *conn;
> @@ -291,12 +300,17 @@ struct l2cap_pinfo {
> =A0 =A0 =A0 =A0struct sock =A0 =A0 =A0 =A0 =A0 =A0 *prev_c;
> =A0};
>
> -#define L2CAP_CONF_REQ_SENT =A0 =A00x01
> -#define L2CAP_CONF_INPUT_DONE =A00x02
> -#define L2CAP_CONF_OUTPUT_DONE 0x04
> -#define L2CAP_CONF_CONNECT_PEND =A0 =A0 =A0 =A00x80
> +#define L2CAP_CONF_REQ_SENT =A0 =A0 =A0 0x01
> +#define L2CAP_CONF_INPUT_DONE =A0 =A0 0x02
> +#define L2CAP_CONF_OUTPUT_DONE =A0 =A00x04
> +#define L2CAP_CONF_MTU_DONE =A0 =A0 =A0 0x08
> +#define L2CAP_CONF_MODE_DONE =A0 =A0 =A00x10
> +#define L2CAP_CONF_CONNECT_PEND =A0 0x20
> +#define L2CAP_CONF_STATE2_DEVICE =A00x80
> +
> +#define L2CAP_CONF_MAX_CONF_REQ 2
> +#define L2CAP_CONF_MAX_CONF_RSP 2
>
> -#define L2CAP_CONF_MAX_RETRIES 2
>
> =A0void l2cap_load(void);
>
> diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> index 7ce1a24..a78277a 100644
> --- a/net/bluetooth/l2cap.c
> +++ b/net/bluetooth/l2cap.c
> @@ -966,6 +966,7 @@ static int l2cap_sock_connect(struct socket *sock, st=
ruct sockaddr *addr, int al
> =A0 =A0 =A0 =A0case L2CAP_MODE_BASIC:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> =A0 =A0 =A0 =A0case L2CAP_MODE_ERTM:
> + =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (enable_ertm)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* fall through */
> @@ -1029,6 +1030,7 @@ static int l2cap_sock_listen(struct socket *sock, i=
nt backlog)
> =A0 =A0 =A0 =A0case L2CAP_MODE_BASIC:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> =A0 =A0 =A0 =A0case L2CAP_MODE_ERTM:
> + =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (enable_ertm)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* fall through */
> @@ -1739,15 +1741,65 @@ static void l2cap_add_conf_opt(void **ptr, u8 typ=
e, u8 len, unsigned long val)
> =A0 =A0 =A0 =A0*ptr +=3D L2CAP_CONF_OPT_SIZE + len;
> =A0}
>
> -static int l2cap_build_conf_req(struct sock *sk, void *data)
> +static int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
> +{
> + =A0 =A0 =A0 u32 local_feat_mask =3D l2cap_feat_mask;
> + =A0 =A0 =A0 if (enable_ertm)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 local_feat_mask |=3D L2CAP_FEAT_ERTM;
> +
> + =A0 =A0 =A0 switch (mode) {
> + =A0 =A0 =A0 case L2CAP_MODE_ERTM:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return L2CAP_FEAT_ERTM & feat_mask & local_=
feat_mask;
> + =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return L2CAP_FEAT_STREAMING & feat_mask & l=
ocal_feat_mask;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0x00;
> + =A0 =A0 =A0 }
> +}
> +
> +static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
> +{
> + =A0 =A0 =A0 switch (mode) {
> + =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> + =A0 =A0 =A0 case L2CAP_MODE_ERTM:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (l2cap_mode_supported(mode, remote_feat_=
mask))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return mode;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* fall through */
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return L2CAP_MODE_BASIC;
> + =A0 =A0 =A0 }
> +}
> +
> +static int l2cap_build_conf_req(struct sock *sk, struct l2cap_conn *conn=
, void *data)
> =A0{
> =A0 =A0 =A0 =A0struct l2cap_pinfo *pi =3D l2cap_pi(sk);
> =A0 =A0 =A0 =A0struct l2cap_conf_req *req =3D data;
> - =A0 =A0 =A0 struct l2cap_conf_rfc rfc =3D { .mode =3D L2CAP_MODE_BASIC =
};
> + =A0 =A0 =A0 struct l2cap_conf_rfc rfc =3D { .mode =3D L2CAP_MODE_ERTM }=
;
> =A0 =A0 =A0 =A0void *ptr =3D req->data;
>
> =A0 =A0 =A0 =A0BT_DBG("sk %p", sk);
>
> + =A0 =A0 =A0 if (pi->num_conf_req || pi->num_conf_rsp)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto done;
> +
> + =A0 =A0 =A0 switch (pi->mode) {
> + =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> + =A0 =A0 =A0 case L2CAP_MODE_ERTM:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->conf_state |=3D L2CAP_CONF_STATE2_DEVIC=
E;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!l2cap_mode_supported(pi->mode, conn->f=
eat_mask)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct l2cap_disconn_req re=
q;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 req.dcid =3D cpu_to_le16(pi=
->dcid);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 req.scid =3D cpu_to_le16(pi=
->scid);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_send_cmd(conn, l2cap_=
get_ident(conn),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 L2CAP_DISCO=
NN_REQ, sizeof(req), &req);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->mode =3D l2cap_select_mode(rfc.mode, co=
nn->feat_mask);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +
> +done:
> =A0 =A0 =A0 =A0switch (pi->mode) {
> =A0 =A0 =A0 =A0case L2CAP_MODE_BASIC:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (pi->imtu !=3D L2CAP_DEFAULT_MTU)
> @@ -1756,14 +1808,26 @@ static int l2cap_build_conf_req(struct sock *sk, =
void *data)
>
> =A0 =A0 =A0 =A0case L2CAP_MODE_ERTM:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rfc.mode =A0 =A0 =A0 =A0 =A0 =A0=3D L2CAP_=
MODE_ERTM;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.txwin_size =A0 =A0 =A0=3D L2CAP_DEFAULT=
_RX_WINDOW;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.txwin_size =A0 =A0 =A0=3D L2CAP_DEFAULT=
_TX_WINDOW;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rfc.max_transmit =A0 =A0=3D L2CAP_DEFAULT_=
MAX_RECEIVE;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.retrans_timeout =3D cpu_to_le16(L2CAP_D=
EFAULT_RETRANS_TO);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.monitor_timeout =3D cpu_to_le16(L2CAP_D=
EFAULT_MONITOR_TO);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.retrans_timeout =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.monitor_timeout =3D 0;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rfc.max_pdu_size =A0 =A0=3D cpu_to_le16(L2=
CAP_DEFAULT_MAX_RX_APDU);
>
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 sizeof(rfc), (unsigned long) &rfc);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, si=
zeof(rfc),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (unsigned l=
ong) &rfc);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.mode =A0 =A0 =A0 =A0 =A0 =A0=3D L2CAP_M=
ODE_STREAMING;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.txwin_size =A0 =A0 =A0=3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.max_transmit =A0 =A0=3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.retrans_timeout =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.monitor_timeout =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.max_pdu_size =A0 =A0=3D cpu_to_le16(L2C=
AP_DEFAULT_MAX_RX_APDU);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, si=
zeof(rfc),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (unsigned l=
ong) &rfc);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> =A0 =A0 =A0 =A0}
>
> @@ -1777,7 +1841,7 @@ static int l2cap_build_conf_req(struct sock *sk, vo=
id *data)
> =A0 =A0 =A0 =A0return ptr - data;
> =A0}
>
> -static int l2cap_parse_conf_req(struct sock *sk, void *data)
> +static int l2cap_parse_conf_req(struct sock *sk, struct l2cap_conn *conn=
, void *data, int *disconnect)


I noted that l2cap_parse_conf_req and l2cap_parse_conf_rsp never
return negative values ( ptr - data is always positive), so I think we
can add a return -Eerror(ECONNREFUSED?) to raise disconnect request.
That way we don't need the extra parameter disconnect.


> =A0{
> =A0 =A0 =A0 =A0struct l2cap_pinfo *pi =3D l2cap_pi(sk);
> =A0 =A0 =A0 =A0struct l2cap_conf_rsp *rsp =3D data;
> @@ -1825,30 +1889,83 @@ static int l2cap_parse_conf_req(struct sock *sk, =
void *data)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> =A0 =A0 =A0 =A0}
>
> + =A0 =A0 =A0 if (pi->num_conf_rsp || pi->num_conf_req)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto done;
> +
> + =A0 =A0 =A0 switch (pi->mode) {
> + =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> + =A0 =A0 =A0 case L2CAP_MODE_ERTM:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->conf_state |=3D L2CAP_CONF_STATE2_DEVIC=
E;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!l2cap_mode_supported(pi->mode, conn->f=
eat_mask))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *disconnect =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->mode =3D l2cap_select_mode(rfc.mode, co=
nn->feat_mask);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +
> +done:
> + =A0 =A0 =A0 if (pi->mode !=3D rfc.mode) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 result =3D L2CAP_CONF_UNACCEPT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.mode =3D pi->mode;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (pi->num_conf_rsp =3D=3D 1)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *disconnect =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2=
CAP_CONF_RFC,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sizeof(rfc)=
, (unsigned long) &rfc);
> + =A0 =A0 =A0 }
> +
> +
> =A0 =A0 =A0 =A0if (result =3D=3D L2CAP_CONF_SUCCESS) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Configure output options and let the ot=
her side know
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 * which ones we don't like. */
>
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (rfc.mode =3D=3D L2CAP_MODE_BASIC) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (mtu < pi->omtu)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 result =3D =
L2CAP_CONF_UNACCEPT;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->omtu =
=3D mtu;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->conf_st=
ate |=3D L2CAP_CONF_OUTPUT_DONE;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (mtu < L2CAP_DEFAULT_MIN_MTU)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 result =3D L2CAP_CONF_UNACC=
EPT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->omtu =3D mtu;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->conf_state |=3D L2CAP_C=
ONF_MTU_DONE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2,=
pi->omtu);
>
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2=
CAP_CONF_MTU, 2, pi->omtu);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 switch (rfc.mode) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case L2CAP_MODE_BASIC:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->fcs =3D L2CAP_FCS_NONE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->conf_state |=3D L2CAP_C=
ONF_MODE_DONE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case L2CAP_MODE_ERTM:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->remote_tx_win =3D rfc.t=
xwin_size;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->remote_max_tx =3D rfc.m=
ax_transmit;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->max_pdu_size =3D rfc.ma=
x_pdu_size;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.retrans_timeout =3D L2C=
AP_DEFAULT_RETRANS_TO;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.monitor_timeout =3D L2C=
AP_DEFAULT_MONITOR_TO;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->conf_state |=3D L2CAP_C=
ONF_MODE_DONE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->remote_tx_win =3D rfc.t=
xwin_size;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->max_pdu_size =3D rfc.ma=
x_pdu_size;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->conf_state |=3D L2CAP_C=
ONF_MODE_DONE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 default:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0result =3D L2CAP_CONF_UNAC=
CEPT;
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0memset(&rfc, 0, sizeof(rfc=
));
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.mode =3D L2CAP_MODE_BAS=
IC;
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2=
CAP_CONF_RFC,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 sizeof(rfc), (unsigned long) &rfc);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rfc.mode =3D pi->mode;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> - =A0 =A0 =A0 }
>
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sizeof(rfc)=
, (unsigned long) &rfc);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (result =3D=3D L2CAP_CONF_SUCCESS)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->conf_state |=3D L2CAP_C=
ONF_OUTPUT_DONE;
> + =A0 =A0 =A0 }
> =A0 =A0 =A0 =A0rsp->scid =A0 =3D cpu_to_le16(pi->dcid);
> =A0 =A0 =A0 =A0rsp->result =3D cpu_to_le16(result);
> =A0 =A0 =A0 =A0rsp->flags =A0=3D cpu_to_le16(0x0000);
> @@ -1856,6 +1973,73 @@ static int l2cap_parse_conf_req(struct sock *sk, v=
oid *data)
> =A0 =A0 =A0 =A0return ptr - data;
> =A0}
>
> +static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, voi=
d *data, u16 *result, int *disconnect)
> +{
> + =A0 =A0 =A0 struct l2cap_pinfo *pi =3D l2cap_pi(sk);
> + =A0 =A0 =A0 struct l2cap_conf_req *req =3D data;
> + =A0 =A0 =A0 void *ptr =3D req->data;
> + =A0 =A0 =A0 int type, olen;
> + =A0 =A0 =A0 unsigned long val;
> + =A0 =A0 =A0 struct l2cap_conf_rfc rfc;
> +
> + =A0 =A0 =A0 BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data)=
;
> +
> + =A0 =A0 =A0 while (len >=3D L2CAP_CONF_OPT_SIZE) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 len -=3D l2cap_get_conf_opt(&rsp, &type, &o=
len, &val);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 switch (type) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case L2CAP_CONF_MTU:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (val < L2CAP_DEFAULT_MIN=
_MTU) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *result =3D=
L2CAP_CONF_UNACCEPT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->omtu =
=3D L2CAP_DEFAULT_MIN_MTU;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->omtu =
=3D val;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2=
CAP_CONF_MTU, 2, pi->omtu);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case L2CAP_CONF_FLUSH_TO:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->flush_to =3D val;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2=
CAP_CONF_FLUSH_TO, 2,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 pi->flush_to);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case L2CAP_CONF_RFC:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (olen =3D=3D sizeof(rfc)=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 memcpy(&rfc=
, (void *)val, olen);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (rfc.mode !=3D pi->mode =
&&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (pi->conf_s=
tate & L2CAP_CONF_STATE2_DEVICE))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *disconnect=
=3D 1;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->mode =3D rfc.mode;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->fcs =3D 0;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_add_conf_opt(&ptr, L2=
CAP_CONF_RFC,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sizeof(rfc)=
, (unsigned long) &rfc);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (*result =3D=3D L2CAP_CONF_SUCCESS) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 switch (rfc.mode) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case L2CAP_MODE_ERTM:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->remote_tx_win =A0 =3D r=
fc.txwin_size;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->retrans_timeout =3D rfc=
.retrans_timeout;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->monitor_timeout =3D rfc=
.monitor_timeout;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->max_pdu_size =A0 =A0=3D=
le16_to_cpu(rfc.max_pdu_size);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case L2CAP_MODE_STREAMING:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pi->max_pdu_size =A0 =A0=3D=
le16_to_cpu(rfc.max_pdu_size);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 req->dcid =A0 =3D cpu_to_le16(pi->dcid);
> + =A0 =A0 =A0 req->flags =A0=3D cpu_to_le16(0x0000);
> +
> + =A0 =A0 =A0 return ptr - data;
> +}
> +
> =A0static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 resul=
t, u16 flags)
> =A0{
> =A0 =A0 =A0 =A0struct l2cap_conf_rsp *rsp =3D data;
> @@ -2041,7 +2225,8 @@ static inline int l2cap_connect_rsp(struct l2cap_co=
nn *conn, struct l2cap_cmd_hd
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0l2cap_pi(sk)->conf_state &=3D ~L2CAP_CONF_=
CONNECT_PEND;
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0l2cap_send_cmd(conn, l2cap_get_ident(conn)=
, L2CAP_CONF_REQ,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 l2cap_build_conf_req(sk, req), req);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 l2cap_build_conf_req(sk, conn, req), req);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_pi(sk)->num_conf_req++;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
>
> =A0 =A0 =A0 =A0case L2CAP_CR_PEND:
> @@ -2063,7 +2248,7 @@ static inline int l2cap_config_req(struct l2cap_con=
n *conn, struct l2cap_cmd_hdr
> =A0 =A0 =A0 =A0u16 dcid, flags;
> =A0 =A0 =A0 =A0u8 rsp[64];
> =A0 =A0 =A0 =A0struct sock *sk;
> - =A0 =A0 =A0 int len;
> + =A0 =A0 =A0 int len, disconn =3D 0;
>
> =A0 =A0 =A0 =A0dcid =A0=3D __le16_to_cpu(req->dcid);
> =A0 =A0 =A0 =A0flags =3D __le16_to_cpu(req->flags);
> @@ -2099,11 +2284,21 @@ static inline int l2cap_config_req(struct l2cap_c=
onn *conn, struct l2cap_cmd_hdr
> =A0 =A0 =A0 =A0}
>
> =A0 =A0 =A0 =A0/* Complete config. */
> - =A0 =A0 =A0 len =3D l2cap_parse_conf_req(sk, rsp);
> + =A0 =A0 =A0 len =3D l2cap_parse_conf_req(sk, conn, rsp, &disconn);
> =A0 =A0 =A0 =A0if (len < 0)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto unlock;
>
> - =A0 =A0 =A0 l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
> + =A0 =A0 =A0 if (disconn) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct l2cap_disconn_req req;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 req.dcid =3D cpu_to_le16(l2cap_pi(sk)->dcid=
);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 req.scid =3D cpu_to_le16(l2cap_pi(sk)->scid=
);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_send_cmd(conn, l2cap_get_ident(conn),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 L2CAP_DISCO=
NN_REQ, sizeof(req), &req);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto unlock;
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF=
_RSP, len, rsp);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_pi(sk)->num_conf_rsp++;
> + =A0 =A0 =A0 }
>
> =A0 =A0 =A0 =A0/* Reset config buffer. */
> =A0 =A0 =A0 =A0l2cap_pi(sk)->conf_len =3D 0;
> @@ -2120,7 +2315,8 @@ static inline int l2cap_config_req(struct l2cap_con=
n *conn, struct l2cap_cmd_hdr
> =A0 =A0 =A0 =A0if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0u8 buf[64];
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0l2cap_send_cmd(conn, l2cap_get_ident(conn)=
, L2CAP_CONF_REQ,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 l2cap_build_conf_req(sk, buf), buf);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 l2cap_build_conf_req(sk, conn, buf), buf);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_pi(sk)->num_conf_req++;
> =A0 =A0 =A0 =A0}
>
> =A0unlock:
> @@ -2133,6 +2329,7 @@ static inline int l2cap_config_rsp(struct l2cap_con=
n *conn, struct l2cap_cmd_hdr
> =A0 =A0 =A0 =A0struct l2cap_conf_rsp *rsp =3D (struct l2cap_conf_rsp *)da=
ta;
> =A0 =A0 =A0 =A0u16 scid, flags, result;
> =A0 =A0 =A0 =A0struct sock *sk;
> + =A0 =A0 =A0 int disconn =3D 0;
>
> =A0 =A0 =A0 =A0scid =A0 =3D __le16_to_cpu(rsp->scid);
> =A0 =A0 =A0 =A0flags =A0=3D __le16_to_cpu(rsp->flags);
> @@ -2150,16 +2347,29 @@ static inline int l2cap_config_rsp(struct l2cap_c=
onn *conn, struct l2cap_cmd_hdr
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
>
> =A0 =A0 =A0 =A0case L2CAP_CONF_UNACCEPT:
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF=
_MAX_RETRIES) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 char req[128];
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* It does not make sense t=
o adjust L2CAP parameters
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* that are currently def=
ined in the spec. We simply
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* resend config request =
that we sent earlier. It is
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* stupid, but it helps q=
ualification testing which
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* expects at least some =
response from us. */
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_send_cmd(conn, l2cap_=
get_ident(conn), L2CAP_CONF_REQ,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 l2cap_build_conf_req(sk, req), req);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto done;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (l2cap_pi(sk)->num_conf_rsp <=3D L2CAP_C=
ONF_MAX_CONF_RSP) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int len =3D cmd->len - size=
of(*rsp);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 char req[64];
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* throw out any old conf r=
equests we stored */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 result =3D L2CAP_CONF_SUCCE=
SS;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 len =3D l2cap_parse_conf_rs=
p(sk, rsp->data, len, req,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 &result, &disconn);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (disconn) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct l2ca=
p_disconn_req req;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 req.dcid =
=3D cpu_to_le16(l2cap_pi(sk)->dcid);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 req.scid =
=3D cpu_to_le16(l2cap_pi(sk)->scid);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_send_=
cmd(conn, l2cap_get_ident(conn),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 L2CAP_DISCONN_REQ, sizeof(req), &req);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto done;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_send_cmd(conn, l2cap_=
get_ident(conn),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 L2CAP_CONF_REQ, len, req);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 l2cap_pi(sk)->num_conf_req+=
+;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (result !=3D L2CAP_CONF_=
SUCCESS)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto done;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
>
> =A0 =A0 =A0 =A0default:
> --
> 1.6.0.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth=
" in
> the body of a message to [email protected]
> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html
>



--=20
Gustavo F. Padovan
http://padovan.org