| /********************************************************************** |
| * |
| * Copyright 2017 The Android Open Source Project |
| * Copyright 2015 Intel Corporation |
| * |
| * 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. |
| * |
| **********************************************************************/ |
| #include <base/bind.h> |
| #include <base/location.h> |
| #include <base/logging.h> |
| #include <base/threading/thread.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <algorithm> |
| |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| |
| #include "buffer_allocator.h" |
| #include "hci_internals.h" |
| #include "hci_layer.h" |
| #include "osi/include/compat.h" |
| #include "osi/include/log.h" |
| #include "osi/include/osi.h" |
| #include "osi/include/properties.h" |
| |
| using base::Thread; |
| |
| #define BTPROTO_HCI 1 |
| #define HCI_CHANNEL_USER 1 |
| #define HCI_CHANNEL_CONTROL 3 |
| #define HCI_DEV_NONE 0xffff |
| |
| #define RFKILL_TYPE_BLUETOOTH 2 |
| #define RFKILL_OP_CHANGE_ALL 3 |
| |
| #define MGMT_OP_INDEX_LIST 0x0003 |
| #define MGMT_EV_INDEX_ADDED 0x0004 |
| #define MGMT_EV_COMMAND_COMP 0x0001 |
| #define MGMT_EV_SIZE_MAX 1024 |
| #define MGMT_EV_POLL_TIMEOUT 3000 /* 3000ms */ |
| |
| struct sockaddr_hci { |
| sa_family_t hci_family; |
| unsigned short hci_dev; |
| unsigned short hci_channel; |
| }; |
| |
| struct rfkill_event { |
| uint32_t idx; |
| uint8_t type; |
| uint8_t op; |
| uint8_t soft, hard; |
| } __attribute__((packed)); |
| |
| struct mgmt_pkt { |
| uint16_t opcode; |
| uint16_t index; |
| uint16_t len; |
| uint8_t data[MGMT_EV_SIZE_MAX]; |
| } __attribute__((packed)); |
| |
| struct mgmt_event_read_index { |
| uint16_t cc_opcode; |
| uint8_t status; |
| uint16_t num_intf; |
| uint16_t index[0]; |
| } __attribute__((packed)); |
| |
| enum HciPacketType { |
| HCI_PACKET_TYPE_UNKNOWN = 0, |
| HCI_PACKET_TYPE_COMMAND = 1, |
| HCI_PACKET_TYPE_ACL_DATA = 2, |
| HCI_PACKET_TYPE_SCO_DATA = 3, |
| HCI_PACKET_TYPE_EVENT = 4 |
| }; |
| |
| extern void initialization_complete(); |
| extern void hci_event_received(const base::Location& from_here, BT_HDR* packet); |
| extern void acl_event_received(BT_HDR* packet); |
| extern void sco_data_received(BT_HDR* packet); |
| |
| static int bt_vendor_fd = -1; |
| static int hci_interface; |
| static int rfkill_en; |
| static int wait_hcidev(void); |
| static int rfkill(int block); |
| |
| int reader_thread_ctrl_fd = -1; |
| Thread* reader_thread = NULL; |
| |
| void monitor_socket(int ctrl_fd, int fd) { |
| const allocator_t* buffer_allocator = buffer_allocator_get_interface(); |
| const size_t buf_size = 2000; |
| uint8_t buf[buf_size]; |
| ssize_t len = read(fd, buf, buf_size); |
| |
| while (len > 0) { |
| if (len == buf_size) |
| LOG(FATAL) << "This packet filled buffer, if it have continuation we " |
| "don't know how to merge it, increase buffer size!"; |
| |
| uint8_t type = buf[0]; |
| |
| size_t packet_size = buf_size + BT_HDR_SIZE; |
| BT_HDR* packet = |
| reinterpret_cast<BT_HDR*>(buffer_allocator->alloc(packet_size)); |
| packet->offset = 0; |
| packet->layer_specific = 0; |
| packet->len = len - 1; |
| memcpy(packet->data, buf + 1, len - 1); |
| |
| switch (type) { |
| case HCI_PACKET_TYPE_COMMAND: |
| packet->event = MSG_HC_TO_STACK_HCI_EVT; |
| hci_event_received(FROM_HERE, packet); |
| break; |
| case HCI_PACKET_TYPE_ACL_DATA: |
| packet->event = MSG_HC_TO_STACK_HCI_ACL; |
| acl_event_received(packet); |
| break; |
| case HCI_PACKET_TYPE_SCO_DATA: |
| packet->event = MSG_HC_TO_STACK_HCI_SCO; |
| sco_data_received(packet); |
| break; |
| case HCI_PACKET_TYPE_EVENT: |
| packet->event = MSG_HC_TO_STACK_HCI_EVT; |
| hci_event_received(FROM_HERE, packet); |
| break; |
| default: |
| LOG(FATAL) << "Unexpected event type: " << +type; |
| break; |
| } |
| |
| fd_set fds; |
| FD_ZERO(&fds); |
| FD_SET(ctrl_fd, &fds); |
| FD_SET(fd, &fds); |
| int res = select(std::max(fd, ctrl_fd) + 1, &fds, NULL, NULL, NULL); |
| if (res <= 0) LOG(INFO) << "Nothing more to read"; |
| |
| if (FD_ISSET(ctrl_fd, &fds)) { |
| LOG(INFO) << "exitting"; |
| return; |
| } |
| |
| len = read(fd, buf, buf_size); |
| } |
| } |
| |
| /* TODO: should thread the device waiting and return immedialty */ |
| void hci_initialize() { |
| LOG(INFO) << __func__; |
| |
| char prop_value[PROPERTY_VALUE_MAX]; |
| osi_property_get("bluetooth.interface", prop_value, "0"); |
| |
| errno = 0; |
| if (memcmp(prop_value, "hci", 3)) |
| hci_interface = strtol(prop_value, NULL, 10); |
| else |
| hci_interface = strtol(prop_value + 3, NULL, 10); |
| if (errno) hci_interface = 0; |
| |
| LOG(INFO) << "Using interface hci" << +hci_interface; |
| |
| osi_property_get("bluetooth.rfkill", prop_value, "1"); |
| |
| rfkill_en = atoi(prop_value); |
| if (rfkill_en) { |
| rfkill(0); |
| } |
| |
| int fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); |
| CHECK(fd >= 0) << "socket create error" << strerror(errno); |
| |
| bt_vendor_fd = fd; |
| |
| if (wait_hcidev()) { |
| LOG(FATAL) << "HCI interface hci" << +hci_interface << " not found"; |
| } |
| |
| struct sockaddr_hci addr; |
| memset(&addr, 0, sizeof(addr)); |
| addr.hci_family = AF_BLUETOOTH; |
| addr.hci_dev = hci_interface; |
| addr.hci_channel = HCI_CHANNEL_USER; |
| if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { |
| PLOG(FATAL) << "socket bind error"; |
| } |
| |
| int sv[2]; |
| if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) { |
| PLOG(FATAL) << "socketpair failed"; |
| } |
| |
| reader_thread_ctrl_fd = sv[0]; |
| reader_thread = new Thread("hci_sock_reader"); |
| reader_thread->Start(); |
| reader_thread->task_runner()->PostTask( |
| FROM_HERE, base::Bind(&monitor_socket, sv[1], bt_vendor_fd)); |
| |
| LOG(INFO) << "HCI device ready"; |
| initialization_complete(); |
| } |
| |
| void hci_close() { |
| LOG(INFO) << __func__; |
| |
| if (bt_vendor_fd != -1) { |
| close(bt_vendor_fd); |
| bt_vendor_fd = -1; |
| } |
| |
| if (reader_thread_ctrl_fd != -1) { |
| uint8_t msg[] = {1}; |
| send(reader_thread_ctrl_fd, msg, sizeof(msg), 0); |
| reader_thread_ctrl_fd = -1; |
| } |
| |
| if (reader_thread != NULL) { |
| reader_thread->Stop(); |
| delete reader_thread; |
| reader_thread = NULL; |
| } |
| |
| rfkill(1); |
| } |
| |
| void hci_transmit(BT_HDR* packet) { |
| uint8_t type = 0; |
| |
| CHECK(bt_vendor_fd != -1); |
| |
| uint16_t event = packet->event & MSG_EVT_MASK; |
| switch (event & MSG_EVT_MASK) { |
| case MSG_STACK_TO_HC_HCI_CMD: |
| type = 1; |
| break; |
| case MSG_STACK_TO_HC_HCI_ACL: |
| type = 2; |
| break; |
| case MSG_STACK_TO_HC_HCI_SCO: |
| type = 3; |
| break; |
| default: |
| LOG(FATAL) << "Unknown packet type " << event; |
| break; |
| } |
| |
| uint8_t* addr = packet->data + packet->offset - 1; |
| uint8_t store = *addr; |
| *addr = type; |
| size_t ret = write(bt_vendor_fd, addr, packet->len + 1); |
| |
| *(addr) = store; |
| |
| if (ret != packet->len + 1) LOG(ERROR) << "Should have send whole packet"; |
| |
| if (ret == -1) PLOG(FATAL) << "write failed"; |
| } |
| |
| static int wait_hcidev(void) { |
| struct sockaddr_hci addr; |
| struct pollfd fds[1]; |
| struct mgmt_pkt ev; |
| int fd; |
| int ret = 0; |
| |
| LOG(INFO) << __func__; |
| |
| fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); |
| if (fd < 0) { |
| PLOG(ERROR) << "Bluetooth socket error"; |
| return -1; |
| } |
| |
| memset(&addr, 0, sizeof(addr)); |
| addr.hci_family = AF_BLUETOOTH; |
| addr.hci_dev = HCI_DEV_NONE; |
| addr.hci_channel = HCI_CHANNEL_CONTROL; |
| |
| if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { |
| PLOG(ERROR) << "HCI Channel Control"; |
| close(fd); |
| return -1; |
| } |
| |
| fds[0].fd = fd; |
| fds[0].events = POLLIN; |
| |
| /* Read Controller Index List Command */ |
| ev.opcode = MGMT_OP_INDEX_LIST; |
| ev.index = HCI_DEV_NONE; |
| ev.len = 0; |
| |
| ssize_t wrote; |
| OSI_NO_INTR(wrote = write(fd, &ev, 6)); |
| if (wrote != 6) { |
| PLOG(ERROR) << "Unable to write mgmt command"; |
| ret = -1; |
| goto end; |
| } |
| |
| while (1) { |
| int n; |
| OSI_NO_INTR(n = poll(fds, 1, MGMT_EV_POLL_TIMEOUT)); |
| if (n == -1) { |
| PLOG(ERROR) << "Poll error"; |
| ret = -1; |
| break; |
| } else if (n == 0) { |
| LOG(ERROR) << "Timeout, no HCI device detected"; |
| ret = -1; |
| break; |
| } |
| |
| if (fds[0].revents & POLLIN) { |
| OSI_NO_INTR(n = read(fd, &ev, sizeof(struct mgmt_pkt))); |
| if (n < 0) { |
| PLOG(ERROR) << "Error reading control channel"; |
| ret = -1; |
| break; |
| } |
| |
| if (ev.opcode == MGMT_EV_INDEX_ADDED && ev.index == hci_interface) { |
| goto end; |
| } else if (ev.opcode == MGMT_EV_COMMAND_COMP) { |
| struct mgmt_event_read_index* cc; |
| int i; |
| |
| cc = (struct mgmt_event_read_index*)ev.data; |
| |
| if (cc->cc_opcode != MGMT_OP_INDEX_LIST || cc->status != 0) continue; |
| |
| for (i = 0; i < cc->num_intf; i++) { |
| if (cc->index[i] == hci_interface) goto end; |
| } |
| } |
| } |
| } |
| |
| end: |
| close(fd); |
| return ret; |
| } |
| |
| static int rfkill(int block) { |
| struct rfkill_event event; |
| int fd; |
| |
| LOG(INFO) << __func__; |
| |
| fd = open("/dev/rfkill", O_WRONLY); |
| if (fd < 0) { |
| LOG(ERROR) << "Unable to open /dev/rfkill"; |
| return -1; |
| } |
| |
| memset(&event, 0, sizeof(struct rfkill_event)); |
| event.op = RFKILL_OP_CHANGE_ALL; |
| event.type = RFKILL_TYPE_BLUETOOTH; |
| event.hard = block; |
| event.soft = block; |
| |
| ssize_t len; |
| OSI_NO_INTR(len = write(fd, &event, sizeof(event))); |
| if (len < 0) { |
| LOG(ERROR) << "Failed to change rfkill state"; |
| close(fd); |
| return 1; |
| } |
| |
| close(fd); |
| return 0; |
| } |
| |
| int hci_open_firmware_log_file() { return INVALID_FD; } |
| |
| void hci_close_firmware_log_file(int fd) {} |
| |
| void hci_log_firmware_debug_packet(int fd, BT_HDR* packet) {} |