Bluetooth: Directly close dlc for not yet started RFCOMM session
[deliverable/linux.git] / net / bluetooth / rfcomm / core.c
index facd8a79c0383eb8898fca8dc905c87ed623202a..b727cd97c5a28253a63cdb53b7f659602d8df4f9 100644 (file)
@@ -216,6 +216,7 @@ static int rfcomm_check_security(struct rfcomm_dlc *d)
 
        switch (d->sec_level) {
        case BT_SECURITY_HIGH:
+       case BT_SECURITY_FIPS:
                auth_type = HCI_AT_GENERAL_BONDING_MITM;
                break;
        case BT_SECURITY_MEDIUM:
@@ -359,6 +360,11 @@ static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
        return NULL;
 }
 
+static int rfcomm_check_channel(u8 channel)
+{
+       return channel < 1 || channel > 30;
+}
+
 static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
 {
        struct rfcomm_session *s;
@@ -368,7 +374,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
        BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d",
               d, d->state, src, dst, channel);
 
-       if (channel < 1 || channel > 30)
+       if (rfcomm_check_channel(channel))
                return -EINVAL;
 
        if (d->state != BT_OPEN && d->state != BT_CLOSED)
@@ -425,6 +431,20 @@ int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 chann
        return r;
 }
 
+static void __rfcomm_dlc_disconn(struct rfcomm_dlc *d)
+{
+       struct rfcomm_session *s = d->session;
+
+       d->state = BT_DISCONN;
+       if (skb_queue_empty(&d->tx_queue)) {
+               rfcomm_send_disc(s, d->dlci);
+               rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
+       } else {
+               rfcomm_queue_disc(d);
+               rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
+       }
+}
+
 static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
 {
        struct rfcomm_session *s = d->session;
@@ -437,32 +457,29 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
        switch (d->state) {
        case BT_CONNECT:
        case BT_CONFIG:
+       case BT_OPEN:
+       case BT_CONNECT2:
                if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
                        set_bit(RFCOMM_AUTH_REJECT, &d->flags);
                        rfcomm_schedule();
-                       break;
+                       return 0;
                }
-               /* Fall through */
+       }
 
+       switch (d->state) {
+       case BT_CONNECT:
        case BT_CONNECTED:
-               d->state = BT_DISCONN;
-               if (skb_queue_empty(&d->tx_queue)) {
-                       rfcomm_send_disc(s, d->dlci);
-                       rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
-               } else {
-                       rfcomm_queue_disc(d);
-                       rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
-               }
+               __rfcomm_dlc_disconn(d);
                break;
 
-       case BT_OPEN:
-       case BT_CONNECT2:
-               if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
-                       set_bit(RFCOMM_AUTH_REJECT, &d->flags);
-                       rfcomm_schedule();
+       case BT_CONFIG:
+               if (s->state != BT_BOUND) {
+                       __rfcomm_dlc_disconn(d);
                        break;
                }
-               /* Fall through */
+               /* if closing a dlc in a session that hasn't been started,
+                * just close and unlink the dlc
+                */
 
        default:
                rfcomm_dlc_clear_timer(d);
@@ -513,6 +530,25 @@ no_session:
        return r;
 }
 
+struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel)
+{
+       struct rfcomm_session *s;
+       struct rfcomm_dlc *dlc = NULL;
+       u8 dlci;
+
+       if (rfcomm_check_channel(channel))
+               return ERR_PTR(-EINVAL);
+
+       rfcomm_lock();
+       s = rfcomm_session_get(src, dst);
+       if (s) {
+               dlci = __dlci(!s->initiator, channel);
+               dlc = rfcomm_dlc_get(s, dlci);
+       }
+       rfcomm_unlock();
+       return dlc;
+}
+
 int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
 {
        int len = skb->len;
@@ -1943,12 +1979,11 @@ static void rfcomm_process_sessions(void)
                        continue;
                }
 
-               if (s->state == BT_LISTEN) {
+               switch (s->state) {
+               case BT_LISTEN:
                        rfcomm_accept_connection(s);
                        continue;
-               }
 
-               switch (s->state) {
                case BT_BOUND:
                        s = rfcomm_check_connection(s);
                        break;
@@ -2085,7 +2120,8 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
                                set_bit(RFCOMM_SEC_PENDING, &d->flags);
                                rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
                                continue;
-                       } else if (d->sec_level == BT_SECURITY_HIGH) {
+                       } else if (d->sec_level == BT_SECURITY_HIGH ||
+                                  d->sec_level == BT_SECURITY_FIPS) {
                                set_bit(RFCOMM_ENC_DROP, &d->flags);
                                continue;
                        }
This page took 0.031562 seconds and 5 git commands to generate.