return pending_sco_events_.RunLoop(context, writer);
};
+ ::grpc::Status FetchHciIso(
+ ::grpc::ServerContext* context,
+ const ::google::protobuf::Empty* request,
+ ::grpc::ServerWriter<HciIsoPacket>* writer) override {
+ return pending_iso_events_.RunLoop(context, writer);
+ };
+
void hciEventReceived(bluetooth::hal::HciPacket event) override {
{
HciEventPacket response;
pending_sco_events_.OnIncomingEvent(std::move(response));
}
+ void isoDataReceived(bluetooth::hal::HciPacket data) override {
+ HciIsoPacket response;
+ response.set_payload(std::string(data.begin(), data.end()));
+ pending_iso_events_.OnIncomingEvent(std::move(response));
+ }
+
private:
HciHal* hal_;
bool can_send_hci_command_ = true;
::bluetooth::grpc::GrpcEventQueue<HciEventPacket> pending_hci_events_{"FetchHciEvent"};
::bluetooth::grpc::GrpcEventQueue<HciAclPacket> pending_acl_events_{"FetchHciAcl"};
::bluetooth::grpc::GrpcEventQueue<HciScoPacket> pending_sco_events_{"FetchHciSco"};
+ ::bluetooth::grpc::GrpcEventQueue<HciIsoPacket> pending_iso_events_{"FetchHciIso"};
};
void HciHalFacadeModule::ListDependencies(ModuleList* list) {
rpc SendHciCommand(HciCommandPacket) returns (google.protobuf.Empty) {}
rpc SendHciAcl(HciAclPacket) returns (google.protobuf.Empty) {}
rpc SendHciSco(HciScoPacket) returns (google.protobuf.Empty) {}
+ rpc SendHciIso(HciIsoPacket) returns (google.protobuf.Empty) {}
rpc FetchHciEvent(google.protobuf.Empty) returns (stream HciEventPacket) {}
rpc FetchHciAcl(google.protobuf.Empty) returns (stream HciAclPacket) {}
rpc FetchHciSco(google.protobuf.Empty) returns (stream HciScoPacket) {}
+ rpc FetchHciIso(google.protobuf.Empty) returns (stream HciIsoPacket) {}
}
message HciEventPacket {
message HciScoPacket {
bytes payload = 1;
}
+
+message HciIsoPacket {
+ bytes payload = 1;
+}
// @param event is the HCI event to be sent to the Bluetooth stack
virtual void hciEventReceived(HciPacket event) = 0;
- // Send an ACL data packet form the controller to the host
+ // Send an ACL data packet from the controller to the host
// @param data the ACL HCI packet to be passed to the host stack
virtual void aclDataReceived(HciPacket data) = 0;
- // Send a SCO data packet form the controller to the host
+ // Send a SCO data packet from the controller to the host
// @param data the SCO HCI packet to be passed to the host stack
virtual void scoDataReceived(HciPacket data) = 0;
+
+ // Send an ISO data packet from the controller to the host
+ // @param data the ISO HCI packet to be passed to the host stack
+ virtual void isoDataReceived(HciPacket data) = 0;
};
// Mirrors hardware/interfaces/bluetooth/1.0/IBluetoothHci.hal in Android
constexpr uint8_t kH4Acl = 0x02;
constexpr uint8_t kH4Sco = 0x03;
constexpr uint8_t kH4Event = 0x04;
+constexpr uint8_t kH4Iso = 0x05;
constexpr uint8_t kH4HeaderSize = 1;
constexpr uint8_t kHciAclHeaderSize = 4;
constexpr uint8_t kHciScoHeaderSize = 3;
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) {
incoming_packet_callback_->scoDataReceived(receivedHciPacket);
}
}
+
+ if (buf[0] == kH4Iso) {
+ RUN_NO_INTR(received_size = recv(sock_fd_, buf + kH4HeaderSize, kHciIsoHeaderSize, 0));
+ ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+ ASSERT_LOG(received_size == kHciIsoHeaderSize, "malformed ISO header received");
+
+ uint8_t hci_iso_data_total_length = buf[3];
+ int payload_size;
+ RUN_NO_INTR(payload_size = recv(sock_fd_, buf + kH4HeaderSize + kHciIsoHeaderSize, hci_iso_data_total_length, 0));
+ ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
+ ASSERT_LOG(payload_size == hci_iso_data_total_length, "malformed ISO packet received: size mismatch");
+
+ HciPacket receivedHciPacket;
+ receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciIsoHeaderSize + payload_size);
+ btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ISO);
+ {
+ std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+ if (incoming_packet_callback_ == nullptr) {
+ LOG_INFO("Dropping a ISO packet after processing");
+ return;
+ }
+ incoming_packet_callback_->isoDataReceived(receivedHciPacket);
+ }
+ }
memset(buf, 0, kBufSize);
}
};
constexpr uint8_t kH4Acl = 0x02;
constexpr uint8_t kH4Sco = 0x03;
constexpr uint8_t kH4Event = 0x04;
+constexpr uint8_t kH4Iso = 0x05;
using H4Packet = std::vector<uint8_t>;
void scoDataReceived(HciPacket packet) override {
incoming_packets_queue_.emplace(kH4Sco, packet);
}
+
+ void isoDataReceived(HciPacket packet) override {
+ incoming_packets_queue_.emplace(kH4Iso, packet);
+ }
};
// An implementation of rootcanal desktop HCI server which listens on localhost:kListeningPort
return pkt;
}
+HciPacket make_sample_h4_iso_pkt(uint8_t payload_size) {
+ HciPacket pkt;
+ pkt.assign(1 + 4 + payload_size, 0x01);
+ pkt[0] = kH4Iso;
+ pkt[3] = payload_size;
+ pkt[4] = 0;
+ return pkt;
+}
+
size_t read_with_retry(int socket, uint8_t* data, size_t length) {
size_t bytes_read = 0;
ssize_t bytes_read_current = 0;
check_packet_equal(packet, incoming_packet);
}
+TEST_F(HciHalRootcanalTest, receive_hci_iso) {
+ H4Packet incoming_packet = make_sample_h4_iso_pkt(3);
+ write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+ while (incoming_packets_queue_.size() != 1) {
+ }
+ auto packet = incoming_packets_queue_.front();
+ incoming_packets_queue_.pop();
+ check_packet_equal(packet, incoming_packet);
+}
+
TEST_F(HciHalRootcanalTest, receive_two_hci_evts) {
H4Packet incoming_packet = make_sample_h4_evt_pkt(3);
H4Packet incoming_packet2 = make_sample_h4_evt_pkt(5);
flags.set(0, true);
flags.set(1, true);
break;
+ case PacketType::ISO:
+ flags.set(0, direction == Direction::INCOMING);
+ flags.set(1, true);
+ break;
}
uint32_t length = packet.size() + /* type byte */ 1;
PacketHeaderType header = {.length_original = htonl(length),
ACL = 2,
SCO = 3,
EVT = 4,
+ ISO = 5,
};
enum Direction {
// Not implemented yet
}
+ void isoDataReceived(hal::HciPacket data_bytes) override {
+ // Not implemented yet
+ }
+
HciLayer& module_;
};
packet BqrControllerDbgInfoEvent : BqrLogDumpEvent (quality_report_id = CONTROLLER_DBG_INFO) {
_payload_, // vendor specific parameter
}
+
+// Isochronous Adaptation Layer
+
+enum IsoPacketBoundaryFlag : 2 {
+ FIRST_FRAGMENT = 0,
+ CONTINUATION_FRAGMENT = 1,
+ COMPLETE_SDU = 2,
+ LAST_FRAGMENT = 3,
+}
+
+enum TimeStampFlag : 1 {
+ NOT_PRESENT = 0,
+ PRESENT = 1,
+}
+
+packet IsoPacket {
+ connection_handle : 12,
+ pb_flag : IsoPacketBoundaryFlag,
+ ts_flag : TimeStampFlag,
+ _reserved_ : 1,
+ _size_(_payload_) : 14,
+ _reserved_ : 2,
+ _payload_,
+}
+
+enum IsoPacketStatusFlag : 2 {
+ VALID = 0,
+ POSSIBLY_INVALID = 1,
+ LOST_DATA = 2,
+}
+
+packet IsoPacketWithTimestamp : IsoPacket (ts_flag = PRESENT) {
+ time_stamp : 32,
+ packet_sequence_number : 16,
+ _size_(_payload_) : 12, // iso_sdu_length
+ _reserved_ : 2,
+ packet_status_flag : IsoPacketStatusFlag,
+ _payload_,
+}
+
+packet IsoPacketWithoutTimestamp : IsoPacket (ts_flag = NOT_PRESENT) {
+ packet_sequence_number : 16,
+ _size_(_payload_) : 12, // iso_sdu_length
+ _reserved_ : 2,
+ packet_status_flag : IsoPacketStatusFlag,
+ _payload_,
+}