diff options
-rw-r--r-- | api/direct_bt/HCIComm.hpp | 85 | ||||
-rw-r--r-- | src/direct_bt/HCIComm.cpp | 227 |
2 files changed, 244 insertions, 68 deletions
diff --git a/api/direct_bt/HCIComm.hpp b/api/direct_bt/HCIComm.hpp index c9474565..1b34b670 100644 --- a/api/direct_bt/HCIComm.hpp +++ b/api/direct_bt/HCIComm.hpp @@ -53,6 +53,84 @@ namespace direct_bt { LE_Advertising_Report = 0x3E }; + /** + * BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes + * <p> + * BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 2 Error code descriptions + * </p> + */ + enum class HCIErrorCode : uint8_t { + SUCCESS = 0x00, + UNKNOWN_HCI_COMMAND = 0x01, + UNKNOWN_CONNECTION_IDENTIFIER = 0x02, + HARDWARE_FAILURE = 0x03, + PAGE_TIMEOUT = 0x04, + AUTHENTICATION_FAILURE = 0x05, + PIN_OR_KEY_MISSING = 0x06, + MEMORY_CAPACITY_EXCEEDED = 0x07, + CONNECTION_TIMEOUT = 0x08, + CONNECTION_LIMIT_EXCEEDED = 0x09, + SYNC_DEVICE_CONNECTION_LIMIT_EXCEEDED = 0x0a, + CONNECTION_ALREADY_EXISTS = 0x0b, + COMMAND_DISALLOWED = 0x0c, + CONNECTION_REJECTED_LIMITED_RESOURCES = 0x0d, + CONNECTION_REJECTED_SECURITY = 0x0e, + CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR = 0x0f, + CONNECTION_ACCEPT_TIMEOUT_EXCEEDED = 0x10, + UNSUPPORTED_FEATURE_OR_PARAM_VALUE = 0x11, + INVALID_HCI_COMMAND_PARAMETERS = 0x12, + REMOTE_USER_TERMINATED_CONNECTION = 0x13, + REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14, + REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15, + CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x16, + REPEATED_ATTEMPTS = 0x17, + PAIRING_NOT_ALLOWED = 0x18, + UNKNOWN_LMP_PDU = 0x19, + UNSUPPORTED_REMOTE_OR_LMP_FEATURE = 0x1a, + SCO_OFFSET_REJECTED = 0x1b, + SCO_INTERVAL_REJECTED = 0x1c, + SCO_AIR_MODE_REJECTED = 0x1d, + INVALID_LMP_OR_LL_PARAMETERS = 0x1e, + UNSPECIFIED_ERROR = 0x1f, + UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE = 0x20, + ROLE_CHANGE_NOT_ALLOWED = 0x21, + LMP_OR_LL_RESPONSE_TIMEOUT = 0x22, + LMP_OR_LL_COLLISION = 0x23, + LMP_PDU_NOT_ALLOWED = 0x24, + ENCRYPTION_MODE_NOT_ACCEPTED = 0x25, + LINK_KEY_CANNOT_BE_CHANGED = 0x26, + REQUESTED_QOS_NOT_SUPPORTED = 0x27, + INSTANT_PASSED = 0x28, + PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29, + DIFFERENT_TRANSACTION_COLLISION = 0x2a, + QOS_UNACCEPTABLE_PARAMETER = 0x2c, + QOS_REJECTED = 0x2d, + CHANNEL_ASSESSMENT_NOT_SUPPORTED = 0x2e, + INSUFFICIENT_SECURITY = 0x2f, + PARAMETER_OUT_OF_RANGE = 0x30, + ROLE_SWITCH_PENDING = 0x32, + RESERVED_SLOT_VIOLATION = 0x34, + ROLE_SWITCH_FAILED = 0x35, + EIR_TOO_LARGE = 0x36, + SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST = 0x37, + HOST_BUSY_PAIRING = 0x38, + CONNECTION_REJECTED_NO_SUITABLE_CHANNEL = 0x39, + CONTROLLER_BUSY = 0x3a, + UNACCEPTABLE_CONNECTION_PARAM = 0x3b, + ADVERTISING_TIMEOUT = 0x3c, + CONNECTION_TERMINATED_MIC_FAILURE = 0x3d, + CONNECTION_EST_FAILED_OR_SYNC_TIMETOUT = 0x3e, + MAX_CONNECTION_FAILED = 0x3f, + COARSE_CLOCK_ADJ_REJECTED = 0x40, + TYPE0_SUBMAP_NOT_DEFINED = 0x41, + UNKNOWN_ADVERTISING_IDENTIFIER = 0x42, + LIMIT_REACHED = 0x43, + OPERATION_CANCELLED_BY_HOST = 0x44, + PACKET_TOO_LONG = 0x45, + INTERNAL_FAILURE = 0xff + }; + std::string getHCIErrorCodeString(const HCIErrorCode ec); + class HCIComm { private: static int hci_open_dev(const uint16_t dev_id, const uint16_t channel); @@ -66,8 +144,8 @@ namespace direct_bt { bool le_scanning; bool send_cmd(const uint16_t opcode, const void *command, const uint8_t command_len); - bool send_req(const uint16_t opcode, const void *command, const uint8_t command_len, - const uint16_t exp_event, void *response, const uint8_t response_len); + HCIErrorCode send_req(const uint16_t opcode, const void *command, const uint8_t command_len, + const uint16_t exp_event, void *response, const uint8_t response_len); bool le_set_scan_enable(const uint8_t enable, const uint8_t filter_dup); bool le_set_scan_parameters(const uint8_t type, const uint16_t interval, @@ -124,6 +202,9 @@ namespace direct_bt { /** * Establish a connection to the given LE peer. * <p> + * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command + * </p> + * <p> * Even if not utilizing a HCI channel, it has been observed that maintaining such * enhanced performance on subsequent communication, i.e. GATT over L2CAP. * </p> diff --git a/src/direct_bt/HCIComm.cpp b/src/direct_bt/HCIComm.cpp index 3344830f..e765f8fa 100644 --- a/src/direct_bt/HCIComm.cpp +++ b/src/direct_bt/HCIComm.cpp @@ -50,6 +50,86 @@ extern "C" { namespace direct_bt { +#define HCI_ERROR_CODE(X) \ + X(SUCCESS) \ + X(UNKNOWN_HCI_COMMAND) \ + X(UNKNOWN_CONNECTION_IDENTIFIER) \ + X(HARDWARE_FAILURE) \ + X(PAGE_TIMEOUT) \ + X(AUTHENTICATION_FAILURE) \ + X(PIN_OR_KEY_MISSING) \ + X(MEMORY_CAPACITY_EXCEEDED) \ + X(CONNECTION_TIMEOUT) \ + X(CONNECTION_LIMIT_EXCEEDED) \ + X(SYNC_DEVICE_CONNECTION_LIMIT_EXCEEDED) \ + X(CONNECTION_ALREADY_EXISTS) \ + X(COMMAND_DISALLOWED) \ + X(CONNECTION_REJECTED_LIMITED_RESOURCES) \ + X(CONNECTION_REJECTED_SECURITY) \ + X(CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR) \ + X(CONNECTION_ACCEPT_TIMEOUT_EXCEEDED) \ + X(UNSUPPORTED_FEATURE_OR_PARAM_VALUE) \ + X(INVALID_HCI_COMMAND_PARAMETERS) \ + X(REMOTE_USER_TERMINATED_CONNECTION) \ + X(REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES) \ + X(REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF) \ + X(CONNECTION_TERMINATED_BY_LOCAL_HOST) \ + X(REPEATED_ATTEMPTS) \ + X(PAIRING_NOT_ALLOWED) \ + X(UNKNOWN_LMP_PDU) \ + X(UNSUPPORTED_REMOTE_OR_LMP_FEATURE) \ + X(SCO_OFFSET_REJECTED) \ + X(SCO_INTERVAL_REJECTED) \ + X(SCO_AIR_MODE_REJECTED) \ + X(INVALID_LMP_OR_LL_PARAMETERS) \ + X(UNSPECIFIED_ERROR) \ + X(UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE) \ + X(ROLE_CHANGE_NOT_ALLOWED) \ + X(LMP_OR_LL_RESPONSE_TIMEOUT) \ + X(LMP_OR_LL_COLLISION) \ + X(LMP_PDU_NOT_ALLOWED) \ + X(ENCRYPTION_MODE_NOT_ACCEPTED) \ + X(LINK_KEY_CANNOT_BE_CHANGED) \ + X(REQUESTED_QOS_NOT_SUPPORTED) \ + X(INSTANT_PASSED) \ + X(PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) \ + X(DIFFERENT_TRANSACTION_COLLISION) \ + X(QOS_UNACCEPTABLE_PARAMETER) \ + X(QOS_REJECTED) \ + X(CHANNEL_ASSESSMENT_NOT_SUPPORTED) \ + X(INSUFFICIENT_SECURITY) \ + X(PARAMETER_OUT_OF_RANGE) \ + X(ROLE_SWITCH_PENDING) \ + X(RESERVED_SLOT_VIOLATION) \ + X(ROLE_SWITCH_FAILED) \ + X(EIR_TOO_LARGE) \ + X(SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST) \ + X(HOST_BUSY_PAIRING) \ + X(CONNECTION_REJECTED_NO_SUITABLE_CHANNEL) \ + X(CONTROLLER_BUSY) \ + X(UNACCEPTABLE_CONNECTION_PARAM) \ + X(ADVERTISING_TIMEOUT) \ + X(CONNECTION_TERMINATED_MIC_FAILURE) \ + X(CONNECTION_EST_FAILED_OR_SYNC_TIMETOUT) \ + X(MAX_CONNECTION_FAILED) \ + X(COARSE_CLOCK_ADJ_REJECTED) \ + X(TYPE0_SUBMAP_NOT_DEFINED) \ + X(UNKNOWN_ADVERTISING_IDENTIFIER) \ + X(LIMIT_REACHED) \ + X(OPERATION_CANCELLED_BY_HOST) \ + X(PACKET_TOO_LONG) \ + X(INTERNAL_FAILURE) + +#define HCI_ERROR_CODE_CASE_TO_STRING(V) case HCIErrorCode::V: return #V; + +std::string getHCIErrorCodeString(const HCIErrorCode ec) { + switch(ec) { + HCI_ERROR_CODE(HCI_ERROR_CODE_CASE_TO_STRING) + default: ; // fall through intended + } + return "Unknown HCI error code"; +} + int HCIComm::hci_open_dev(const uint16_t dev_id, const uint16_t channel) { sockaddr_hci a; @@ -230,13 +310,13 @@ bool HCIComm::send_cmd(const uint16_t opcode, const void *command, const uint8_t #define _HCI_PKT_TRY_COUNT 10 -bool HCIComm::send_req(const uint16_t opcode, const void *command, const uint8_t command_len, - const uint16_t exp_event, void *response, const uint8_t response_len) +HCIErrorCode HCIComm::send_req(const uint16_t opcode, const void *command, const uint8_t command_len, + const uint16_t exp_event, void *response, const uint8_t response_len) { const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor if( 0 > _dd ) { ERR_PRINT("hci_send_req: device not open"); - return false; + return HCIErrorCode::INTERNAL_FAILURE; } uint8_t buf[HCI_MAX_EVENT_SIZE]; const uint16_t opcode_le16 = cpu_to_le(opcode); @@ -249,7 +329,7 @@ bool HCIComm::send_req(const uint16_t opcode, const void *command, const uint8_t olen = sizeof(of); if (getsockopt(_dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) { ERR_PRINT("hci_send_req"); - return false; + return HCIErrorCode::INTERNAL_FAILURE; } filter_clear(&nf); @@ -263,10 +343,11 @@ bool HCIComm::send_req(const uint16_t opcode, const void *command, const uint8_t filter_set_opcode(opcode_le16, &nf); if (setsockopt(_dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) { ERR_PRINT("hci_send_req"); - return false; + return HCIErrorCode::INTERNAL_FAILURE; } int _timeoutMS = timeoutMS; + HCIErrorCode res = HCIErrorCode::INTERNAL_FAILURE; if ( !send_cmd(opcode, command, command_len) ) { ERR_PRINT("hci_send_req"); @@ -330,18 +411,23 @@ bool HCIComm::send_req(const uint16_t opcode, const void *command, const uint8_t switch (hdr->evt) { case HCI_EV_CMD_STATUS: { const hci_ev_cmd_status *cs = static_cast<const hci_ev_cmd_status *>(static_cast<const void *>( ptr )); + const HCIErrorCode status = static_cast<HCIErrorCode>(cs->status); - DBG_PRINT("hci_send_req: HCI_EV_CMD_STATUS: opcode 0x%X, exp_event 0x%X, status 0x%2.2X, rlen %d/%d", - cs->opcode, exp_event, cs->status, response_len, len); + DBG_PRINT("hci_send_req: HCI_EV_CMD_STATUS: opcode 0x%X, exp_event 0x%X, status 0x%2.2X (%s), rlen %d/%d", + cs->opcode, exp_event, + cs->status, getHCIErrorCodeString(status).c_str(), + response_len, len); if (cs->opcode != opcode_le16) { continue; } if (exp_event != HCI_EV_CMD_STATUS) { - if (cs->status) { + if ( HCIErrorCode::SUCCESS != status ) { errno = EIO; - ERR_PRINT("hci_send_req: event exp 0x%X != has 0x%X, error status 0x%2.2X", exp_event, hdr->evt, cs->status); + ERR_PRINT("hci_send_req: event exp 0x%X != has 0x%X, error status 0x%2.2X (%s)", exp_event, + hdr->evt, cs->status, getHCIErrorCodeString(status).c_str()); + res = status; goto failed; } break; @@ -439,11 +525,11 @@ failed: err = errno; setsockopt(_dd, SOL_HCI, HCI_FILTER, &of, sizeof(of)); errno = err; - return false; + return res; done: setsockopt(_dd, SOL_HCI, HCI_FILTER, &of, sizeof(of)); - return true; + return HCIErrorCode::SUCCESS; } bool HCIComm::disconnect(const uint16_t le_conn_handle, const uint8_t reason) @@ -462,18 +548,20 @@ bool HCIComm::disconnect(const uint16_t le_conn_handle, const uint8_t reason) cp.handle = le_conn_handle; cp.reason = reason; - if( !send_req( hci_opcode_pack(OGF_LINK_CTL, HCI_OP_DISCONNECT), &cp, sizeof(cp), - HCI_EV_DISCONN_COMPLETE, &rp, sizeof(rp) ) ) - { - DBG_PRINT("hci_disconnect: errno %d %s", errno, strerror(errno)); - return false; - } - - if (rp.status) { - errno = EIO; - DBG_PRINT("hci_disconnect: error status 0x%2.2X, errno %d %s", rp.status, errno, strerror(errno)); - return false; - } + HCIErrorCode res = send_req( hci_opcode_pack(OGF_LINK_CTL, HCI_OP_DISCONNECT), &cp, sizeof(cp), + HCI_EV_DISCONN_COMPLETE, &rp, sizeof(rp) ); + if( HCIErrorCode::SUCCESS != res ) { + ERR_PRINT("hci_disconnect: 0x%2.2X (%s), errno %d %s", + static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno)); + return false; + } + HCIErrorCode status = static_cast<HCIErrorCode>(rp.status); + if( HCIErrorCode::SUCCESS != status ) { + errno = EIO; + ERR_PRINT("hci_disconnect: error status 0x%2.2X (%s), errno %d %s", + rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno)); + return false; + } return true; } @@ -484,24 +572,25 @@ bool HCIComm::le_set_scan_enable(const uint8_t enable, const uint8_t filter_dup) return false; } hci_cp_le_set_scan_enable cp; - uint8_t status; + HCIErrorCode status; bzero(&cp, sizeof(cp)); cp.enable = enable; cp.filter_dup = filter_dup; - if( !send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_SET_SCAN_ENABLE), &cp, sizeof(cp), - 0, &status, sizeof(status) ) ) - { - ERR_PRINT("hci_le_set_scan_enable(%d)", enable); + HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_SET_SCAN_ENABLE), &cp, sizeof(cp), + 0, &status, sizeof(status) ); + if( HCIErrorCode::SUCCESS != res ) { + ERR_PRINT("hci_le_set_scan_enable(%d): 0x%2.2X (%s), errno %d %s", + enable, static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno)); + return false; + } + if( HCIErrorCode::SUCCESS != status ) { + errno = EIO; + ERR_PRINT("hci_le_set_scan_enable(%d): error status 0x%2.2X (%s), errno %d %s", + enable, static_cast<uint8_t>(status), getHCIErrorCodeString(status).c_str(), errno, strerror(errno)); return false; } - - if (status) { - errno = EIO; - ERR_PRINT("hci_le_set_scan_enable(%d): error status 0x%2.2X", enable, status); - return false; - } return true; } @@ -515,7 +604,7 @@ bool HCIComm::le_set_scan_parameters(const uint8_t type, const uint16_t interval return false; } hci_cp_le_set_scan_param cp; - uint8_t status; + HCIErrorCode status; bzero(&cp, sizeof(cp)); cp.type = type; @@ -524,19 +613,19 @@ bool HCIComm::le_set_scan_parameters(const uint8_t type, const uint16_t interval cp.own_address_type = own_type; cp.filter_policy = filter; - if( !send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_SET_SCAN_PARAM), &cp, sizeof(cp), - 0, &status, sizeof(status) ) ) - { - ERR_PRINT("hci_le_set_scan_parameters"); + HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_SET_SCAN_PARAM), &cp, sizeof(cp), + 0, &status, sizeof(status) ); + if( HCIErrorCode::SUCCESS != res ) { + ERR_PRINT("hci_le_set_scan_parameters: 0x%2.2X (%s), errno %d %s", + static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno)); + return false; + } + if( HCIErrorCode::SUCCESS != status ) { + errno = EIO; + ERR_PRINT("hci_le_set_scan_parameters: error status 0x%2.2X (%s), errno %d %s", + static_cast<uint8_t>(status), getHCIErrorCodeString(status).c_str(), errno, strerror(errno)); return false; } - - if (status) { - errno = EIO; - ERR_PRINT("hci_le_set_scan_parameters: error status 0x%2.2X", status); - return false; - } - return true; } @@ -596,6 +685,8 @@ uint16_t HCIComm::le_create_conn(const EUI48 &peer_bdaddr, const uint16_t min_interval, const uint16_t max_interval, const uint16_t latency, const uint16_t supervision_timeout) { + + /** BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command */ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor if( 0 > _dd ) { ERR_PRINT("hci_le_create_conn: device not open"); @@ -604,9 +695,9 @@ uint16_t HCIComm::le_create_conn(const EUI48 &peer_bdaddr, hci_cp_le_create_conn cp; hci_ev_le_conn_complete rp; - const uint16_t min_ce_length = 0x0000; // 0x0001 ?? - const uint16_t max_ce_length = 0x0000; // 0x0001 ?? - const uint8_t initiator_filter = 0x00; + const uint16_t min_ce_length = 0x0001; // 0x0001 ?? + const uint16_t max_ce_length = 0x0001; // 0x0001 ?? + const uint8_t initiator_filter = 0x00; // whitelist not used but peer_bdaddr* bzero((void*)&cp, sizeof(cp)); @@ -623,18 +714,20 @@ uint16_t HCIComm::le_create_conn(const EUI48 &peer_bdaddr, cp.min_ce_len = cpu_to_le(min_ce_length); cp.max_ce_len = cpu_to_le(max_ce_length); - if( !send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_CREATE_CONN), &cp, sizeof(cp), - HCI_EV_LE_CONN_COMPLETE, &rp, sizeof(rp) ) ) - { - ERR_PRINT("hci_le_create_conn"); + HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_CREATE_CONN), &cp, sizeof(cp), + HCI_EV_LE_CONN_COMPLETE, &rp, sizeof(rp) ); + if( HCIErrorCode::SUCCESS != res ) { + ERR_PRINT("hci_le_create_conn: error status 0x%2.2X (%s), errno %d %s", + static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno)); + return 0; + } + HCIErrorCode status = static_cast<HCIErrorCode>(rp.status); + if( HCIErrorCode::SUCCESS != status ) { + errno = EIO; + ERR_PRINT("hci_le_create_conn: error status 0x%2.2X (%s), errno %d %s", + rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno)); return 0; } - - if (rp.status) { - errno = EIO; - ERR_PRINT("hci_le_create_conn: error status 0x%2.2X", rp.status); - return 0; - } return rp.handle; } @@ -657,16 +750,18 @@ uint16_t HCIComm::create_conn(const EUI48 &bdaddr, const uint16_t pkt_type, cp.clock_offset = cpu_to_le(clock_offset); cp.role_switch = role_switch; - if( !send_req( hci_opcode_pack(OGF_LINK_CTL, HCI_OP_CREATE_CONN), &cp, sizeof(cp), - HCI_EV_CONN_COMPLETE, &rp, sizeof(rp) ) ) - { - ERR_PRINT("hci_create_conn"); + HCIErrorCode res = send_req( hci_opcode_pack(OGF_LINK_CTL, HCI_OP_CREATE_CONN), &cp, sizeof(cp), + HCI_EV_CONN_COMPLETE, &rp, sizeof(rp) ); + if( HCIErrorCode::SUCCESS != res ) { + ERR_PRINT("hci_create_conn: error status 0x%2.2X (%s), errno %d %s", + static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno)); return 0; } - - if (rp.status) { + HCIErrorCode status = static_cast<HCIErrorCode>(rp.status); + if( HCIErrorCode::SUCCESS != status ) { errno = EIO; - ERR_PRINT("hci_create_conn: error status 0x%2.2X", rp.status); + ERR_PRINT("hci_create_conn: error status 0x%2.2X (%s), errno %d %s", + rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno)); return 0; } return rp.handle; |