public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
From: "Martin Röhricht" <ml@felicis.org>
To: bluez-devel@lists.sourceforge.net
Subject: Re: [Bluez-devel] L2CAP Flow Control
Date: Mon, 13 Feb 2006 19:10:27 +0100	[thread overview]
Message-ID: <1139854227.16687.36.camel@localhost.localdomain> (raw)
In-Reply-To: <1139482797.2349.32.camel@localhost.localdomain>

[-- Attachment #1: Type: text/plain, Size: 3045 bytes --]

Status update to L2CAP Flow Control work.

In the function l2cap_parse_conf_req() I changed the result array to
have a size of 128 instead of 5, because we have to respond to each
request no matter which possible option is used. Currently the standard
defines 4 options (0x01 to 0x04) but in case the remote device sends a
request for option 0x42 we have to create an unknown option response
with the value of 0x42 as a parameter (that's why we need to keep track
of all incoming options). 128 reflects the maximum possible options
because the option type field is 8 bits long (256 in decimal) where the
most significant bit determines if the option is to be recognized as a
hint. The result value for each option is stored in result[OPTION] and
the overall result value of the request is stored in result[0] (nice
effect, that no option will ever have 0x00 as its option type value).
For a reasonable initialization we chose to use 255:
	memset(result, 255, sizeof(result));
as this can never be used in a real request and 0 would have stated
SUCCESS for every single of the 127 possible options.
The default case for us is now an unknown option, therefore the use of
L2CAP_CONF_QOS and L2CAP_CONF_FLUSH_TO are currently commented out and
can be extended whenever somebody works on these parts.

Then I introduced a new function called »l2cap_check_conf_opt()« for the
purpose of validity checks against all values that have been written to
the conf_* variables. This function will return a result code for this
one option that it has to check. MTU currently lacks completion but RFC
is already in progress. In case one value is not acceptable, we need to
generate a response with the values that would have been accepted if
sent in the original request. So in case we will end with
L2CAP_CONF_UNACCEPT we do not want to write or have written any value to
our outgoing channel configuration. That's why we will use the conf_*
variables as long as we do not know that the request is perfectly valid
(SUCCESS). So this function makes some basic checks even if I do not
know where I can retrieve acceptable values for the Retransmission
Timeout, the Monitor Timeout or the MPS/PDU size. That's definitely an
issue that needs to be solved. Perhaps somebody with more insight comes
with an idea?

Once the check function returned and we parsed each option of the
request, we need to differentiate between the major result code
result[0] and create corresponding response packets. For the case
everything is fine (L2CAP_CONF_SUCCESS) we write the conf_* values to
the corresponding o* variables.

So the next step is to rewrite the functions that create a response
packet as we would run into trouble again within the function
l2cap_add_conf_opt() on Big Endian machines. It becomes a bit more fuzzy
to keep track of different options that must be handled within one
request/response in a generic way that offers us enough power to create
the responses individualy.

That's it for today.

Martin

[-- Attachment #2: patch-2.6.14-mr --]
[-- Type: text/plain, Size: 11219 bytes --]

--- linux-2.6.14-mh2/net/bluetooth/l2cap.c	2006-01-20 12:05:17.000000000 +0100
+++ linux-2.6.14-mr/net/bluetooth/l2cap.c	2006-02-10 16:04:49.000000000 +0100
@@ -1236,75 +1236,194 @@
 	return NULL;
 }
 
-static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val)
+static int l2cap_check_conf_opt(struct sock *sk, int option)
 {
-	struct l2cap_conf_opt *opt = *ptr;
-	int len;
-
-	len = L2CAP_CONF_OPT_SIZE + opt->len;
-	*ptr += len;
+        int result = L2CAP_CONF_SUCCESS;
+        struct l2cap_pinfo *pi = l2cap_pi(sk);
 
-	*type = opt->type;
-	*olen = opt->len;
+        switch (option) {
+                case L2CAP_CONF_MTU:
+                        break;
+                case L2CAP_CONF_FLUSH_TO:
+                        break;
+                case L2CAP_CONF_QOS:
+                        break;
+                case L2CAP_CONF_RFC:
+                        if (pi->conf_mode == L2CAP_MODE_BASIC) {
+                                pi->omode = L2CAP_MODE_BASIC;
+                                break;
+                        }
+                        /* Retransmission Mode not supported yet */
+                        if (pi->conf_mode != L2CAP_MODE_FLOW) {
+                                pi->conf_mode = L2CAP_MODE_FLOW;
+                                result = L2CAP_CONF_UNACCEPT;
+                        }
+                        if (pi->conf_txw < 1 || pi->conf_txw > 32) {
+                                pi->conf_txw = 32;
+                                result = L2CAP_CONF_UNACCEPT;
+                        }
+                        if (pi->conf_maxt < 1) {
+                                pi->conf_maxt = 400;
+                                result = L2CAP_CONF_UNACCEPT;
+                        }
+                        if (pi->conf_ret_to < 100) {
+                                pi->conf_ret_to = 100;
+                                result = L2CAP_CONF_UNACCEPT;
+                        }
+                        if (pi->conf_mon_to < 100) {
+                                pi->conf_mon_to = 100;
+                                result = L2CAP_CONF_UNACCEPT;
+                        }
+                        /* MPS/PDU check */
+                        break;
+        }
 
-	switch (opt->len) {
-	case 1:
-		*val = *((__u8 *) opt->val);
-		break;
-
-	case 2:
-		*val = __le16_to_cpu(*((__le16 *) opt->val));
-		break;
-
-	case 4:
-		*val = __le32_to_cpu(*((__le32 *) opt->val));
-		break;
-
-	default:
-		*val = (unsigned long) opt->val;
-		break;
-	}
-
-	BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val);
-	return len;
+        return result;
 }
 
 static inline void l2cap_parse_conf_req(struct sock *sk, void *data, u16 len)
 {
-	int type, hint, olen; 
-	unsigned long val;
-	void *ptr = data;
-
-	BT_DBG("sk %p len %d", sk, len);
-
-	while (len >= L2CAP_CONF_OPT_SIZE) {
-		len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);
-		u8 req_len = *(u8*)(ptr + 1);
-
-		hint  = type & 0x80;
-		type &= 0x7f;
-
-		BT_DBG("Type: %d len: %d", type, req_len);
-		switch (type) {
-		case L2CAP_CONF_MTU:
-			l2cap_pi(sk)->conf_mtu = val;
-			break;
-
-		case L2CAP_CONF_FLUSH_TO:
-			l2cap_pi(sk)->flush_to = val;
-			break;
-
-		case L2CAP_CONF_QOS:
-			break;
-
-		default:
-			if (hint)
-				break;
-
-			/* FIXME: Reject unknown option */
-			break;
+        struct l2cap_pinfo *cap = l2cap_pi(sk);
+        int type, hint;
+        u8 *ptr = data;
+        u8 options = 0; /* bit flag that indicates which options are set */
+        u8 result[128]; /* collected result codes */
+        unsigned int i;
+
+        BT_DBG("sk %p len %d", sk, len);
+
+        for (i = 0; i < len; ++i)
+                printk("%02X ", ptr[i]);
+        printk("\n");
+
+        memset(result, 255, sizeof(result));
+        result[0] = L2CAP_CONF_SUCCESS;
+
+        while (len >= L2CAP_CONF_OPT_SIZE) {
+                u8 req_len = *(u8*)(ptr + 1);
+
+                /* Bail out if req length > packet length */
+                if (len < req_len) return;
+
+                len -= req_len + 2;
+                type = *(u8*)(ptr + 0);
+
+                hint  = type & 0x80;
+                type &= 0x7f;
+
+                BT_DBG("Type: %d len: %d", type, req_len);
+
+                switch (type) {
+                case 0:
+                        result[0] = L2CAP_CONF_REJ;
+                        break;
+                case L2CAP_CONF_MTU:
+                        if (HAS_BIT(options, L2CAP_CONF_MTU)) break;
+                        if (req_len != 2) {
+                                result[0] = L2CAP_CONF_REJ;
+                                break;
+                        }
+                        cap->conf_mtu = __le16_to_cpup((__le16*)(ptr + 2));
+                        SET_BIT(options, L2CAP_CONF_MTU);
+			result[L2CAP_CONF_MTU] = l2cap_check_conf_opt(sk, L2CAP_CONF_MTU);
+                        if (result[L2CAP_CONF_MTU] == L2CAP_CONF_UNACCEPT)
+                                result[0] = L2CAP_CONF_UNACCEPT;
+                        break;
+
+                /* Flush TO not supported yet */
+#if 0
+                case L2CAP_CONF_FLUSH_TO:
+                        if (HAS_BIT(options, L2CAP_CONF_FLUSH_TO)) break;
+                        if (req_len != 2) {
+                                result[0] = L2CAP_CONF_REJ;
+                                break;
+                        }
+                        cap->flush_to = __le16_to_cpup((__le16*)(ptr + 2));
+                        result[0] = L2CAP_CONF_UNKNOWN;
+                        result[L2CAP_FLUSH_TO] = L2CAP_CONF_UNKNOWN;
+                        SET_BIT(options, L2CAP_CONF_FLUSH_TO);
+                        break;
+#endif
+
+                /* QoS not supported yet */
+#if 0
+                case L2CAP_CONF_QOS:
+                        if (HAS_BIT(options, L2CAP_CONF_QOS)) break;
+                        if (req_len != 22) {
+                                result[0] = L2CAP_CONF_REJ;
+                                break;
+                        }
+                        result[0] = L2CAP_CONF_UNKNOWN;
+                        result[L2CAP_CONF_QOS] = L2CAP_CONF_UNKNOWN;
+                        SET_BIT(options, L2CAP_CONF_QOS);
+                        break;
+#endif
+
+                case L2CAP_CONF_RFC: {
+                        if (HAS_BIT(options, L2CAP_CONF_RFC)) break;
+                        if (req_len != 9) {
+                                result[0] = L2CAP_CONF_REJ;
+                                break;
+                        }
+                        cap->conf_mode = *(__u8*)(ptr + 2);
+                        cap->conf_txw  = *(__u8*)(ptr + 3);
+                        cap->conf_maxt = *(__u8*)(ptr + 4);
+                        cap->conf_ret_to = __le16_to_cpup((__le16*)(ptr + 5));
+                        cap->conf_mon_to = __le16_to_cpup((__le16*)(ptr + 7));
+                        cap->conf_mps    = __le16_to_cpup((__le16*)(ptr + 9));
+                        SET_BIT(options, L2CAP_CONF_RFC);
+			result[L2CAP_CONF_RFC] = l2cap_check_conf_opt(sk, L2CAP_CONF_RFC);
+                        if (result[L2CAP_CONF_RFC] == L2CAP_CONF_UNACCEPT)
+                                result[0] = L2CAP_CONF_UNACCEPT;
+                        break;
+                }
+
+                default:
+			result[0] = L2CAP_CONF_UNKNOWN;
+                        result[type] = L2CAP_CONF_UNKNOWN;
+                        if (hint)
+                                break;
+
+                        break;
+                }
+
+                /* Go to start of (possible) next request */
+                ptr += req_len;
+        }
+        if (result[0] == L2CAP_CONF_UNKNOWN) {
+                for (i = 1; i < sizeof(result); ++i) {
+                        if (result[i] == L2CAP_CONF_UNKNOWN) {
+                                /* create response packet with option type i */
+                        }
+                }
+        }
+        if (result[0] == L2CAP_CONF_UNACCEPT) {
+                for (i = 1; i < sizeof(result); ++i) {
+                        if (result[i] == L2CAP_CONF_UNACCEPT) {
+                                /* create response packet with option type i */
+                        }
 		}
-	}
+        }
+        if (result[0] == L2CAP_CONF_SUCCESS) {
+                for (i = 1; i < sizeof(result); ++i) {
+                        if (result[i] == L2CAP_CONF_SUCCESS) {
+                                switch (i) {
+                                        case L2CAP_CONF_MTU:
+                                                cap->omtu = cap->conf_mtu;
+                                                break;
+                                        case L2CAP_CONF_RFC:
+                                                cap->omode = cap->conf_mode;
+                                                cap->otxw = cap->conf_txw;
+                                                cap->omaxt = cap->conf_maxt;
+                                                cap->oret_to = cap->conf_ret_to;
+                                                cap->omon_to = cap->conf_mon_to;
+                                                cap->omps = cap->conf_mps;
+                                                break;
+                                }
+                                /* create response packet for option type i */
+                        }
+                }
+        }
 }
 
 static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
--- linux-2.6.14-mh2/include/net/bluetooth/l2cap.h	2005-12-23 21:14:59.000000000 +0100
+++ linux-2.6.14-mr/include/net/bluetooth/l2cap.h	2006-02-09 10:54:57.000000000 +0100
@@ -28,6 +28,8 @@
 /* L2CAP defaults */
 #define L2CAP_DEFAULT_MTU	672
 #define L2CAP_DEFAULT_FLUSH_TO	0xFFFF
+#define L2CAP_MIN_TXW		1
+#define L2CAP_MAX_TXW		32
 
 #define L2CAP_CONN_TIMEOUT	(HZ * 40)
 
@@ -47,6 +49,10 @@
 	__u8  mode;
 };
 
+#define L2CAP_MODE_BASIC	0x00
+#define L2CAP_MDOE_RET		0x01
+#define L2CAP_MODE_FLOW		0x02
+
 #define L2CAP_CONNINFO	0x02
 struct l2cap_conninfo {
 	__u16 hci_handle;
@@ -129,8 +135,10 @@
 	__u8       data[0];
 } __attribute__ ((packed));
 
-#define L2CAP_CONF_SUCCESS	0x00
-#define L2CAP_CONF_UNACCEPT	0x01
+#define L2CAP_CONF_SUCCESS	0x0000
+#define L2CAP_CONF_UNACCEPT	0x0001
+#define L2CAP_CONF_REJ		0x0002
+#define L2CAP_CONF_UNKNOWN	0x0003
 
 struct l2cap_conf_opt {
 	__u8       type;
@@ -144,7 +152,8 @@
 #define L2CAP_CONF_QOS		0x03
 #define L2CAP_CONF_RFC		0x04
 
-#define L2CAP_CONF_MAX_SIZE	22
+#define SET_BIT(x, v) ((x) |= 1 << (v))
+#define HAS_BIT(x, v) (((x) & (1 << (v))) != 0)
 
 struct l2cap_disconn_req {
 	__le16     dcid;
@@ -220,6 +229,31 @@
 	__u8		conf_retry;
 	__u16		conf_mtu;
 
+	/* Configuration Request RFC Options */
+	__u8		conf_mode;
+	__u8		conf_txw;
+	__u8		conf_maxt;
+	__u16		conf_ret_to;
+	__u16		conf_mon_to;
+	__u16		conf_mps;
+	
+	/* incoming RFC Options */
+	__u8		imode;
+	__u8		itxw;
+	__u8		imaxt;
+	__u16		iret_to;
+	__u16		imon_to;
+	__u16		imps;
+
+	/* outgoing RFC Options */
+	__u8		omode;
+	__u8		otxw;
+	__u8		omaxt;
+	__u16		oret_to;
+	__u16		omon_to;
+	__u16		omps;
+
+	
 	__u8		ident;
 
 	struct l2cap_conn	*conn;

  reply	other threads:[~2006-02-13 18:10 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-02-09 10:59 [Bluez-devel] L2CAP Flow Control Martin Röhricht
2006-02-13 18:10 ` Martin Röhricht [this message]
  -- strict thread matches above, loose matches on Subject: below --
2006-09-15 20:30 Martin Röhricht
2006-09-15 21:12 ` Marcel Holtmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1139854227.16687.36.camel@localhost.localdomain \
    --to=ml@felicis.org \
    --cc=bluez-devel@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox