OSDN Git Service

Add flow control for PAN.
authorSharvil Nanavati <sharvil@google.com>
Mon, 7 Apr 2014 20:52:37 +0000 (13:52 -0700)
committerMatthew Xie <mattx@google.com>
Tue, 6 May 2014 08:14:23 +0000 (01:14 -0700)
This change reads from the TAP fd only when btpan_cb.flow == 1.
The value of this flag changes based on L2CAP congestion events.

http://b/13246629

Change-Id: Ic28bf65737dbd74c41b68198fceee58e3bff08d4

Conflicts:
btif/src/btif_pan.c

btif/co/bta_pan_co.c
btif/include/btif_pan_internal.h
btif/src/btif_pan.c

index 1dcce8e..b0f2945 100644 (file)
@@ -33,6 +33,7 @@
 #include <hardware/bluetooth.h>
 #include <hardware/bt_pan.h>
 #include "btif_pan_internal.h"
+#include "btif_sock_thread.h"
 #include "bd.h"
 #include <string.h>
 #include "btif_util.h"
@@ -98,6 +99,7 @@ void bta_pan_co_open(UINT16 handle, UINT8 app_id, tBTA_PAN_ROLE local_role,
         }
         if(btpan_cb.tap_fd >= 0)
         {
+            btpan_cb.flow = 1;
             conn->state = PAN_STATE_OPEN;
             bta_pan_ci_rx_ready(handle);
         }
@@ -129,14 +131,10 @@ void bta_pan_co_close(UINT16 handle, UINT8 app_id)
         conn->state = PAN_STATE_CLOSE;
         btpan_cb.open_count--;
 
-        if(btpan_cb.open_count == 0)
+        if(btpan_cb.open_count == 0 && btpan_cb.tap_fd != -1)
         {
-            destroy_tap_read_thread();
-            if(btpan_cb.tap_fd != -1)
-            {
-                btpan_tap_close(btpan_cb.tap_fd);
-                btpan_cb.tap_fd = -1;
-            }
+            btpan_tap_close(btpan_cb.tap_fd);
+            btpan_cb.tap_fd = -1;
         }
     }
 }
@@ -171,7 +169,7 @@ void bta_pan_co_tx_path(UINT16 handle, UINT8 app_id)
     BTIF_TRACE_API2("bta_pan_co_tx_path, handle:%d, app_id:%d", handle, app_id);
 
     btpan_conn_t* conn = btpan_find_conn_handle(handle);
-    if(conn && conn->state != PAN_STATE_OPEN)
+    if(!conn || conn->state != PAN_STATE_OPEN)
     {
         BTIF_TRACE_ERROR2("bta_pan_co_tx_path: cannot find pan connction or conn"
             "is not opened, conn:%p, conn->state:%d", conn, conn->state);
@@ -304,6 +302,10 @@ void bta_pan_co_rx_flow(UINT16 handle, UINT8 app_id, BOOLEAN enable)
     UNUSED(enable);
 
     BTIF_TRACE_API1("bta_pan_co_rx_flow, enabled:%d, not used", enable);
+    btpan_conn_t* conn = btpan_find_conn_handle(handle);
+    if(!conn || conn->state != PAN_STATE_OPEN)
+        return;
+    btpan_set_flow_control(enable);
 }
 
 /*******************************************************************************
index d697ac8..64bab99 100644 (file)
@@ -79,7 +79,10 @@ typedef struct
     int tap_fd;
     int enabled;
     int open_count;
+    int flow; // 1: outbound data flow on; 0: outbound data flow off
     btpan_conn_t conns[MAX_PAN_CONNS];
+    int congest_packet_size;
+    unsigned char congest_packet[1600]; //max ethernet packet size
 } btpan_cb_t;
 
 
@@ -91,6 +94,7 @@ extern btpan_cb_t btpan_cb;
 btpan_conn_t *btpan_new_conn(int handle, const BD_ADDR addr, int local_role, int peer_role);
 btpan_conn_t *btpan_find_conn_addr(const BD_ADDR addr);
 btpan_conn_t *btpan_find_conn_handle(UINT16 handle);
+void btpan_set_flow_control(BOOLEAN enable);
 int btpan_get_connected_count(void);
 int btpan_tap_open(void);
 void create_tap_read_thread(int tap_fd);
index 5119493..b0e284f 100644 (file)
@@ -26,6 +26,7 @@
  ***********************************************************************************/
 #include <hardware/bluetooth.h>
 #include <hardware/bt_pan.h>
+#include <assert.h>
 #include <signal.h>
 #include <ctype.h>
 #include <sys/select.h>
 #include "btif_sock_thread.h"
 #include "btif_sock_util.h"
 #include "btif_pan_internal.h"
+#include "gki.h"
 
+#define FORWARD_IGNORE        1
+#define FORWARD_SUCCESS       0
+#define FORWARD_FAILURE     (-1)
+#define FORWARD_CONGEST     (-2)
 //#define PANU_DISABLED TRUE
 
 #if (PAN_NAP_DISABLED == TRUE) && (PANU_DISABLED == TRUE)
@@ -71,6 +77,8 @@
 
 #define asrt(s) if(!(s)) BTIF_TRACE_ERROR3("btif_pan: ## %s assert %s failed at line:%d ##",__FUNCTION__, #s, __LINE__)
 
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
 btpan_cb_t btpan_cb;
 
 BD_ADDR local_addr;
@@ -85,15 +93,8 @@ static int btpan_get_local_role(void);
 static void btpan_tap_fd_signaled(int fd, int type, int flags, uint32_t user_id);
 static void btpan_cleanup_conn(btpan_conn_t* conn);
 static void bta_pan_callback(tBTA_PAN_EVT event, tBTA_PAN *p_data);
-/*******************************************************************************
- **
- ** Function         btpan_ini
- **
- ** Description     initializes the pan interface
- **
- ** Returns         bt_status_t
- **
- *******************************************************************************/
+static void btu_exec_tap_fd_read(void *p_param);
+
 static btpan_interface_t pan_if = {
     sizeof(pan_if),
     btpan_jni_init,
@@ -103,10 +104,21 @@ static btpan_interface_t pan_if = {
     btpan_disconnect,
     btpan_jni_cleanup
 };
+
 btpan_interface_t *btif_pan_get_interface()
 {
     return &pan_if;
 }
+
+/*******************************************************************************
+ **
+ ** Function        btif_pan_init
+ **
+ ** Description     initializes the pan interface
+ **
+ ** Returns         bt_status_t
+ **
+ *******************************************************************************/
 void btif_pan_init()
 {
     BTIF_TRACE_DEBUG2("jni_initialized = %d, btpan_cb.enabled:%d", jni_initialized, btpan_cb.enabled);
@@ -116,6 +128,7 @@ void btif_pan_init()
         BTIF_TRACE_DEBUG0("Enabling PAN....");
         memset(&btpan_cb, 0, sizeof(btpan_cb));
         btpan_cb.tap_fd = -1;
+        btpan_cb.flow = 1;
         int i;
         for(i = 0; i < MAX_PAN_CONNS; i++)
             btpan_cleanup_conn(&btpan_cb.conns[i]);
@@ -124,6 +137,7 @@ void btif_pan_init()
         btpan_enable(BTPAN_LOCAL_ROLE);
     }
 }
+
 static void pan_disable()
 {
     if(btpan_cb.enabled)
@@ -132,12 +146,12 @@ static void pan_disable()
         BTA_PanDisable();
         if(btpan_cb.tap_fd != -1)
         {
-            destroy_tap_read_thread();
             btpan_tap_close(btpan_cb.tap_fd);
             btpan_cb.tap_fd = -1;
         }
     }
 }
+
 void btif_pan_cleanup()
 {
     if(stack_initialized)
@@ -167,6 +181,7 @@ static void btpan_jni_cleanup()
     pan_disable();
     jni_initialized = FALSE;
 }
+
 static inline int bta_role_to_btpan(int bta_pan_role)
 {
     int btpan_role = 0;
@@ -181,6 +196,7 @@ static inline int bta_role_to_btpan(int bta_pan_role)
     }
     return btpan_role;
 }
+
 static inline int btpan_role_to_bta(int btpan_role)
 {
     int bta_pan_role = PAN_ROLE_INACTIVE;
@@ -195,6 +211,7 @@ static inline int btpan_role_to_bta(int btpan_role)
     }
     return bta_pan_role;
 }
+
 static volatile int btpan_dev_local_role;
 static tBTA_PAN_ROLE_INFO bta_panu_info = {PANU_SERVICE_NAME, 0, PAN_SECURITY};
 static tBTA_PAN_ROLE_INFO bta_pan_nap_info = {PAN_NAP_SERVICE_NAME, 1, PAN_SECURITY};
@@ -212,11 +229,13 @@ static bt_status_t btpan_enable(int local_role)
     return BT_STATUS_FAIL;
 #endif
 }
+
 static int btpan_get_local_role()
 {
     BTIF_TRACE_DEBUG1("btpan_dev_local_role:%d", btpan_dev_local_role);
     return btpan_dev_local_role;
 }
+
 static bt_status_t btpan_connect(const bt_bdaddr_t *bd_addr, int local_role, int remote_role)
 {
     BTIF_TRACE_DEBUG2("local_role:%d, remote_role:%d", local_role, remote_role);
@@ -226,6 +245,7 @@ static bt_status_t btpan_connect(const bt_bdaddr_t *bd_addr, int local_role, int
     BTA_PanOpen((UINT8*)bd_addr->address, bta_local_role, bta_remote_role);
     return BT_STATUS_SUCCESS;
 }
+
 static void btif_in_pan_generic_evt(UINT16 event, char *p_param)
 {
     BTIF_TRACE_EVENT2("%s: event=%d", __FUNCTION__, event);
@@ -251,6 +271,7 @@ static void btif_in_pan_generic_evt(UINT16 event, char *p_param)
         break;
     }
 }
+
 static bt_status_t btpan_disconnect(const bt_bdaddr_t *bd_addr)
 {
     btpan_conn_t* conn = btpan_find_conn_addr(bd_addr->address);
@@ -264,24 +285,25 @@ static bt_status_t btpan_disconnect(const bt_bdaddr_t *bd_addr)
     }
     return BT_STATUS_FAIL;
 }
-static int pth = -1;
+
+static int pan_pth = -1;
 void create_tap_read_thread(int tap_fd)
 {
-    if(pth < 0)
-    {
-        pth = btsock_thread_create(btpan_tap_fd_signaled, NULL);
-        if(pth >= 0)
-            btsock_thread_add_fd(pth, tap_fd, 0, SOCK_THREAD_FD_RD, 0);
-    }
+    if(pan_pth < 0)
+        pan_pth = btsock_thread_create(btpan_tap_fd_signaled, NULL);
+    if(pan_pth >= 0)
+        btsock_thread_add_fd(pan_pth, tap_fd, 0, SOCK_THREAD_FD_RD, 0);
 }
+
 void destroy_tap_read_thread(void)
 {
-    if(pth >= 0)
+    if(pan_pth >= 0)
     {
-        btsock_thread_exit(pth);
-        pth = -1;
+        btsock_thread_exit(pan_pth);
+        pan_pth = -1;
     }
 }
+
 static int tap_if_up(const char *devname, BD_ADDR addr)
 {
     struct ifreq ifr;
@@ -354,6 +376,18 @@ static int tap_if_down(const char *devname)
 
     return 0;
 }
+
+void btpan_set_flow_control(BOOLEAN enable) {
+    if (btpan_cb.tap_fd == -1)
+        return;
+
+    btpan_cb.flow = enable;
+    if (enable) {
+        btsock_thread_add_fd(pan_pth, btpan_cb.tap_fd, 0, SOCK_THREAD_FD_RD, 0);
+        bta_dmexecutecallback(btu_exec_tap_fd_read, (void *)btpan_cb.tap_fd);
+    }
+}
+
 int btpan_tap_open()
 {
     struct ifreq ifr;
@@ -390,6 +424,7 @@ int btpan_tap_open()
     close(fd);
     return -1;
 }
+
 int btpan_tap_send(int tap_fd, const BD_ADDR src, const BD_ADDR dst, UINT16 proto, const char* buf,
                     UINT16 len, BOOLEAN ext, BOOLEAN forward)
 {
@@ -423,12 +458,16 @@ int btpan_tap_send(int tap_fd, const BD_ADDR src, const BD_ADDR dst, UINT16 prot
     return -1;
 
 }
+
 int btpan_tap_close(int fd)
 {
     tap_if_down(TAP_IF_NAME);
     close(fd);
+    if(pan_pth >= 0)
+        btsock_thread_wakeup(pan_pth);
     return 0;
 }
+
 btpan_conn_t * btpan_find_conn_handle(UINT16 handle)
 {
     int i;
@@ -437,6 +476,7 @@ btpan_conn_t * btpan_find_conn_handle(UINT16 handle)
             return &btpan_cb.conns[i];
     return NULL;
 }
+
 btpan_conn_t* btpan_find_conn_addr(const BD_ADDR addr)
 {
     int i;
@@ -445,6 +485,7 @@ btpan_conn_t* btpan_find_conn_addr(const BD_ADDR addr)
             return &btpan_cb.conns[i];
     return NULL;
 }
+
 static void btpan_cleanup_conn(btpan_conn_t* conn)
 {
     if(conn)
@@ -456,6 +497,7 @@ static void btpan_cleanup_conn(btpan_conn_t* conn)
         conn->local_role = conn->remote_role = 0;
     }
 }
+
 btpan_conn_t* btpan_new_conn(int handle, const BD_ADDR addr, int local_role, int remote_role )
 {
     int i;
@@ -485,6 +527,7 @@ void btpan_close_handle(btpan_conn_t *p)
     p->remote_role = -1;
     memset(&p->peer, 0, 6);
 }
+
 static inline int should_forward(tETH_HDR* hdr)
 {
     if(ntohs(hdr->h_proto) == ETH_P_IP || ntohs(hdr->h_proto) == ETH_P_ARP)
@@ -492,39 +535,30 @@ static inline int should_forward(tETH_HDR* hdr)
     BTIF_TRACE_DEBUG1("unknown proto:%x", ntohs(hdr->h_proto));
     return FALSE;
 }
-#if BTA_PAN_INCLUDED == TRUE
-extern void bta_pan_ci_rx_write(UINT16 handle, BD_ADDR dst, BD_ADDR src, UINT16 protocol,
-        UINT8 *p_data, UINT16 len, BOOLEAN ext);
-#else
-static void bta_pan_ci_rx_write(UINT16 handle, BD_ADDR dst, BD_ADDR src, UINT16 protocol,
-        UINT8 *p_data, UINT16 len, BOOLEAN ext)
-{
-    UNUSED(handle);
-    UNUSED(dst);
-    UNUSED(src);
-    UNUSED(protocol);
-    UNUSED(p_data);
-    UNUSED(len);
-    UNUSED(ext);
-}
-#endif
-static void forward_bnep(tETH_HDR* eth_hdr, char * packet, int size)
-{
+
+static int forward_bnep(tETH_HDR* eth_hdr, BT_HDR *hdr) {
     int broadcast = eth_hdr->h_dest[0] & 1;
     int i;
-    for(i = 0; i < MAX_PAN_CONNS; i++)
-    {
+
+    // Find the right connection to send this frame over.
+    for (i = 0; i < MAX_PAN_CONNS; i++) {
         UINT16 handle = btpan_cb.conns[i].handle;
-        if(handle != (UINT16)-1 &&
+        if (handle != (UINT16)-1 &&
                 (broadcast || memcmp(btpan_cb.conns[i].eth_addr, eth_hdr->h_dest, sizeof(BD_ADDR)) == 0
-                 || memcmp(btpan_cb.conns[i].peer, eth_hdr->h_dest, sizeof(BD_ADDR)) == 0))
-        {
-            BTIF_TRACE_DEBUG1("calling bta_pan_ci_rx_write, handle:%d", handle);
-            bta_pan_ci_rx_write(handle, eth_hdr->h_dest, eth_hdr->h_src,
-                    ntohs(eth_hdr->h_proto), (UINT8*)packet, size, 0);
-            break;
+                 || memcmp(btpan_cb.conns[i].peer, eth_hdr->h_dest, sizeof(BD_ADDR)) == 0)) {
+            int result = PAN_WriteBuf(handle, eth_hdr->h_dest, eth_hdr->h_src, ntohs(eth_hdr->h_proto), hdr, 0);
+            switch (result) {
+                case PAN_Q_SIZE_EXCEEDED:
+                    return FORWARD_CONGEST;
+                case PAN_SUCCESS:
+                    return FORWARD_SUCCESS;
+                default:
+                    return FORWARD_FAILURE;
+            }
         }
     }
+    GKI_freebuf(hdr);
+    return FORWARD_IGNORE;
 }
 
 static void bta_pan_callback_transfer(UINT16 event, char *p_param)
@@ -621,36 +655,98 @@ static void bta_pan_callback(tBTA_PAN_EVT event, tBTA_PAN *p_data)
 {
     btif_transfer_context(bta_pan_callback_transfer, event, (char*)p_data, sizeof(tBTA_PAN), NULL);
 }
-#define MAX_PACKET_SIZE 2000
-static void btpan_tap_fd_signaled(int fd, int type, int flags, uint32_t user_id)
-{
-    char packet[MAX_PACKET_SIZE];
-    tETH_HDR eth_hdr;
-    UNUSED(type);
-    UNUSED(user_id);
 
-    if(flags & SOCK_THREAD_FD_EXCEPTION)
-    {
-        BTIF_TRACE_ERROR1("pan tap fd:%d exception", fd);
-    }
-    else if(flags & SOCK_THREAD_FD_RD)
-    {
-        /* debug("tab fd read trigged,  data"); */
-        int size = read(fd, packet, MAX_PACKET_SIZE);
-        /* debug("tap fd read trigged, read size:%d", size); */
-        memcpy(&eth_hdr, &packet, sizeof(tETH_HDR));
-        /* debug("eth src = %02x:%02x:%02x:%02x:%02x:%02x", */
-        /*         eth_hdr.h_src[0],  eth_hdr.h_src[1], eth_hdr.h_src[2], eth_hdr.h_src[3], */
-        /*         eth_hdr.h_src[4], eth_hdr.h_src[5]); */
-        /* debug("eth dest = %02x:%02x:%02x:%02x:%02x:%02x", */
-        /*         eth_hdr.h_dest[0], eth_hdr.h_dest[1], eth_hdr.h_dest[2], eth_hdr.h_dest[3], */
-        /*         eth_hdr.h_dest[4], eth_hdr.h_dest[5]); */
-        //dump_bin("eth packet received", packet, size);
-        if(should_forward(&eth_hdr))
-        {
-            forward_bnep(&eth_hdr, packet + sizeof(tETH_HDR),  size - sizeof(tETH_HDR));
+#define IS_EXCEPTION(e) ((e) & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL))
+static void btu_exec_tap_fd_read(void *p_param) {
+    struct pollfd ufd;
+    int fd = (int)p_param;
+
+    if (fd == -1 || fd != btpan_cb.tap_fd)
+        return;
+
+    // Keep sending until someone either turns off BTIF or disables data the flow.
+    while (btif_is_enabled() && btpan_cb.flow) {
+        BT_HDR *buffer = (BT_HDR *)GKI_getpoolbuf(PAN_POOL_ID);
+        if (!buffer) {
+            BTIF_TRACE_WARNING1("%s unable to allocate buffer for packet.", __func__);
+            break;
+        }
+        buffer->offset = PAN_MINIMUM_OFFSET;
+        buffer->len = GKI_get_buf_size(buffer) - sizeof(BT_HDR) - buffer->offset;
+
+        UINT8 *packet = (UINT8 *)buffer + sizeof(BT_HDR) + buffer->offset;
+
+        // If we don't have an undelivered packet left over, pull one from the TAP driver.
+        // We save it in the congest_packet right away in case we can't deliver it in this
+        // attempt.
+        if (!btpan_cb.congest_packet_size) {
+            ssize_t ret = read(fd, btpan_cb.congest_packet, sizeof(btpan_cb.congest_packet));
+            switch (ret) {
+                case -1:
+                    BTIF_TRACE_ERROR2("%s unable to read from driver: %s", __func__, strerror(errno));
+                    GKI_freebuf(buffer);
+                    return;
+                case 0:
+                    BTIF_TRACE_WARNING1("%s end of file reached.", __func__);
+                    GKI_freebuf(buffer);
+                    return;
+                default:
+                    btpan_cb.congest_packet_size = ret;
+                    break;
+            }
+        }
+
+        memcpy(packet, btpan_cb.congest_packet, MIN(btpan_cb.congest_packet_size, buffer->len));
+        buffer->len = MIN(btpan_cb.congest_packet_size, buffer->len);
+
+        if (buffer->len > sizeof(tETH_HDR) && should_forward((tETH_HDR *)packet)) {
+            // Extract the ethernet header from the buffer since the PAN_WriteBuf inside
+            // forward_bnep can't handle two pointers that point inside the same GKI buffer.
+            tETH_HDR hdr;
+            memcpy(&hdr, packet, sizeof(tETH_HDR));
+
+            // Skip the ethernet header.
+            buffer->len -= sizeof(tETH_HDR);
+            buffer->offset += sizeof(tETH_HDR);
+            if (forward_bnep(&hdr, buffer) != FORWARD_CONGEST)
+                btpan_cb.congest_packet_size = 0;
+        } else {
+            BTIF_TRACE_WARNING2("%s dropping packet of length %d", __func__, buffer->len);
+            btpan_cb.congest_packet_size = 0;
+            GKI_freebuf(buffer);
+        }
+
+        // Bail out of the loop if reading from the TAP fd would block.
+        ufd.fd = fd;
+        ufd.events = POLLIN;
+        ufd.revents = 0;
+        if(poll(&ufd, 1, 0) <= 0 || IS_EXCEPTION(ufd.revents)) {
+            btsock_thread_add_fd(pan_pth, fd, 0, SOCK_THREAD_FD_RD, 0);
+            return;
         }
-        btsock_thread_add_fd(pth, fd, 0, SOCK_THREAD_FD_RD | SOCK_THREAD_ADD_FD_SYNC, 0);
     }
 }
 
+static void btif_pan_close_all_conns() {
+    int i;
+    if (!stack_initialized)
+        return;
+
+    for (i = 0; i < MAX_PAN_CONNS; ++i)
+        if (btpan_cb.conns[i].handle != -1)
+            BTA_PanClose(btpan_cb.conns[i].handle);
+}
+
+static void btpan_tap_fd_signaled(int fd, int type, int flags, uint32_t user_id) {
+    assert(btpan_cb.tap_fd == fd);
+
+    if (btpan_cb.tap_fd != fd)
+        return;
+
+    if(flags & SOCK_THREAD_FD_EXCEPTION) {
+        btpan_cb.tap_fd = -1;
+        btpan_tap_close(fd);
+        btif_pan_close_all_conns();
+    } else if(flags & SOCK_THREAD_FD_RD)
+        bta_dmexecutecallback(btu_exec_tap_fd_read, (void *)fd);
+}