/****************************************************************************** * * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /* * TODO(b/249193511): Replace this MGMT interface with sockopt/ioctl. * This file will be replaced such that it is not optimized for now. */ #include "hal/mgmt.h" #include #include #include #include #include #include "common/init_flags.h" #include "os/log.h" namespace bluetooth { namespace hal { #define RETRY_ON_INTR(fn) \ do { \ } while ((fn) == -1 && errno == EINTR) struct sockaddr_hci { sa_family_t hci_family; unsigned short hci_dev; unsigned short hci_channel; }; constexpr static uint8_t BTPROTO_HCI = 1; constexpr static uint16_t HCI_CHANNEL_CONTROL = 3; constexpr static uint16_t HCI_DEV_NONE = 0xffff; static int btsocket_open_mgmt(uint16_t hci) { int fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_NONBLOCK, BTPROTO_HCI); if (fd < 0) { log::error("Failed to open BT socket."); return -errno; } struct sockaddr_hci addr = { .hci_family = AF_BLUETOOTH, .hci_dev = HCI_DEV_NONE, .hci_channel = HCI_CHANNEL_CONTROL, }; int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if (ret < 0) { log::error("Failed to bind BT socket."); close(fd); return -errno; } return fd; } /* * Given a vendor specification, e.g., MSFT extension, this function returns * the vendor specific opcode. * * If the controller does not support MSFT extension or there are errors * or failures in writing/reading the MGMT socket, the return opcode would * be HCI_OP_NOP (0x0000). */ uint16_t Mgmt::get_vs_opcode(uint16_t vendor_specification) { int hci = bluetooth::common::InitFlags::GetAdapterIndex(); int fd = btsocket_open_mgmt(hci); uint16_t ret_opcode = HCI_OP_NOP; if (fd < 0) { log::error("Failed to open mgmt channel for hci {}, error= {}.", hci, fd); return ret_opcode; } struct mgmt_pkt ev; ev.opcode = MGMT_OP_GET_VS_OPCODE; ev.index = HCI_DEV_NONE; ev.len = sizeof(struct mgmt_cp_get_vs_opcode); struct mgmt_cp_get_vs_opcode* cp = reinterpret_cast(ev.data); cp->hci_id = hci; cp->vendor_specification = MGMT_VS_OPCODE_MSFT; int ret; struct pollfd writable[1]; writable[0].fd = fd; writable[0].events = POLLOUT; do { ret = poll(writable, 1, MGMT_POLL_TIMEOUT_MS); if (ret > 0) { RETRY_ON_INTR(ret = write(fd, &ev, MGMT_PKT_HDR_SIZE + ev.len)); if (ret < 0) { log::error("Failed to call MGMT opcode 0x{:04x}, errno {}", ev.opcode, -errno); close(fd); return ret_opcode; }; break; } else if (ret < 0) { log::error("msft poll ret {} errno {}", ret, -errno); } } while (ret > 0); if (ret <= 0) { log::info( "Skip because mgmt socket is not writable: ev.opcode 0x{:04x} ret {}", ev.opcode, ret); close(fd); return ret_opcode; } struct pollfd fds[1]; struct mgmt_pkt cc_ev; fds[0].fd = fd; fds[0].events = POLLIN; do { ret = poll(fds, 1, MGMT_POLL_TIMEOUT_MS); if (ret > 0) { if (fds[0].revents & POLLIN) { RETRY_ON_INTR(ret = read(fd, &cc_ev, sizeof(cc_ev))); if (ret < 0) { log::error("Failed to read mgmt socket: {}", -errno); close(fd); return ret_opcode; } if (cc_ev.opcode == MGMT_EV_COMMAND_COMPLETE) { struct mgmt_ev_cmd_complete* cc = reinterpret_cast(cc_ev.data); if (cc->opcode == ev.opcode && cc->status == 0) { struct mgmt_rp_get_vs_opcode* rp = reinterpret_cast(cc->data); if (rp->hci_id == hci) { // If the controller supports the MSFT extension, the returned opcode // will not be HCI_OP_NOP. if (rp->opcode != HCI_OP_NOP) { ret_opcode = rp->opcode; } close(fd); return ret_opcode; } } } } } else if (ret == 0) { log::error("Timeout while waiting for response of calling MGMT opcode: 0x{:04x}", ev.opcode); ret = -1; } } while (ret > 0); close(fd); return ret_opcode; } } // namespace hal } // namespace bluetooth