Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions include/BluetoothHciL2Socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ class BluetoothHciSocket;
// Include necessary headers
#include "BluetoothStructs.h"

enum class BluetoothHciL2ConnectResult {
SETUP_FAILED,
CONNECTED,
CONNECTION_FAILED
};

// Bluetooth HCI L2CAP Socket class
class BluetoothHciL2Socket {
public:
Expand All @@ -29,8 +35,14 @@ class BluetoothHciL2Socket {
/// Destructor
~BluetoothHciL2Socket();

/// Connects to the remote device.
void connect();
/**
* Connects to the remote device.
*
* SETUP_FAILED means no controller connection was requested, so the caller
* may safely use another transport. CONNECTION_FAILED means the kernel did
* request a controller connection and reported its failure.
*/
BluetoothHciL2ConnectResult connect();

/// Disconnects the socket.
void disconnect();
Expand Down
54 changes: 43 additions & 11 deletions src/BluetoothHciL2Socket.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
Expand Down Expand Up @@ -50,25 +52,55 @@ BluetoothHciL2Socket::~BluetoothHciL2Socket() {
}
}

void BluetoothHciL2Socket::connect() {
BluetoothHciL2ConnectResult BluetoothHciL2Socket::connect() {
this->_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if(this->_socket < 0) return;
if(this->_socket < 0) return BluetoothHciL2ConnectResult::SETUP_FAILED;

if (bind(this->_socket, (struct sockaddr*)&_l2_src, sizeof(_l2_src)) < 0) {
close(this->_socket);
this->_socket = -1;
return;
return BluetoothHciL2ConnectResult::SETUP_FAILED;
}

// the kernel needs to flush the socket before we continue
while (::connect(_socket, (struct sockaddr *)&_l2_dst, sizeof(_l2_dst)) == -1 ) {
if(errno == EINTR) {
continue;
}
close(_socket);
_socket = -1;
break;
const int socketFlags = fcntl(this->_socket, F_GETFL, 0);
if (socketFlags < 0 || fcntl(this->_socket, F_SETFL, socketFlags | O_NONBLOCK) < 0) {
close(this->_socket);
this->_socket = -1;
return BluetoothHciL2ConnectResult::SETUP_FAILED;
}

if (::connect(this->_socket, (struct sockaddr *)&_l2_dst, sizeof(_l2_dst)) == 0) {
fcntl(this->_socket, F_SETFL, socketFlags);
return BluetoothHciL2ConnectResult::CONNECTED;
}

if (errno != EINPROGRESS) {
close(this->_socket);
this->_socket = -1;
return BluetoothHciL2ConnectResult::SETUP_FAILED;
}

// EINPROGRESS confirms that the kernel accepted the request and initiated
// the controller connection. Preserve the previous synchronous behavior,
// but retain this distinction if the controller ultimately reports failure.
struct pollfd descriptor = { this->_socket, POLLOUT, 0 };
int pollResult;
do {
pollResult = poll(&descriptor, 1, -1);
} while (pollResult < 0 && errno == EINTR);

int connectError = 0;
socklen_t connectErrorLength = sizeof(connectError);
if (pollResult < 0 ||
getsockopt(this->_socket, SOL_SOCKET, SO_ERROR, &connectError, &connectErrorLength) < 0 ||
connectError != 0) {
close(this->_socket);
this->_socket = -1;
return BluetoothHciL2ConnectResult::CONNECTION_FAILED;
}

fcntl(this->_socket, F_SETFL, socketFlags);
return BluetoothHciL2ConnectResult::CONNECTED;
}

void BluetoothHciL2Socket::disconnect() {
Expand Down
12 changes: 9 additions & 3 deletions src/BluetoothHciSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,19 @@ bool BluetoothHciSocket::kernelConnectWorkArounds(char * data, int length) {
this->_l2sockets_connecting[bdaddr_dst] = l2socket_ptr;

// Attempt to connect
l2socket_ptr->connect();
const auto connectResult = l2socket_ptr->connect();

// Check if connected successfully
if (!l2socket_ptr->isConnected()) {
if (connectResult == BluetoothHciL2ConnectResult::SETUP_FAILED) {
this->_l2sockets_connecting.erase(bdaddr_dst);
return false;
}

if (connectResult == BluetoothHciL2ConnectResult::CONNECTION_FAILED) {
// The kernel already sent the controller connection command. Do not
// fall through to Write() and create a second raw HCI attempt.
this->_l2sockets_connecting.erase(bdaddr_dst);
return true;
}
}
}

Expand Down
Loading