Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit faae707c authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Generic Bluetooth HAL for standard Linux hci interface"

parents b7ef1fda be37a609
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
subdirs = [
    "test_vendor_lib",
    "linux",
]
+3 −0
Original line number Diff line number Diff line
subdirs = [
    "interface",
]
+0 −47
Original line number Diff line number Diff line
#
#  Copyright (C) 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.
#

LOCAL_PATH := $(call my-dir)

ifeq ($(BOARD_HAVE_BLUETOOTH_LINUX), true)

# libbt-vendor shared library for target
# ========================================================
include $(CLEAR_VARS)

LOCAL_CPP_EXTENSION := .cc

LOCAL_SRC_FILES := \
        bt_vendor_linux.cc

LOCAL_C_INCLUDES := \
        $(LOCAL_PATH)/../../

LOCAL_SHARED_LIBRARIES := \
        liblog \
        libcutils

LOCAL_STATIC_LIBRARIES := libosi

LOCAL_MODULE := libbt-vendor
LOCAL_MODULE_TAGS := optional

LOCAL_CFLAGS += $(test-vendor_CFLAGS)
LOCAL_CONLYFLAGS += $(test-vendor_CONLYFLAGS)

include $(BUILD_SHARED_LIBRARY)

endif  # BOARD_HAVE_BLUETOOTH_LINUX
+0 −411
Original line number Diff line number Diff line
/**********************************************************************
 *
 *  Copyright (C) 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.
 *
 **********************************************************************/

#define LOG_TAG "bt_vendor"

#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <sys/ioctl.h>
#include <sys/socket.h>

#include "hci/include/bt_vendor_lib.h"
#include "osi/include/compat.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
#include "osi/include/properties.h"

#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 */

#define IOCTL_HCIDEVDOWN _IOW('H', 202, int)

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));

static const bt_vendor_callbacks_t* bt_vendor_callbacks;
static unsigned char bt_vendor_local_bdaddr[6];
static int bt_vendor_fd = -1;
static int hci_interface;
static int rfkill_en;
static int bt_hwcfg_en;

static int bt_vendor_init(const bt_vendor_callbacks_t* p_cb,
                          unsigned char* local_bdaddr) {
  char prop_value[PROPERTY_VALUE_MAX];

  LOG_INFO(LOG_TAG, "%s", __func__);

  if (p_cb == NULL) {
    LOG_ERROR(LOG_TAG, "init failed with no user callbacks!");
    return -1;
  }

  bt_vendor_callbacks = p_cb;

  memcpy(bt_vendor_local_bdaddr, local_bdaddr, sizeof(bt_vendor_local_bdaddr));

  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(LOG_TAG, "Using interface hci%d", hci_interface);

  osi_property_get("bluetooth.rfkill", prop_value, "0");

  rfkill_en = atoi(prop_value);
  if (rfkill_en) LOG_INFO(LOG_TAG, "RFKILL enabled");

  bt_hwcfg_en =
      osi_property_get("bluetooth.hwcfg", prop_value, NULL) > 0 ? 1 : 0;
  if (bt_hwcfg_en) LOG_INFO(LOG_TAG, "HWCFG enabled");

  return 0;
}

static int bt_vendor_hw_cfg(int stop) {
  if (!bt_hwcfg_en) return 0;

  if (stop) {
    if (osi_property_set("bluetooth.hwcfg", "stop") < 0) {
      LOG_ERROR(LOG_TAG, "%s cannot stop btcfg service via prop", __func__);
      return 1;
    }
  } else {
    if (osi_property_set("bluetooth.hwcfg", "start") < 0) {
      LOG_ERROR(LOG_TAG, "%s cannot start btcfg service via prop", __func__);
      return 1;
    }
  }
  return 0;
}

static int bt_vendor_wait_hcidev(void) {
  struct sockaddr_hci addr;
  struct pollfd fds[1];
  struct mgmt_pkt ev;
  int fd;
  int ret = 0;

  LOG_INFO(LOG_TAG, "%s", __func__);

  fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
  if (fd < 0) {
    LOG_ERROR(LOG_TAG, "Bluetooth socket error: %s", strerror(errno));
    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) {
    LOG_ERROR(LOG_TAG, "HCI Channel Control: %s", strerror(errno));
    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) {
    LOG_ERROR(LOG_TAG, "Unable to write mgmt command: %s", strerror(errno));
    ret = -1;
    goto end;
  }

  while (1) {
    int n;
    OSI_NO_INTR(n = poll(fds, 1, MGMT_EV_POLL_TIMEOUT));
    if (n == -1) {
      LOG_ERROR(LOG_TAG, "Poll error: %s", strerror(errno));
      ret = -1;
      break;
    } else if (n == 0) {
      LOG_ERROR(LOG_TAG, "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) {
        LOG_ERROR(LOG_TAG, "Error reading control channel: %s",
                  strerror(errno));
        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 bt_vendor_open(void* param) {
  int(*fd_array)[] = (int(*)[])param;
  int fd;

  LOG_INFO(LOG_TAG, "%s", __func__);

  fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
  if (fd < 0) {
    LOG_ERROR(LOG_TAG, "socket create error %s", strerror(errno));
    return -1;
  }

  (*fd_array)[CH_CMD] = fd;
  (*fd_array)[CH_EVT] = fd;
  (*fd_array)[CH_ACL_OUT] = fd;
  (*fd_array)[CH_ACL_IN] = fd;

  bt_vendor_fd = fd;

  LOG_INFO(LOG_TAG, "%s returning %d", __func__, bt_vendor_fd);

  return 1;
}

static int bt_vendor_close(void* param) {
  (void)(param);

  LOG_INFO(LOG_TAG, "%s", __func__);

  if (bt_vendor_fd != -1) {
    close(bt_vendor_fd);
    bt_vendor_fd = -1;
  }

  return 0;
}

static int bt_vendor_rfkill(int block) {
  struct rfkill_event event;
  int fd;

  LOG_INFO(LOG_TAG, "%s", __func__);

  fd = open("/dev/rfkill", O_WRONLY);
  if (fd < 0) {
    LOG_ERROR(LOG_TAG, "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(LOG_TAG, "Failed to change rfkill state");
    close(fd);
    return 1;
  }

  close(fd);
  return 0;
}

/* TODO: fw config should thread the device waiting and return immedialty */
static void bt_vendor_fw_cfg(void) {
  struct sockaddr_hci addr;
  int fd = bt_vendor_fd;

  LOG_INFO(LOG_TAG, "%s", __func__);

  if (fd == -1) {
    LOG_ERROR(LOG_TAG, "bt_vendor_fd: %s", strerror(EBADF));
    goto failure;
  }

  memset(&addr, 0, sizeof(addr));
  addr.hci_family = AF_BLUETOOTH;
  addr.hci_dev = hci_interface;
  addr.hci_channel = HCI_CHANNEL_USER;

  if (bt_vendor_wait_hcidev()) {
    LOG_ERROR(LOG_TAG, "HCI interface (%d) not found", hci_interface);
    goto failure;
  }

  if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
    LOG_ERROR(LOG_TAG, "socket bind error %s", strerror(errno));
    goto failure;
  }

  LOG_INFO(LOG_TAG, "HCI device ready");

  bt_vendor_callbacks->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);

  return;

failure:
  LOG_ERROR(LOG_TAG, "Hardware Config Error");
  bt_vendor_callbacks->fwcfg_cb(BT_VND_OP_RESULT_FAIL);
}

static int bt_vendor_op(bt_vendor_opcode_t opcode, void* param) {
  int retval = 0;

  LOG_INFO(LOG_TAG, "%s op %d", __func__, opcode);

  switch (opcode) {
    case BT_VND_OP_POWER_CTRL:
      if (!rfkill_en || !param) break;

      if (*((int*)param) == BT_VND_PWR_ON) {
        retval = bt_vendor_rfkill(0);
        if (!retval) retval = bt_vendor_hw_cfg(0);
      } else {
        retval = bt_vendor_hw_cfg(1);
        if (!retval) retval = bt_vendor_rfkill(1);
      }

      break;

    case BT_VND_OP_FW_CFG:
      bt_vendor_fw_cfg();
      break;

    case BT_VND_OP_SCO_CFG:
      bt_vendor_callbacks->scocfg_cb(BT_VND_OP_RESULT_SUCCESS);
      break;

    case BT_VND_OP_USERIAL_OPEN:
      retval = bt_vendor_open(param);
      break;

    case BT_VND_OP_USERIAL_CLOSE:
      retval = bt_vendor_close(param);
      break;

    case BT_VND_OP_GET_LPM_IDLE_TIMEOUT:
      *((uint32_t*)param) = 3000;
      retval = 0;
      break;

    case BT_VND_OP_LPM_SET_MODE:
      bt_vendor_callbacks->lpm_cb(BT_VND_OP_RESULT_SUCCESS);
      break;

    case BT_VND_OP_LPM_WAKE_SET_STATE:
      break;

    case BT_VND_OP_SET_AUDIO_STATE:
      bt_vendor_callbacks->audio_state_cb(BT_VND_OP_RESULT_SUCCESS);
      break;

    case BT_VND_OP_EPILOG:
      bt_vendor_callbacks->epilog_cb(BT_VND_OP_RESULT_SUCCESS);
      break;

    case BT_VND_OP_A2DP_OFFLOAD_START:
      break;

    case BT_VND_OP_A2DP_OFFLOAD_STOP:
      break;
  }

  LOG_INFO(LOG_TAG, "%s op %d retval %d", __func__, opcode, retval);

  return retval;
}

static void bt_vendor_cleanup(void) {
  LOG_INFO(LOG_TAG, "%s", __func__);

  bt_vendor_callbacks = NULL;
}

EXPORT_SYMBOL const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE = {
    sizeof(bt_vendor_interface_t), bt_vendor_init, bt_vendor_op,
    bt_vendor_cleanup,
};
+42 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2017 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.

cc_binary {
    name: "android.hardware.bluetooth@1.0-service.btlinux",
    proprietary: true,
    relative_install_path: "hw",
    srcs: [
        "hci_packetizer.cc",
        "h4_protocol.cc",
        "bluetooth_hci.cc",
        "async_fd_watcher.cc",
        "service.cc"
    ],
    shared_libs: [
        "android.hardware.bluetooth@1.0",
        "libbase",
        "libcutils",
        "libhardware",
        "libhidlbase",
        "libhidltransport",
        "liblog",
        "libutils",
    ],
    conlyflags: [
        "-std=c99",
    ],
    init_rc: ["android.hardware.bluetooth@1.0-service.btlinux.rc"],
}
Loading