Skip to content

rework the disconnect / connect workaround #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 12, 2021
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
79 changes: 68 additions & 11 deletions src/BluetoothHciSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ void BluetoothHciSocket::stop() {
}

void BluetoothHciSocket::write_(char* data, int length) {
if (this->_mode == HCI_CHANNEL_RAW && this->kernelConnectWorkArounds(data, length)) {
return;
}

if (write(this->_socket, data, length) < 0) {
this->emitErrnoError("write");
}
Expand Down Expand Up @@ -350,11 +354,10 @@ int BluetoothHciSocket::kernelDisconnectWorkArounds(int length, char* data) {
// HCI Event - LE Meta Event - LE Connection Complete => manually create L2CAP socket to force kernel to book keep
// HCI Event - Disconn Complete =======================> close socket from above

if (length == 22 && data[0] == 0x04 && data[1] == 0x3e && data[2] == 0x13 && data[3] == 0x01 && data[4] == 0x00) {
if (length == 22 && data[0] == 0x04 && data[1] == 0x3e && data[2] == 0x13 && data[3] == 0x01 && data[4] == 0x00 && data[7] == 0x01) {
int l2socket;
struct sockaddr_l2 l2a = {};
unsigned short l2cid;
unsigned short handle = *((unsigned short*)(&data[5]));

#if __BYTE_ORDER == __LITTLE_ENDIAN
l2cid = ATT_CID;
Expand Down Expand Up @@ -385,21 +388,75 @@ int BluetoothHciSocket::kernelDisconnectWorkArounds(int length, char* data) {
l2a.l2_cid = l2cid;
l2a.l2_bdaddr_type = data[8] + 1; // BDADDR_LE_PUBLIC (0x01), BDADDR_LE_RANDOM (0x02)

if (connect(l2socket, (struct sockaddr *)&l2a, sizeof(l2a)) < -1) {
while (connect(l2socket, (struct sockaddr *)&l2a, sizeof(l2a) == -1) ) {
if(errno == EINTR) {
continue;
}
close(l2socket);
return -3;
}

this->_l2sockets[handle] = l2socket;
} else if (length == 7 && data[0] == 0x04 && data[1] == 0x05 && data[2] == 0x04 && data[3] == 0x00) {
unsigned short handle = *((unsigned short*)(&data[4]));
return 0;
}
}

if (this->_l2sockets.count(handle) > 0) {
close(this->_l2sockets[handle]);
this->_l2sockets.erase(handle);
}

bool BluetoothHciSocket::kernelConnectWorkArounds(char* data, int length)
{
if (length == 29 && data[0] == 0x01 && data[1] == 0x0d && data[2] == 0x20 && data[3] == 0x19) {
int l2socket;
struct sockaddr_l2 l2a;
unsigned short l2cid;
unsigned short connMinInterval;
unsigned short connMaxInterval;
unsigned short connLatency;
unsigned short supervisionTimeout;
char command[128];

#if __BYTE_ORDER == __LITTLE_ENDIAN
l2cid = ATT_CID;
#elif __BYTE_ORDER == __BIG_ENDIAN
l2cid = bswap_16(ATT_CID);
#else
#error "Unknown byte order"
#endif

l2socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

memset(&l2a, 0, sizeof(l2a));
l2a.l2_family = AF_BLUETOOTH;
l2a.l2_cid = l2cid;
memcpy(&l2a.l2_bdaddr, this->_address, sizeof(l2a.l2_bdaddr));
l2a.l2_bdaddr_type = this->_addressType;
bind(l2socket, (struct sockaddr*)&l2a, sizeof(l2a));

memset(&l2a, 0, sizeof(l2a));
l2a.l2_family = AF_BLUETOOTH;
memcpy(&l2a.l2_bdaddr, &data[10], sizeof(l2a.l2_bdaddr));
l2a.l2_cid = l2cid;
l2a.l2_bdaddr_type = data[9] + 1; // BDADDR_LE_PUBLIC (0x01), BDADDR_LE_RANDOM (0x02)

// extract the connection parameter
connMinInterval = (data[18] << 8) | data[17];
connMaxInterval = (data[20] << 8) | data[19];
connLatency = (data[22] << 8) | data[21];
supervisionTimeout = (data[24] << 8) | data[23];

// override the HCI devices connection parameters using debugfs
sprintf(command, "echo %u > /sys/kernel/debug/bluetooth/hci%d/conn_min_interval", connMinInterval, this->_devId);
system(command);
sprintf(command, "echo %u > /sys/kernel/debug/bluetooth/hci%d/conn_max_interval", connMaxInterval, this->_devId);
system(command);
sprintf(command, "echo %u > /sys/kernel/debug/bluetooth/hci%d/conn_latency", connLatency, this->_devId);
system(command);
sprintf(command, "echo %u > /sys/kernel/debug/bluetooth/hci%d/supervision_timeout", supervisionTimeout, this->_devId);
system(command);

connect(l2socket, (struct sockaddr *)&l2a, sizeof(l2a));
return true;
}
return 0;

return false;
}

NAN_METHOD(BluetoothHciSocket::New) {
Expand Down
4 changes: 1 addition & 3 deletions src/BluetoothHciSocket.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#ifndef ___BLUETOOTH_HCI_SOCKET_H___
#define ___BLUETOOTH_HCI_SOCKET_H___

#include <map>

#include <node.h>

#include <nan.h>
Expand Down Expand Up @@ -42,6 +40,7 @@ class BluetoothHciSocket : public node::ObjectWrap {
void emitErrnoError(const char *syscall);
int devIdFor(const int* devId, bool isUp);
int kernelDisconnectWorkArounds(int length, char* data);
bool kernelConnectWorkArounds(char* data, int length);

static void PollCloseCallback(uv_poll_t* handle);
static void PollCallback(uv_poll_t* handle, int status, int events);
Expand All @@ -53,7 +52,6 @@ class BluetoothHciSocket : public node::ObjectWrap {
int _socket;
int _devId;
uv_poll_t _pollHandle;
std::map<unsigned short,int> _l2sockets;
uint8_t _address[6];
uint8_t _addressType;

Expand Down