Skip to content

Commit

Permalink
Add a way to connect to linux raw HCI HAL
Browse files Browse the repository at this point in the history
Tag: #refactor
Bug: 181590011
Test: cert/run
Change-Id: I5308fd604b091392b9a8b15154732afeb03dde57
  • Loading branch information
Hansong Zhang committed Mar 8, 2021
1 parent c98b5a6 commit 6453e4a
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 34 deletions.
4 changes: 2 additions & 2 deletions gd/Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ cc_defaults {
host: {
srcs: [
":BluetoothBtaaSources_host",
":BluetoothHalSources_hci_rootcanal",
":BluetoothHalSources_hci_host",
":BluetoothOsSources_host",
],
},
Expand Down Expand Up @@ -295,7 +295,7 @@ cc_test {
},
host: {
srcs: [
":BluetoothHalTestSources_hci_rootcanal",
":BluetoothHalTestSources_hci_host",
":BluetoothOsTestSources_host",
],
},
Expand Down
8 changes: 4 additions & 4 deletions gd/docs/architecture/style_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ Root directory under Android tree:
* hci_hal_android_hidl.cc: implementation of hci_hal.h using Android HIDL
* hci_hal_android_hidl_test.cc: unit tests for the Android HIDL
implementation
* hci_hal_host_rootcanal.cc: implementation of hci_hal.h using root-canal
emulator
* hci_hal_host_rootcanal_test.cc: unit tests for the root-canal emulator
implementation
* hci_hal_host.cc: implementation of hci_hal.h using root-canal
emulator or linux Bluetooth HCI socket
* hci_hal_host_test.cc: unit tests for the socket based HAL (root-canal)
emulator implementation
* facade.proto: gRPC automation interface definition for this layer
* facade.h/cc: an implementation of the above gRPC interface for the GD
stack
Expand Down
2 changes: 1 addition & 1 deletion gd/facade/facade_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

#include "common/init_flags.h"
#include "facade/grpc_root_server.h"
#include "hal/hci_hal_host_rootcanal.h"
#include "hal/hci_hal_host.h"
#include "hal/snoop_logger.h"
#include "os/log.h"
#include "os/parameter_provider.h"
Expand Down
8 changes: 4 additions & 4 deletions gd/hal/Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ filegroup {
}

filegroup {
name: "BluetoothHalSources_hci_rootcanal",
name: "BluetoothHalSources_hci_host",
srcs: [
"hci_hal_host_rootcanal.cc",
"hci_hal_host.cc",
],
}

Expand All @@ -36,9 +36,9 @@ filegroup {
}

filegroup {
name: "BluetoothHalTestSources_hci_rootcanal",
name: "BluetoothHalTestSources_hci_host",
srcs: [
"hci_hal_host_rootcanal_test.cc",
"hci_hal_host_test.cc",
],
}

Expand Down
196 changes: 174 additions & 22 deletions gd/hal/hci_hal_host_rootcanal.cc → gd/hal/hci_hal_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
* limitations under the License.
*/

#include "hal/hci_hal_host_rootcanal.h"
#include "hal/hci_hal_host.h"

#include <netdb.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
Expand Down Expand Up @@ -48,7 +49,160 @@ constexpr uint8_t kHciEvtHeaderSize = 2;
constexpr uint8_t kHciIsoHeaderSize = 4;
constexpr int kBufSize = 1024 + 4 + 1; // DeviceProperties::acl_data_packet_size_ + ACL header + H4 header

int ConnectToRootCanal(const std::string& server, int port) {
#ifdef USE_LINUX_HCI_SOCKET
constexpr uint8_t BTPROTO_HCI = 1;
constexpr uint16_t HCI_CHANNEL_USER = 1;
constexpr uint16_t HCI_CHANNEL_CONTROL = 3;
constexpr uint16_t HCI_DEV_NONE = 0xffff;

/* reference from <kernel>/include/net/bluetooth/mgmt.h */
#define MGMT_OP_INDEX_LIST 0x0003
#define MGMT_EV_INDEX_ADDED 0x0004
#define MGMT_EV_COMMAND_COMP 0x0001
#define MGMT_EV_SIZE_MAX 1024
#define WRITE_NO_INTR(fn) \
do { \
} while ((fn) == -1 && errno == EINTR)

struct sockaddr_hci {
sa_family_t hci_family;
unsigned short hci_dev;
unsigned short hci_channel;
};

struct mgmt_pkt {
uint16_t opcode;
uint16_t index;
uint16_t len;
uint8_t data[MGMT_EV_SIZE_MAX];
} __attribute__((packed));

struct mgmt_event_read_index {
uint16_t cc_opcode;
uint8_t status;
uint16_t num_intf;
uint16_t index[0];
} __attribute__((packed));

int waitHciDev(int hci_interface) {
struct sockaddr_hci addr;
struct pollfd fds[1];
struct mgmt_pkt ev;
int fd;
int ret = 0;

fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
if (fd < 0) {
LOG_ERROR("Bluetooth socket error: %s", strerror(errno));
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.hci_family = AF_BLUETOOTH;
addr.hci_dev = HCI_DEV_NONE;
addr.hci_channel = HCI_CHANNEL_CONTROL;
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
LOG_ERROR("HCI Channel Control: %s", strerror(errno));
close(fd);
return -1;
}

fds[0].fd = fd;
fds[0].events = POLLIN;

/* Read Controller Index List Command */
ev.opcode = MGMT_OP_INDEX_LIST;
ev.index = HCI_DEV_NONE;
ev.len = 0;

ssize_t wrote;
WRITE_NO_INTR(wrote = write(fd, &ev, 6));
if (wrote != 6) {
LOG_ERROR("Unable to write mgmt command: %s", strerror(errno));
close(fd);
return -1;
}
/* validate mentioned hci interface is present and registered with sock system */
while (1) {
int n;
WRITE_NO_INTR(n = poll(fds, 1, -1));
if (n == -1) {
LOG_ERROR("Poll error: %s", strerror(errno));
ret = -1;
break;
} else if (n == 0) {
LOG_ERROR("Timeout, no HCI device detected");
ret = -1;
break;
}

if (fds[0].revents & POLLIN) {
WRITE_NO_INTR(n = read(fd, &ev, sizeof(struct mgmt_pkt)));
if (n < 0) {
LOG_ERROR("Error reading control channel: %s", strerror(errno));
ret = -1;
break;
}

if (ev.opcode == MGMT_EV_INDEX_ADDED && ev.index == hci_interface) {
close(fd);
return -1;
} else if (ev.opcode == MGMT_EV_COMMAND_COMP) {
struct mgmt_event_read_index* cc;
int i;

cc = (struct mgmt_event_read_index*)ev.data;

if (cc->cc_opcode != MGMT_OP_INDEX_LIST || cc->status != 0) continue;

for (i = 0; i < cc->num_intf; i++) {
if (cc->index[i] == hci_interface) {
close(fd);
return 0;
}
}
}
}
}

close(fd);
return -1;
}

// Connect to Linux HCI socket
int ConnectToSocket() {
int socket_fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
if (socket_fd < 1) {
LOG_ERROR("can't create socket: %s", strerror(errno));
return INVALID_FD;
}

int hci_interface = 0; // Assume we only have HCI 0

if (waitHciDev(hci_interface) != 0) {
::close(socket_fd);
return INVALID_FD;
}

struct sockaddr_hci addr;
memset(&addr, 0, sizeof(addr));
addr.hci_family = AF_BLUETOOTH;
addr.hci_dev = hci_interface;
addr.hci_channel = HCI_CHANNEL_USER;
if (bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
LOG_ERROR("HCI Channel Control: %s", strerror(errno));
::close(socket_fd);
return INVALID_FD;
}
LOG_INFO("HCI device ready");
return socket_fd;
}
#else
// Connect to root canal socket
int ConnectToSocket() {
auto* config = bluetooth::hal::HciHalHostRootcanalConfig::Get();
const std::string& server = config->GetServerAddress();
int port = config->GetPort();

int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd < 1) {
LOG_ERROR("can't create socket: %s", strerror(errno));
Expand Down Expand Up @@ -85,12 +239,13 @@ int ConnectToRootCanal(const std::string& server, int port) {
}
return socket_fd;
}
#endif
} // namespace

namespace bluetooth {
namespace hal {

class HciHalHostRootcanal : public HciHal {
class HciHalHost : public HciHal {
public:
void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
std::lock_guard<std::mutex> lock(api_mutex_);
Expand Down Expand Up @@ -119,7 +274,7 @@ class HciHalHostRootcanal : public HciHal {
std::vector<uint8_t> packet = std::move(command);
btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
packet.insert(packet.cbegin(), kH4Command);
write_to_rootcanal_fd(packet);
write_to_fd(packet);
}

void sendAclData(HciPacket data) override {
Expand All @@ -128,7 +283,7 @@ class HciHalHostRootcanal : public HciHal {
std::vector<uint8_t> packet = std::move(data);
btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
packet.insert(packet.cbegin(), kH4Acl);
write_to_rootcanal_fd(packet);
write_to_fd(packet);
}

void sendScoData(HciPacket data) override {
Expand All @@ -137,7 +292,7 @@ class HciHalHostRootcanal : public HciHal {
std::vector<uint8_t> packet = std::move(data);
btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
packet.insert(packet.cbegin(), kH4Sco);
write_to_rootcanal_fd(packet);
write_to_fd(packet);
}

void sendIsoData(HciPacket data) override {
Expand All @@ -146,7 +301,7 @@ class HciHalHostRootcanal : public HciHal {
std::vector<uint8_t> packet = std::move(data);
btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ISO);
packet.insert(packet.cbegin(), kH4Iso);
write_to_rootcanal_fd(packet);
write_to_fd(packet);
}

protected:
Expand All @@ -157,25 +312,23 @@ class HciHalHostRootcanal : public HciHal {
void Start() override {
std::lock_guard<std::mutex> lock(api_mutex_);
ASSERT(sock_fd_ == INVALID_FD);
sock_fd_ = ConnectToRootCanal(config_->GetServerAddress(), config_->GetPort());
sock_fd_ = ConnectToSocket();
ASSERT(sock_fd_ != INVALID_FD);
reactable_ = hci_incoming_thread_.GetReactor()->Register(
sock_fd_,
common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
common::Closure());
sock_fd_, common::Bind(&HciHalHost::incoming_packet_received, common::Unretained(this)), common::Closure());
btsnoop_logger_ = GetDependency<SnoopLogger>();
LOG_INFO("Rootcanal HAL opened successfully");
LOG_INFO("HAL opened successfully");
}

void Stop() override {
std::lock_guard<std::mutex> lock(api_mutex_);
LOG_INFO("Rootcanal HAL is closing");
LOG_INFO("HAL is closing");
if (reactable_ != nullptr) {
hci_incoming_thread_.GetReactor()->Unregister(reactable_);
LOG_INFO("Rootcanal HAL is stopping, start waiting for last callback");
LOG_INFO("HAL is stopping, start waiting for last callback");
// Wait up to 1 second for the last incoming packet callback to finish
hci_incoming_thread_.GetReactor()->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
LOG_INFO("Rootcanal HAL is stopping, finished waiting for last callback");
LOG_INFO("HAL is stopping, finished waiting for last callback");
ASSERT(sock_fd_ != INVALID_FD);
}
reactable_ = nullptr;
Expand All @@ -185,13 +338,12 @@ class HciHalHostRootcanal : public HciHal {
}
::close(sock_fd_);
sock_fd_ = INVALID_FD;
LOG_INFO("Rootcanal HAL is closed");
LOG_INFO("HAL is closed");
}

private:
// Held when APIs are called, NOT to be held during callbacks
std::mutex api_mutex_;
HciHalHostRootcanalConfig* config_ = HciHalHostRootcanalConfig::Get();
HciHalCallbacks* incoming_packet_callback_ = nullptr;
std::mutex incoming_packet_callback_mutex_;
int sock_fd_ = INVALID_FD;
Expand All @@ -201,14 +353,14 @@ class HciHalHostRootcanal : public HciHal {
std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
SnoopLogger* btsnoop_logger_ = nullptr;

void write_to_rootcanal_fd(HciPacket packet) {
void write_to_fd(HciPacket packet) {
// TODO: replace this with new queue when it's ready
hci_outgoing_queue_.emplace(packet);
if (hci_outgoing_queue_.size() == 1) {
hci_incoming_thread_.GetReactor()->ModifyRegistration(
reactable_,
common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
common::Bind(&HciHalHostRootcanal::send_packet_ready, common::Unretained(this)));
common::Bind(&HciHalHost::incoming_packet_received, common::Unretained(this)),
common::Bind(&HciHalHost::send_packet_ready, common::Unretained(this)));
}
}

Expand All @@ -223,7 +375,7 @@ class HciHalHostRootcanal : public HciHal {
if (hci_outgoing_queue_.empty()) {
this->hci_incoming_thread_.GetReactor()->ModifyRegistration(
this->reactable_,
common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
common::Bind(&HciHalHost::incoming_packet_received, common::Unretained(this)),
common::Closure());
}
}
Expand Down Expand Up @@ -356,7 +508,7 @@ class HciHalHostRootcanal : public HciHal {
}
};

const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHostRootcanal(); });
const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHost(); });

} // namespace hal
} // namespace bluetooth
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

#include "hal/hci_hal_host_rootcanal.h"
#include "hal/hci_hal_host.h"

#include <fcntl.h>
#include <gtest/gtest.h>
Expand Down

0 comments on commit 6453e4a

Please sign in to comment.