Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9efbac20 authored by Myles Watson's avatar Myles Watson Committed by Gerrit Code Review
Browse files

Merge "btlinux: Parse each HCI packet"

parents 7a30c68e e7a61272
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -66,3 +66,32 @@ cc_library_static {
        ".",
    ],
}

cc_test_host {
    name: "bluetooth-btlinux-hci-test",
    srcs: [
        "async_fd_watcher.cc",
        "hci_packetizer.cc",
        "h4_protocol.cc",
        "h4_protocol_unittest.cc",
    ],
    cflags: [
        "-Wall",
        "-Werror",
        "-DNO_THREAD_PRIORITY",
    ],
    shared_libs: [
        "libbase",
        "libhidlbase",
        "liblog",
        "libutils",
    ],
    static_libs: [
        "libgmock",
    ],
    sanitize: {
        address: true,
        cfi: true,
    },
    test_suites: ["general-tests"],
}
+4 −1
Original line number Diff line number Diff line
@@ -29,7 +29,9 @@
#include "unistd.h"

static const int INVALID_FD = -1;
#ifndef NO_THREAD_PRIORITY
static const int BT_RT_PRIORITY = 1;
#endif

namespace android {
namespace hardware {
@@ -117,7 +119,7 @@ int AsyncFdWatcher::notifyThread() {
}

void AsyncFdWatcher::ThreadRoutine() {

#ifndef NO_THREAD_PRIORITY
  // Make watching thread RT.
  struct sched_param rt_params;
  rt_params.sched_priority = BT_RT_PRIORITY;
@@ -125,6 +127,7 @@ void AsyncFdWatcher::ThreadRoutine() {
    ALOGE("%s unable to set SCHED_FIFO for pid %d, tid %d, error %s", __func__,
          getpid(), gettid(), strerror(errno));
  }
#endif

  while (running_) {
    fd_set read_fds;
+2 −1
Original line number Diff line number Diff line
@@ -306,7 +306,8 @@ Return<void> BluetoothHci::initialize_impl(
      [cb](const hidl_vec<uint8_t>& packet) { cb->scoDataReceived(packet); },
      [cb_1_1](const hidl_vec<uint8_t>& packet) {
        cb_1_1->isoDataReceived(packet);
      });
      },
      []() { ALOGE("UART disconnected."); });

  fd_watcher_.WatchFdForNonBlockingReads(
          hci_fd, [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
+57 −37
Original line number Diff line number Diff line
@@ -28,6 +28,17 @@ namespace hardware {
namespace bluetooth {
namespace hci {

H4Protocol::H4Protocol(int fd, PacketReadCallback event_cb,
                       PacketReadCallback acl_cb, PacketReadCallback sco_cb,
                       PacketReadCallback iso_cb,
                       OnDisconnectCallback disconnect_cb)
    : uart_fd_(fd),
      event_cb_(std::move(event_cb)),
      acl_cb_(std::move(acl_cb)),
      sco_cb_(std::move(sco_cb)),
      iso_cb_(std::move(iso_cb)),
      disconnect_cb_(std::move(disconnect_cb)) {}

size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) {
    /* For HCI communication over USB dongle, multiple write results in
     * response timeout as driver expect type + data at once to process
@@ -56,59 +67,68 @@ size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) {
    return ret;
}

void H4Protocol::OnPacketReady() {
size_t H4Protocol::on_packet_ready(const hidl_vec<uint8_t>& packet) {
  switch (hci_packet_type_) {
    case HCI_PACKET_TYPE_EVENT:
      event_cb_(hci_packetizer_.GetPacket());
      event_cb_(packet);
      break;
    case HCI_PACKET_TYPE_ACL_DATA:
      acl_cb_(hci_packetizer_.GetPacket());
      acl_cb_(packet);
      break;
    case HCI_PACKET_TYPE_SCO_DATA:
      sco_cb_(hci_packetizer_.GetPacket());
      sco_cb_(packet);
      break;
    case HCI_PACKET_TYPE_ISO_DATA:
      iso_cb_(hci_packetizer_.GetPacket());
      iso_cb_(packet);
      break;
    default: {
      bool bad_packet_type = true;
      CHECK(!bad_packet_type);
      LOG_ALWAYS_FATAL("Unhandled packet of type 0x%x", hci_packet_type_);
    }
  }
  return packet.size();
}

void H4Protocol::send_data_to_packetizer(uint8_t* buffer, size_t length) {
  std::vector<uint8_t> input_buffer{buffer, buffer + length};
  size_t buffer_offset = 0;
  while (buffer_offset < input_buffer.size()) {
    if (hci_packet_type_ == HCI_PACKET_TYPE_UNKNOWN) {
      hci_packet_type_ =
          static_cast<HciPacketType>(input_buffer.data()[buffer_offset]);
      buffer_offset += 1;
    } else {
      bool packet_ready = hci_packetizer_.OnDataReady(
          hci_packet_type_, input_buffer, buffer_offset);
      if (packet_ready) {
        // Call packet callback and move offset.
        buffer_offset += on_packet_ready(hci_packetizer_.GetPacket());
        // Get ready for the next type byte.
        hci_packet_type_ = HCI_PACKET_TYPE_UNKNOWN;
      } else {
        // The data was consumed, but there wasn't a packet.
        buffer_offset = input_buffer.size();
      }
    }
  }
}

void H4Protocol::OnDataReady(int fd) {
    if (hci_packet_type_ == HCI_PACKET_TYPE_UNKNOWN) {
        /**
         * read full buffer. ACL max length is 2 bytes, and SCO max length is 2
         * byte. so taking 64K as buffer length.
         * Question : Why to read in single chunk rather than multiple reads,
         * which can give parameter length arriving in response ?
         * Answer: The multiple reads does not work with BT USB dongle. At least
         * with Bluetooth 2.0 supported USB dongle. After first read, either
         * firmware/kernel (do not know who is responsible - inputs ??) driver
         * discard the whole message and successive read results in forever
         * blocking loop. - Is there any other way to make it work with multiple
         * reads, do not know yet (it can eliminate need of this function) ?
         * Reading in single shot gives expected response.
         */
        const size_t max_plen = 64*1024;
        hidl_vec<uint8_t> tpkt;
        tpkt.resize(max_plen);
        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, tpkt.data(), max_plen));
  if (disconnected_) {
    return;
  }
  uint8_t buffer[kMaxPacketLength];
  ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, kMaxPacketLength));
  if (bytes_read == 0) {
            ALOGI("No bytes read");
    ALOGI("No bytes read, calling the disconnect callback");
    disconnected_ = true;
    disconnect_cb_();
    return;
  }
  if (bytes_read < 0) {
    ALOGW("error reading from UART (%s)", strerror(errno));
    return;
  }
        hci_packet_type_ = static_cast<HciPacketType>(tpkt.data()[0]);
        hci_packetizer_.CbHciPacket(tpkt.data()+1, bytes_read-1);
    }
  send_data_to_packetizer(buffer, bytes_read);
}

}  // namespace hci
+18 −9
Original line number Diff line number Diff line
@@ -29,34 +29,43 @@ namespace hci {

using ::android::hardware::hidl_vec;
using PacketReadCallback = std::function<void(const hidl_vec<uint8_t>&)>;
using OnDisconnectCallback = std::function<void()>;

class H4Protocol {
 public:
  H4Protocol(int fd, PacketReadCallback event_cb, PacketReadCallback acl_cb,
             PacketReadCallback sco_cb, PacketReadCallback iso_cb)
      : uart_fd_(fd),
        event_cb_(event_cb),
        acl_cb_(acl_cb),
        sco_cb_(sco_cb),
        iso_cb_(iso_cb),
        hci_packetizer_([this]() { OnPacketReady(); }) {}
             PacketReadCallback sco_cb, PacketReadCallback iso_cb,
             OnDisconnectCallback disconnect_cb);

  size_t Send(uint8_t type, const uint8_t* data, size_t length);
  virtual ~H4Protocol() {}

  void OnPacketReady();
  size_t Send(uint8_t type, const uint8_t* data, size_t length);

  void OnDataReady(int fd);

 private:
  int uart_fd_;
  bool disconnected_{false};

  size_t on_packet_ready(const hidl_vec<uint8_t>& packet);
  void send_data_to_packetizer(uint8_t* buffer, size_t length);

  PacketReadCallback event_cb_;
  PacketReadCallback acl_cb_;
  PacketReadCallback sco_cb_;
  PacketReadCallback iso_cb_;
  OnDisconnectCallback disconnect_cb_;

  HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN};
  HciPacketizer hci_packetizer_;

  /**
   * Question : Why read in single chunk rather than multiple reads?
   * Answer: Using multiple reads does not work with some BT USB dongles.
   * Reading in single shot gives expected response.
   * ACL max length is 2 bytes, so using 64K as the buffer length.
   */
  static constexpr size_t kMaxPacketLength = 64 * 1024;
};

}  // namespace hci
Loading