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 128577 c41e47d576077a59acc6c395522d89d51c437ff0
parent 128576 954c25a05631239dd4977fa404c24eaef06ab7aa
child 128578 0984b5e72dc8d02390fcd0c03a9f065b007fb1e0
push id26397
push userechou@mozilla.com
push dateFri, 12 Apr 2013 10:46:18 +0000
treeherdermozilla-inbound@0984b5e72dc8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot, gyeh
bugs823803
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;