blob: 5c223ad4878791c627ffb29ec636f44687c1188f [file] [log] [blame]
// Copyright 2017 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 "util.h"
#include <endian.h>
#include <zircon/assert.h>
namespace bt::hci_spec {
std::string HCIVersionToString(HCIVersion version) {
switch (version) {
case HCIVersion::k1_0b:
return "1.0b";
case HCIVersion::k1_1:
return "1.1";
case HCIVersion::k1_2:
return "1.2";
case HCIVersion::k2_0_EDR:
return "2.0 + EDR";
case HCIVersion::k2_1_EDR:
return "2.1 + EDR";
case HCIVersion::k3_0_HS:
return "3.0 + HS";
case HCIVersion::k4_0:
return "4.0";
case HCIVersion::k4_1:
return "4.1";
case HCIVersion::k4_2:
return "4.2";
case HCIVersion::k5_0:
return "5.0";
default:
break;
}
return "(unknown)";
}
// clang-format off
std::string StatusCodeToString(hci_spec::StatusCode code) {
switch (code) {
case kSuccess: return "success";
case kUnknownCommand: return "unknown command";
case kUnknownConnectionId: return "unknown connection ID";
case kHardwareFailure: return "hardware failure";
case kPageTimeout: return "page timeout";
case kAuthenticationFailure: return "authentication failure";
case kPinOrKeyMissing: return "pin or key missing";
case kMemoryCapacityExceeded: return "memory capacity exceeded";
case kConnectionTimeout: return "connection timeout";
case kConnectionLimitExceeded: return "connection limit exceeded";
case kSynchronousConnectionLimitExceeded: return "synchronous connection limit exceeded";
case kConnectionAlreadyExists: return "connection already exists";
case kCommandDisallowed: return "command disallowed";
case kConnectionRejectedLimitedResources: return "connection rejected: limited resources";
case kConnectionRejectedSecurity: return "connection rejected: security";
case kConnectionRejectedBadBdAddr: return "connection rejected: bad BD_ADDR";
case kConnectionAcceptTimeoutExceeded: return "connection accept timeout exceeded";
case kUnsupportedFeatureOrParameter: return "unsupported feature or parameter";
case kInvalidHCICommandParameters: return "invalid HCI command parameters";
case kRemoteUserTerminatedConnection: return "remote user terminated connection";
case kRemoteDeviceTerminatedConnectionLowResources: return "remote device terminated connection: low resources";
case kRemoteDeviceTerminatedConnectionPowerOff: return "remote device terminated connection: power off";
case kConnectionTerminatedByLocalHost: return "connection terminated by local host";
case kRepeatedAttempts: return "repeated attempts";
case kPairingNotAllowed: return "pairing not allowed";
case kUnknownLMPPDU: return "unknown LMP PDU";
case kUnsupportedRemoteFeature: return "unsupported remote feature";
case kSCOOffsetRejected: return "SCO offset rejected";
case kSCOIntervalRejected: return "SCO interval rejected";
case kSCOAirModeRejected: return "SCO air mode rejected";
case kInvalidLMPOrLLParameters: return "invalid LMP or LL parameters";
case kUnspecifiedError: return "unspecified error";
case kUnsupportedLMPOrLLParameterValue: return "unsupported LMP or LL parameter value";
case kRoleChangeNotAllowed: return "role change not allowed";
case kLMPOrLLResponseTimeout: return "LMP or LL response timeout";
case kLMPErrorTransactionCollision: return "LMP error transaction collision";
case kLMPPDUNotAllowed: return "LMP PDU not allowed";
case kEncryptionModeNotAcceptable: return "encryption mode not acceptable";
case kLinkKeyCannotBeChanged: return "link key cannot be changed";
case kRequestedQoSNotSupported: return "requested QoS not supported";
case kInstantPassed: return "instant passed";
case kPairingWithUnitKeyNotSupported: return "pairing with unit key not supported";
case kDifferentTransactionCollision: return "different transaction collision";
case kQoSUnacceptableParameter: return "QoS unacceptable parameter";
case kQoSRejected: return "QoS rejected";
case kChannelClassificationNotSupported: return "channel classification not supported";
case kInsufficientSecurity: return "insufficient security";
case kParameterOutOfMandatoryRange: return "parameter out of mandatory range";
case kRoleSwitchPending: return "role switch pending";
case kReservedSlotViolation: return "reserved slot violation";
case kRoleSwitchFailed: return "role switch failed";
case kExtendedInquiryResponseTooLarge: return "extended inquiry response too large";
case kSecureSimplePairingNotSupportedByHost: return "secure simple pairing not supported by host";
case kHostBusyPairing: return "host busy pairing";
case kConnectionRejectedNoSuitableChannelFound: return "connection rejected: no suitable channel found";
case kControllerBusy: return "controller busy";
case kUnacceptableConnectionParameters: return "unacceptable connection parameters";
case kDirectedAdvertisingTimeout: return "directed advertising timeout";
case kConnectionTerminatedMICFailure: return "connection terminated: MIC failure";
case kConnectionFailedToBeEstablished: return "connection failed to be established";
case kMACConnectionFailed: return "MAC connection failed";
case kCoarseClockAdjustmentRejected: return "coarse clock adjustment rejected";
case kType0SubmapNotDefined: return "type 0 submap not defined";
case kUnknownAdvertisingIdentifier: return "unknown advertising identifier";
case kLimitReached: return "limit reached";
case kOperationCancelledByHost: return "operation cancelled by host";
default: break;
};
return "unknown status";
}
// clang-format on
std::string LinkTypeToString(hci_spec::LinkType link_type) {
switch (link_type) {
case LinkType::kSCO:
return "SCO";
case LinkType::kACL:
return "ACL";
case LinkType::kExtendedSCO:
return "eSCO";
default:
return "<Unknown LinkType>";
};
}
std::string ConnectionRoleToString(hci_spec::ConnectionRole role) {
switch (role) {
case ConnectionRole::kCentral:
return "central";
case ConnectionRole::kPeripheral:
return "peripheral";
default:
return "<unknown role>";
}
}
// TODO(fxbug.dev/80048): various parts of the spec call for a 3 byte integer. If we need to in the
// future, we should generalize this logic and make a uint24_t type that makes it easier to work
// with these types of conversions.
void EncodeLegacyAdvertisingInterval(uint16_t input, uint8_t (&result)[3]) {
MutableBufferView result_view(result, sizeof(result));
result_view.SetToZeros();
// Core spec Volume 6, Part B, Section 1.2: Link layer order is little endian, convert to little
// endian if host order is big endian
input = htole16(input);
BufferView input_view(&input, sizeof(input));
input_view.Copy(&result_view, 0, sizeof(input));
}
// TODO(fxbug.dev/80048): various parts of the spec call for a 3 byte integer. If we need to in the
// future, we should generalize this logic and make a uint24_t type that makes it easier to work
// with these types of conversions.
uint32_t DecodeExtendedAdvertisingInterval(const uint8_t (&input)[3]) {
uint32_t result = 0;
MutableBufferView result_view(&result, sizeof(result));
BufferView input_view(input, sizeof(input));
input_view.Copy(&result_view);
// Core spec Volume 6, Part B, Section 1.2: Link layer order is little endian, convert to little
// endian if host order is big endian
return letoh32(result);
}
std::optional<AdvertisingEventBits> AdvertisingTypeToEventBits(LEAdvertisingType type) {
// TODO(fxbug.dev/81470): for backwards compatibility and because supporting extended advertising
// PDUs is a much larger project, we currently only support legacy PDUs. Without using legacy
// PDUs, non-Bluetooth 5 devices will not be able to discover extended advertisements.
uint16_t adv_event_properties = kLEAdvEventPropBitUseLegacyPDUs;
// Bluetooth Spec Volume 4, Part E, Section 7.8.53, Table 7.2 defines the mapping of legacy PDU
// types to the corresponding bits within adv_event_properties.
switch (type) {
case LEAdvertisingType::kAdvInd:
adv_event_properties |= kLEAdvEventPropBitConnectable;
adv_event_properties |= kLEAdvEventPropBitScannable;
break;
case LEAdvertisingType::kAdvDirectIndLowDutyCycle:
adv_event_properties |= kLEAdvEventPropBitConnectable;
adv_event_properties |= kLEAdvEventPropBitDirected;
break;
case LEAdvertisingType::kAdvDirectIndHighDutyCycle:
adv_event_properties |= kLEAdvEventPropBitConnectable;
adv_event_properties |= kLEAdvEventPropBitDirected;
adv_event_properties |= kLEAdvEventPropBitHighDutyCycleDirectedConnectable;
break;
case LEAdvertisingType::kAdvScanInd:
adv_event_properties |= kLEAdvEventPropBitScannable;
break;
case LEAdvertisingType::kAdvNonConnInd:
// no extra bits to set
break;
default:
return std::nullopt;
}
return adv_event_properties;
}
} // namespace bt::hci_spec