Bug 823803 - Add L2CAP/EL2CAP Socket support, r=qdot, r=gyeh
authorEric Chou <echou@mozilla.com>
Fri, 12 Apr 2013 18:45:37 +0800
changeset 135354 c41e47d576077a59acc6c395522d89d51c437ff0
parent 135353 954c25a05631239dd4977fa404c24eaef06ab7aa
child 135355 0984b5e72dc8d02390fcd0c03a9f065b007fb1e0
push idunknown
push userunknown
push dateunknown
reviewersqdot, gyeh
bugs823803
milestone23.0a1
Bug 823803 - Add L2CAP/EL2CAP Socket support, r=qdot, r=gyeh This version of BlueZ from Code Aurora has added GOEP_PSM to Object Push Profile service record, which means that remote devices may connect with us via both L2CAP and RFCOMM. This patch completes L2CAP/EL2CAP socket implementation.
dom/bluetooth/BluetoothCommon.h
dom/bluetooth/BluetoothUnixSocketConnector.cpp
dom/bluetooth/BluetoothUuid.h
ipc/unixsocket/UnixSocket.h
--- a/dom/bluetooth/BluetoothCommon.h
+++ b/dom/bluetooth/BluetoothCommon.h
@@ -54,18 +54,19 @@ extern bool gBluetoothDebugFlag;
 // Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx)
 #define BLUETOOTH_ADDRESS_LENGTH 17
 #define BLUETOOTH_ADDRESS_NONE   "00:00:00:00:00:00"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 enum BluetoothSocketType {
   RFCOMM = 1,
-  SCO = 2,
-  L2CAP = 3
+  SCO    = 2,
+  L2CAP  = 3,
+  EL2CAP = 4
 };
 
 class BluetoothSignal;
 typedef mozilla::Observer<BluetoothSignal> BluetoothSignalObserver;
 
 // Enums for object types, currently used for shared function lookups
 // (get/setproperty, etc...). Possibly discernable via dbus paths, but this
 // method is future-proofed for platform independence.
--- a/dom/bluetooth/BluetoothUnixSocketConnector.cpp
+++ b/dom/bluetooth/BluetoothUnixSocketConnector.cpp
@@ -23,27 +23,30 @@
 
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
 
 #include <sys/socket.h>
 #include <bluetooth/bluetooth.h>
-#include <bluetooth/sco.h>
+#include <bluetooth/l2cap.h>
 #include <bluetooth/rfcomm.h>
-#include <bluetooth/l2cap.h>
+#include <bluetooth/sco.h>
 
 #include "BluetoothUnixSocketConnector.h"
 #include "nsThreadUtils.h"
 
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
-static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer
+static const int RFCOMM_SO_SNDBUF = 70 * 1024;  // 70 KB send buffer
+static const int L2CAP_SO_SNDBUF = 400 * 1024;  // 400 KB send buffer
+static const int L2CAP_SO_RCVBUF = 400 * 1024;  // 400 KB receive buffer
+static const int L2CAP_MAX_MTU = 65000;
 
 static
 int get_bdaddr(const char *str, bdaddr_t *ba)
 {
   char *d = ((char*)ba) + 5, *endp;
   for (int i = 0; i < 6; i++) {
     *d-- = strtol(str, &endp, 16);
     MOZ_ASSERT(!(*endp != ':' && i != 5));
@@ -69,48 +72,96 @@ BluetoothUnixSocketConnector::BluetoothU
                  , mEncrypt(aEncrypt)
 {
 }
 
 bool
 BluetoothUnixSocketConnector::SetUp(int aFd)
 {
   int lm = 0;
-  int sndbuf;
+  int sndbuf, rcvbuf;
+
   /* kernel does not yet support LM for SCO */
   switch (mType) {
   case BluetoothSocketType::RFCOMM:
     lm |= mAuth ? RFCOMM_LM_AUTH : 0;
     lm |= mEncrypt ? RFCOMM_LM_ENCRYPT : 0;
     break;
   case BluetoothSocketType::L2CAP:
+  case BluetoothSocketType::EL2CAP:
     lm |= mAuth ? L2CAP_LM_AUTH : 0;
     lm |= mEncrypt ? L2CAP_LM_ENCRYPT : 0;
     break;
   case BluetoothSocketType::SCO:
     break;
   default:
     MOZ_NOT_REACHED("Unknown socket type!");
   }
 
   if (lm) {
-    if (setsockopt(aFd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
-      NS_WARNING("setsockopt(RFCOMM_LM) failed, throwing");
-      return false;
+    if (mType == BluetoothSocketType::RFCOMM) {
+      if (setsockopt(aFd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
+        NS_WARNING("setsockopt(RFCOMM_LM) failed, throwing");
+        return false;
+      }
+    } else if (mType == BluetoothSocketType::L2CAP ||
+               mType == BluetoothSocketType::EL2CAP) {
+      if (setsockopt(aFd, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm))) {
+        NS_WARNING("setsockopt(L2CAP_LM) failed, throwing");
+        return false;
+      }
     }
   }
 
   if (mType == BluetoothSocketType::RFCOMM) {
     sndbuf = RFCOMM_SO_SNDBUF;
     if (setsockopt(aFd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
       NS_WARNING("setsockopt(SO_SNDBUF) failed, throwing");
       return false;
     }
   }
 
+  /* Setting L2CAP socket options */
+  if (mType == BluetoothSocketType::L2CAP ||
+      mType == BluetoothSocketType::EL2CAP) {
+    struct l2cap_options opts;
+    int optlen = sizeof(opts), err;
+    err = getsockopt(aFd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen);
+    if (!err) {
+      /* setting MTU for [E]L2CAP */
+      opts.omtu = opts.imtu = L2CAP_MAX_MTU;
+
+      /* Enable ERTM for [E]L2CAP */
+      if (mType == BluetoothSocketType::EL2CAP) {
+        opts.flush_to = 0xffff; /* infinite */
+        opts.mode = L2CAP_MODE_ERTM;
+        opts.fcs = 1;
+        opts.txwin_size = 64;
+        opts.max_tx = 10;
+      }
+
+      err = setsockopt(aFd, SOL_L2CAP, L2CAP_OPTIONS, &opts, optlen);
+    }
+
+    /* Set larger SNDBUF & RCVBUF for EL2CAP connections */
+    if (mType == BluetoothSocketType::EL2CAP) {
+      sndbuf = L2CAP_SO_SNDBUF;
+      if (setsockopt(aFd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
+        NS_WARNING("setsockopt(SO_SNDBUF) failed, throwing");
+        return false;
+      }
+
+      rcvbuf = L2CAP_SO_RCVBUF;
+      if (setsockopt(aFd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))) {
+        NS_WARNING("setsockopt(SO_RCVBUF) failed, throwing");
+        return false;
+      }
+    }
+  }
+
   return true;
 }
 
 int
 BluetoothUnixSocketConnector::Create()
 {
   MOZ_ASSERT(!NS_IsMainThread());
   int fd = -1;
@@ -120,28 +171,33 @@ BluetoothUnixSocketConnector::Create()
     fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
     break;
   case BluetoothSocketType::SCO:
     fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
     break;
   case BluetoothSocketType::L2CAP:
     fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
     break;
+  case BluetoothSocketType::EL2CAP:
+    fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP);
+    break;
   default:
     MOZ_NOT_REACHED();
   }
 
   if (fd < 0) {
     NS_WARNING("Could not open bluetooth socket!");
     return -1;
   }
 
   if (!SetUp(fd)) {
     NS_WARNING("Could not set up socket!");
+    return -1;
   }
+
   return fd;
 }
 
 bool
 BluetoothUnixSocketConnector::CreateAddr(bool aIsServer,
                                          socklen_t& aAddrSize,
                                          sockaddr_any& aAddr,
                                          const char* aAddress)
@@ -151,24 +207,35 @@ BluetoothUnixSocketConnector::CreateAddr
 
   if (!aIsServer && aAddress && strlen(aAddress) > 0) {
     if (get_bdaddr(aAddress, &bd_address_obj)) {
       NS_WARNING("Can't get bluetooth address!");
       return false;
     }
   }
 
+  // Initialize
+  memset(&aAddr, 0, sizeof(aAddr));
+
   switch (mType) {
   case BluetoothSocketType::RFCOMM:
     struct sockaddr_rc addr_rc;
     aAddrSize = sizeof(addr_rc);
     aAddr.rc.rc_family = AF_BLUETOOTH;
     aAddr.rc.rc_channel = mChannel;
     memcpy(&aAddr.rc.rc_bdaddr, &bd_address_obj, sizeof(bd_address_obj));
     break;
+  case BluetoothSocketType::L2CAP:
+  case BluetoothSocketType::EL2CAP:
+    struct sockaddr_l2 addr_l2;
+    aAddrSize = sizeof(addr_l2);
+    aAddr.l2.l2_family = AF_BLUETOOTH;
+    aAddr.l2.l2_psm = mChannel;
+    memcpy(&aAddr.l2.l2_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
+    break;
   case BluetoothSocketType::SCO:
     struct sockaddr_sco addr_sco;
     aAddrSize = sizeof(addr_sco);
     aAddr.sco.sco_family = AF_BLUETOOTH;
     memcpy(&aAddr.sco.sco_bdaddr, &bd_address_obj, sizeof(bd_address_obj));
     break;
   default:
     NS_WARNING("Socket type unknown!");
@@ -184,13 +251,17 @@ BluetoothUnixSocketConnector::GetSocketA
   char addr[18];
   switch (mType) {
   case BluetoothSocketType::RFCOMM:
     get_bdaddr_as_string((bdaddr_t*)(&aAddr.rc.rc_bdaddr), addr);
     break;
   case BluetoothSocketType::SCO:
     get_bdaddr_as_string((bdaddr_t*)(&aAddr.sco.sco_bdaddr), addr);
     break;
+  case BluetoothSocketType::L2CAP:
+  case BluetoothSocketType::EL2CAP:
+    get_bdaddr_as_string((bdaddr_t*)(&aAddr.l2.l2_bdaddr), addr);
+    break;
   default:
     MOZ_NOT_REACHED("Socket should be either RFCOMM or SCO!");
   }
   aAddrStr.AssignASCII(addr);
 }
--- a/dom/bluetooth/BluetoothUuid.h
+++ b/dom/bluetooth/BluetoothUuid.h
@@ -45,19 +45,20 @@ public:
 
 // TODO/qdot: Move these back into gonk and make the service handler deal with
 // it there.
 //
 // Gotten from reading the "u8" values in B2G/external/bluez/src/adapter.c
 // These were hardcoded into android
 enum BluetoothReservedChannels {
   CHANNEL_DIALUP_NETWORK = 1,
-  CHANNEL_HANDSFREE_AG = 10,
-  CHANNEL_HEADSET_AG = 11,
-  CHANNEL_OPUSH = 12,
-  CHANNEL_SIM_ACCESS = 15,
-  CHANNEL_PBAP_PSE = 19,
-  CHANNEL_FTP = 20,
+  CHANNEL_HANDSFREE_AG   = 10,
+  CHANNEL_HEADSET_AG     = 11,
+  CHANNEL_OPUSH          = 12,
+  CHANNEL_SIM_ACCESS     = 15,
+  CHANNEL_PBAP_PSE       = 19,
+  CHANNEL_FTP            = 20,
+  CHANNEL_OPUSH_L2CAP    = 5255
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/ipc/unixsocket/UnixSocket.h
+++ b/ipc/unixsocket/UnixSocket.h
@@ -10,16 +10,17 @@
 
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <netinet/in.h>
 #ifdef MOZ_B2G_BT
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sco.h>
+#include <bluetooth/l2cap.h>
 #include <bluetooth/rfcomm.h>
 #endif
 #include <stdlib.h>
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
@@ -28,16 +29,17 @@ namespace ipc {
 union sockaddr_any {
   sockaddr_storage storage; // address-family only
   sockaddr_un un;
   sockaddr_in in;
   sockaddr_in6 in6;
 #ifdef MOZ_B2G_BT
   sockaddr_sco sco;
   sockaddr_rc rc;
+  sockaddr_l2 l2;
 #endif
   // ... others
 };
 
 class UnixSocketRawData
 {
 public:
   nsAutoArrayPtr<uint8_t> mData;