blob: 8c7b80d67851adfdf88c3f8610a2bcd29583614e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "fake_gatt_server.h"
#include <endian.h>
#include <lib/fit/function.h>
#include <zircon/assert.h>
#include "fake_controller.h"
#include "fake_peer.h"
#include "src/connectivity/bluetooth/core/bt-host/att/packet.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/gatt_defs.h"
namespace bt::testing {
FakeGattServer::FakeGattServer(FakePeer* dev) : dev_(dev) { ZX_DEBUG_ASSERT(dev_); }
void FakeGattServer::HandlePdu(hci::ConnectionHandle conn, const ByteBuffer& pdu) {
if (pdu.size() < sizeof(att::OpCode)) {
bt_log(WARN, "fake-hci", "malformed ATT packet!");
return;
}
att::OpCode opcode = le16toh(pdu.As<att::OpCode>());
switch (opcode) {
case att::kExchangeMTURequest:
// Always reply back with the default ATT_MTU.
Send(conn, CreateStaticByteBuffer(att::kExchangeMTUResponse, att::kLEMinMTU, 0x00));
break;
case att::kReadByGroupTypeRequest:
HandleReadByGrpType(conn, pdu.view(sizeof(att::OpCode)));
break;
default:
SendErrorRsp(conn, opcode, 0, att::ErrorCode::kRequestNotSupported);
break;
}
}
void FakeGattServer::RegisterWithL2cap(FakeL2cap* l2cap_) {
auto cb = fit::bind_member(this, &FakeGattServer::HandlePdu);
l2cap_->RegisterHandler(l2cap::kATTChannelId, cb);
return;
}
void FakeGattServer::HandleReadByGrpType(hci::ConnectionHandle conn, const ByteBuffer& bytes) {
// Don't support 128-bit group types.
if (bytes.size() != sizeof(att::ReadByGroupTypeRequestParams16)) {
SendErrorRsp(conn, att::kReadByGroupTypeRequest, 0, att::ErrorCode::kInvalidPDU);
return;
}
const auto& params = bytes.As<att::ReadByGroupTypeRequestParams16>();
att::Handle start = le16toh(params.start_handle);
att::Handle end = le16toh(params.end_handle);
if (!start || end < start) {
SendErrorRsp(conn, att::kReadByGroupTypeRequest, start, att::ErrorCode::kInvalidHandle);
return;
}
// Only support primary service discovery.
uint16_t grp_type = le16toh(params.type);
if (grp_type != gatt::types::kPrimaryService16 || start > 1) {
SendErrorRsp(conn, att::kReadByGroupTypeRequest, start, att::ErrorCode::kAttributeNotFound);
return;
}
// We report back the standard services.
// TODO(armansito): Support standard characteristics and more services.
auto rsp = CreateStaticByteBuffer(att::kReadByGroupTypeResponse, // opcode
6, // entry length
0x01, 0x00, // start handle: 1
0x01, 0x00, // end handle: 1
0x00, 0x18, // "GAP Service" UUID: 0x1800
0x02, 0x00, // start handle: 2
0x02, 0x00, // end handle: 2
0x01, 0x18 // "GATT Service" UUID: 0x1801
);
Send(conn, rsp);
}
void FakeGattServer::Send(hci::ConnectionHandle conn, const ByteBuffer& pdu) {
if (dev_->ctrl()) {
dev_->ctrl()->SendL2CAPBFrame(conn, l2cap::kATTChannelId, pdu);
} else {
bt_log(WARN, "fake-hci", "no assigned FakeController!");
}
}
void FakeGattServer::SendErrorRsp(hci::ConnectionHandle conn, att::OpCode opcode,
att::Handle handle, att::ErrorCode ecode) {
StaticByteBuffer<sizeof(att::ErrorResponseParams) + sizeof(att::OpCode)> buffer;
att::PacketWriter writer(att::kErrorResponse, &buffer);
auto* params = writer.mutable_payload<att::ErrorResponseParams>();
params->request_opcode = htole16(opcode);
params->attribute_handle = htole16(handle);
params->error_code = ecode;
Send(conn, buffer);
}
} // namespace bt::testing