Skip to content

Commit

Permalink
feat: refactored entire hci native module
Browse files Browse the repository at this point in the history
  • Loading branch information
stoprocent committed Sep 22, 2024
1 parent 5eb6d67 commit 805d9e2
Show file tree
Hide file tree
Showing 13 changed files with 4,267 additions and 5,665 deletions.
47 changes: 39 additions & 8 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,50 @@
'variables': {
'openssl_fips' : ''
},
'targets': [
"targets": [
{
'target_name': 'bluetooth_hci_socket',
"target_name": "bluetooth_hci_socket",
'defines': [
'NAPI_CPP_EXCEPTIONS=1'
],
'sources': [],
'include_dirs': [
"<!@(node -p \"require('node-addon-api').include\")",
"include"
],
'dependencies': [
"<!(node -p \"require('node-addon-api').gyp\")"
],
'cflags!': [ '-fno-exceptions', '-std=c99' ],
'cflags_cc!': [ '-fno-exceptions', '-std=c++20' ],
'xcode_settings': {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'CLANG_CXX_LIBRARY': 'libc++',
'CLANG_CXX_LANGUAGE_STANDARD': 'c++20',
'MACOSX_DEPLOYMENT_TARGET': '12'
},
'conditions': [
['OS=="linux" or OS=="android" or OS=="freebsd"', {
'sources': [
'src/BluetoothHciSocket.cpp'
"sources": [
"<!@(node -p \"require('fs').readdirSync('src').map(f=>'src/'+f).join(' ')\")"
]
}],
['OS=="win"', {
'defines': [
'_Static_assert=static_assert'
],
'msvs_settings': {
'VCCLCompilerTool': {
'AdditionalOptions': [ '-std:c++20', ],
'ExceptionHandling': 1
}
}
}, { # OS != "win",
'defines': [
'restrict=__restrict'
],
}]
],
"include_dirs" : [
"<!(node -e \"require('nan')\")"
]
}
]
}
}
4 changes: 2 additions & 2 deletions examples/le-advertisement-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,6 @@ console.log('isDevUp = ' + bluetoothHciSocket.isDevUp());

setAdvertiseEnable(false);
setAdvertisingParameter();
setScanResponseData(Buffer.alloc('0909657374696d6f74650e160a182eb8855fb5ddb601000200', 'hex'));
setAdvertisingData(Buffer.alloc('0201061aff4c000215b9407f30f5f8466eaff925556b57fe6d00010002b6', 'hex'));
setScanResponseData(Buffer.from('0909657374696d6f74650e160a182eb8855fb5ddb601000200', 'hex'));
setAdvertisingData(Buffer.from('0201061aff4c000215b9407f30f5f8466eaff925556b57fe6d00010002b6', 'hex'));
setAdvertiseEnable(true);
4 changes: 2 additions & 2 deletions examples/le-connection-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ bluetoothHciSocket.on('data', function (data) {
console.log('\t' + latency);
console.log('\t' + supervisionTimeout * 10);

writeHandle(handle, Buffer.alloc('020001', 'hex'));
writeHandle(handle, Buffer.from('020001', 'hex'));
}
}
} else if (data.readUInt8(0) === HCI_ACLDATA_PKT) {
Expand Down Expand Up @@ -143,7 +143,7 @@ function createConnection (address, addressType) {
cmd.writeUInt8(0x00, 8); // initiator filter

cmd.writeUInt8(addressType === 'random' ? 0x01 : 0x00, 9); // peer address type
(Buffer.alloc(address.split(':').reverse().join(''), 'hex')).copy(cmd, 10); // peer address
(Buffer.from(address.split(':').reverse().join(''), 'hex')).copy(cmd, 10); // peer address

cmd.writeUInt8(0x00, 16); // own address type

Expand Down
59 changes: 59 additions & 0 deletions include/BluetoothHciL2Socket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#ifndef BLUETOOTH_HCI_L2_SOCKET_H
#define BLUETOOTH_HCI_L2_SOCKET_H

// Forward declaration of BluetoothHciSocket
class BluetoothHciSocket;

// Include necessary headers
#include "BluetoothStructs.h"

// Bluetooth HCI L2CAP Socket class
class BluetoothHciL2Socket {
public:
/**
* @brief Constructor for BluetoothHciL2Socket.
* @param parent Pointer to the parent Bluetooth HCI socket.
* @param bdaddr_src Source Bluetooth device address.
* @param src_type Source address type (public or random).
* @param bdaddr_dst Destination Bluetooth device address.
* @param dst_type Destination address type (public or random).
* @param expires Expiration time in milliseconds.
*/
BluetoothHciL2Socket(BluetoothHciSocket* parent,
const bdaddr_t* bdaddr_src,
uint8_t src_type,
const bdaddr_t* bdaddr_dst,
uint8_t dst_type,
uint64_t expires);

/// Destructor
~BluetoothHciL2Socket();

/// Connects to the remote device.
void connect();

/// Disconnects the socket.
void disconnect();

/// Sets the expiration time.
void setExpires(uint64_t expires);

/// Retrieves the expiration time.
uint64_t getExpires() const;

/// Checks if the socket is connected.
bool isConnected() const;

private:
int _socket; ///< Socket file descriptor.
BluetoothHciSocket* _parent; ///< Pointer to the parent HCI socket.
uint64_t _expires; ///< Expiration time in milliseconds, or 0 if connected.
struct sockaddr_l2 _l2_src; ///< Source L2CAP address.
struct sockaddr_l2 _l2_dst; ///< Destination L2CAP address.

// Disable copy constructor and assignment operator
BluetoothHciL2Socket(const BluetoothHciL2Socket&) = delete;
BluetoothHciL2Socket& operator=(const BluetoothHciL2Socket&) = delete;
};

#endif // BLUETOOTH_HCI_L2_SOCKET_H
176 changes: 176 additions & 0 deletions include/BluetoothHciSocket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#ifndef BLUETOOTH_HCI_SOCKET_H
#define BLUETOOTH_HCI_SOCKET_H

// Include necessary headers
#include <napi.h> // N-API for Node.js addons

#include <atomic> // For std::atomic
#include <thread> // For std::thread
#include <map> // For std::map
#include <memory> // For smart pointers
#include "BluetoothHciL2Socket.h" // Header for BluetoothHciL2Socket class

/**
* @brief Class representing a Bluetooth HCI (Host Controller Interface) socket.
*
* This class interfaces with the Bluetooth hardware using HCI sockets
* and integrates with Node.js using N-API for addon development.
*/
class BluetoothHciSocket : public Napi::ObjectWrap<BluetoothHciSocket> {
friend class BluetoothHciL2Socket; ///< Grant access to BluetoothHciL2Socket class

public:
/**
* @brief Initializes the BluetoothHciSocket class and sets up exports to Node.js.
* @param env The N-API environment.
* @param exports The exports object to which the class is added.
* @return The modified exports object.
*/
static Napi::Object Init(Napi::Env env, Napi::Object exports);

/**
* @brief Constructor for BluetoothHciSocket.
* @param info Callback information from N-API.
*/
BluetoothHciSocket(const Napi::CallbackInfo& info);

/// Destructor
~BluetoothHciSocket();

// Binding methods to interface with HCI socket
/**
* @brief Binds the socket in raw mode.
* @param info Callback information from N-API.
* @return Napi::Value indicating success or failure.
*/
Napi::Value BindRaw(const Napi::CallbackInfo& info);

/**
* @brief Binds the socket in user mode.
* @param info Callback information from N-API.
* @return Napi::Value indicating success or failure.
*/
Napi::Value BindUser(const Napi::CallbackInfo& info);

/**
* @brief Binds the socket in control mode.
* @param info Callback information from N-API.
*/
void BindControl(const Napi::CallbackInfo& info);

// Device methods
/**
* @brief Checks if the Bluetooth device is up.
* @param info Callback information from N-API.
* @return Napi::Value indicating the device status.
*/
Napi::Value IsDevUp(const Napi::CallbackInfo& info);

/**
* @brief Retrieves the list of Bluetooth devices.
* @param info Callback information from N-API.
* @return Napi::Value containing the device list.
*/
Napi::Value GetDeviceList(const Napi::CallbackInfo& info);

// Configuration methods
/**
* @brief Sets the HCI filter for the socket.
* @param info Callback information from N-API.
*/
void SetFilter(const Napi::CallbackInfo& info);

// Control methods
/**
* @brief Starts the socket for communication.
* @param info Callback information from N-API.
*/
void Start(const Napi::CallbackInfo& info);

/**
* @brief Stops the socket communication.
* @param info Callback information from N-API.
*/
void Stop(const Napi::CallbackInfo& info);

/**
* @brief Writes data to the socket.
* @param info Callback information from N-API.
*/
void Write(const Napi::CallbackInfo& info);

/**
* @brief Cleans up resources used by the socket.
* @param info Callback information from N-API.
*/
void Cleanup(const Napi::CallbackInfo& info);

private:
/**
* @brief Polls the socket for events in a separate thread.
*/
void PollSocket();

/**
* @brief Emits an error event based on errno.
* @param info Callback information from N-API.
* @param syscall Name of the system call that failed.
*/
void EmitError(const Napi::CallbackInfo& info, const char* syscall);

/**
* @brief Retrieves the device ID for a given device.
* @param devId Pointer to the device ID.
* @param isUp Whether to check if the device is up.
* @return Device ID or -1 on failure.
*/
int devIdFor(const int* devId, bool isUp);

/**
* @brief Workaround for kernel disconnect issues.
* @param length Length of the data.
* @param data Pointer to the data buffer.
* @return Result code.
*/
int kernelDisconnectWorkArounds(int length, char* data);

/**
* @brief Workaround for kernel connect issues.
* @param data Pointer to the data buffer.
* @param length Length of the data.
* @return True if handled, false otherwise.
*/
bool kernelConnectWorkArounds(char* data, int length);

/**
* @brief Sets connection parameters for Bluetooth LE connections.
* @param connMinInterval Minimum connection interval.
* @param connMaxInterval Maximum connection interval.
* @param connLatency Connection latency.
* @param supervisionTimeout Supervision timeout.
*/
void setConnectionParameters(uint16_t connMinInterval, uint16_t connMaxInterval, uint16_t connLatency, uint16_t supervisionTimeout);

// N-API thread-safe function and object reference
Napi::ThreadSafeFunction tsfn; ///< Thread-safe function for callbacks
Napi::ObjectReference thisObj; ///< Reference to the JavaScript object

// Threading and synchronization
std::atomic<bool> stopFlag; ///< Atomic flag to signal the polling thread to stop
std::thread pollingThread; ///< Thread for polling the socket

// Internal state
int _mode; ///< Operating mode of the socket
int _socket; ///< File descriptor for the socket
int _devId; ///< Device ID

uint8_t _address[6]; ///< Local Bluetooth device address
uint8_t _addressType; ///< Address type (public or random)

// Maps to manage connected and connecting L2CAP sockets
std::map<bdaddr_t, std::weak_ptr<BluetoothHciL2Socket>> _l2sockets_connected; ///< Connected L2CAP sockets
std::map<bdaddr_t, std::shared_ptr<BluetoothHciL2Socket>> _l2sockets_connecting; ///< Connecting L2CAP sockets
std::map<uint16_t, std::shared_ptr<BluetoothHciL2Socket>> _l2sockets_handles; ///< L2CAP sockets by handle
};

#endif // BLUETOOTH_HCI_SOCKET_H
Loading

0 comments on commit 805d9e2

Please sign in to comment.