Bluetooth: Pause RFCOMM TX when encryption drops
authorMarcel Holtmann <marcel@holtmann.org>
Fri, 16 Jan 2009 07:17:51 +0000 (08:17 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 27 Feb 2009 05:14:33 +0000 (06:14 +0100)
A role switch with devices following the Bluetooth pre-2.1 standards
or without Encryption Pause and Resume support is not possible if
encryption is enabled. Most newer headsets require the role switch,
but also require that the connection is encrypted.

For connections with a high security mode setting, the link will be
immediately dropped. When the connection uses medium security mode
setting, then a grace period is introduced where the TX is halted and
the remote device gets a change to re-enable encryption after the
role switch. If not re-enabled the link will be dropped.

Based on initial work by Ville Tervo <ville.tervo@nokia.com>

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/rfcomm.h
net/bluetooth/rfcomm/core.c

index bda68d833dddfe91c1949c503c57bfcffd27c3af..80072611d26a44820304593292398f0e3d67d7eb 100644 (file)
@@ -204,10 +204,11 @@ struct rfcomm_dlc {
 #define RFCOMM_TX_THROTTLED 1
 #define RFCOMM_TIMED_OUT    2
 #define RFCOMM_MSC_PENDING  3
-#define RFCOMM_AUTH_PENDING 4
-#define RFCOMM_AUTH_ACCEPT  5
-#define RFCOMM_AUTH_REJECT  6
-#define RFCOMM_DEFER_SETUP  7
+#define RFCOMM_SEC_PENDING  4
+#define RFCOMM_AUTH_PENDING 5
+#define RFCOMM_AUTH_ACCEPT  6
+#define RFCOMM_AUTH_REJECT  7
+#define RFCOMM_DEFER_SETUP  8
 
 /* Scheduling flags and events */
 #define RFCOMM_SCHED_STATE  0
index db83f92d274c5a302137c8466bb95ed705105661..dafaee91cdfbf86d2e205842ac9d68923e2f7564 100644 (file)
@@ -1979,12 +1979,23 @@ static void rfcomm_security_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 (!status && encrypt == 0x00 &&
-                               d->sec_level == BT_SECURITY_HIGH &&
-                                       (d->state == BT_CONNECTED ||
-                                               d->state == BT_CONFIG)) {
-                       __rfcomm_dlc_close(d, ECONNREFUSED);
-                       continue;
+               if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
+                       rfcomm_dlc_clear_timer(d);
+                       if (status || encrypt == 0x00) {
+                               __rfcomm_dlc_close(d, ECONNREFUSED);
+                               continue;
+                       }
+               }
+
+               if (d->state == BT_CONNECTED && !status && encrypt == 0x00) {
+                       if (d->sec_level == BT_SECURITY_MEDIUM) {
+                               set_bit(RFCOMM_SEC_PENDING, &d->flags);
+                               rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+                               continue;
+                       } else if (d->sec_level == BT_SECURITY_HIGH) {
+                               __rfcomm_dlc_close(d, ECONNREFUSED);
+                               continue;
+                       }
                }
 
                if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))