//
// Copyright 2016 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.
//

#define LOG_TAG "android.hardware.bluetooth@1.0-btlinux"
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>

#include <utils/Log.h>

#include "bluetooth_hci.h"

#define BTPROTO_HCI 1

#define HCI_CHANNEL_USER 1
#define HCI_CHANNEL_CONTROL 3
#define HCI_DEV_NONE 0xffff

/* reference from <kernel>/include/net/bluetooth/mgmt.h */
#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 WRITE_NO_INTR(fn) \
  do {                  \
  } while ((fn) == -1 && errno == EINTR)

struct sockaddr_hci {
    sa_family_t hci_family;
    unsigned short hci_dev;
    unsigned short hci_channel;
};

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

namespace android {
namespace hardware {
namespace bluetooth {
namespace V1_0 {
namespace btlinux {

int BluetoothHci::openBtHci() {

  ALOGI( "%s", __func__);

  int hci_interface = 0;
  rfkill_state_ = NULL;
  rfKill(1);

  int fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
  if (fd < 0) {
    ALOGE( "Bluetooth socket error: %s", strerror(errno));
    return -1;
  }
  bt_soc_fd_ = fd;

  if (waitHciDev(hci_interface)) {
    ALOGE( "HCI interface (%d) not found", hci_interface);
    ::close(fd);
    return -1;
  }
  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) {
    ALOGE( "HCI Channel Control: %s", strerror(errno));
    ::close(fd);
    return -1;
  }
  ALOGI( "HCI device ready");
  return fd;
}

void BluetoothHci::closeBtHci() {
  if (bt_soc_fd_ != -1) {
    ::close(bt_soc_fd_);
    bt_soc_fd_ = -1;
  }
  rfKill(0);
  free(rfkill_state_);
}

int BluetoothHci::waitHciDev(int hci_interface) {
  struct sockaddr_hci addr;
  struct pollfd fds[1];
  struct mgmt_pkt ev;
  int fd;
  int ret = 0;

  ALOGI( "%s", __func__);
  fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
  if (fd < 0) {
    ALOGE( "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) {
    ALOGE( "HCI Channel Control: %s", strerror(errno));
    ret = -1;
    goto end;
  }

  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;
  WRITE_NO_INTR(wrote = write(fd, &ev, 6));
  if (wrote != 6) {
    ALOGE( "Unable to write mgmt command: %s", strerror(errno));
    ret = -1;
    goto end;
  }
  /* validate mentioned hci interface is present and registered with sock system */
  while (1) {
    int n;
    WRITE_NO_INTR(n = poll(fds, 1, MGMT_EV_POLL_TIMEOUT));
    if (n == -1) {
      ALOGE( "Poll error: %s", strerror(errno));
      ret = -1;
      break;
    } else if (n == 0) {
      ALOGE( "Timeout, no HCI device detected");
      ret = -1;
      break;
    }

    if (fds[0].revents & POLLIN) {
      WRITE_NO_INTR(n = read(fd, &ev, sizeof(struct mgmt_pkt)));
      if (n < 0) {
        ALOGE( "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;
}

int BluetoothHci::findRfKill() {
    char rfkill_type[64];
    char type[16];
    int fd, size, i;
    for(i = 0; rfkill_state_ == NULL; i++)
    {
        snprintf(rfkill_type, sizeof(rfkill_type), "/sys/class/rfkill/rfkill%d/type", i);
        if ((fd = open(rfkill_type, O_RDONLY)) < 0)
        {
            ALOGE("open(%s) failed: %s (%d)\n", rfkill_type, strerror(errno), errno);
            return -1;
        }

        size = read(fd, &type, sizeof(type));
        ::close(fd);

        if ((size >= 9) && !memcmp(type, "bluetooth", 9))
        {
            ::asprintf(&rfkill_state_, "/sys/class/rfkill/rfkill%d/state", i);
            break;
        }
    }
    return 0;
}

int BluetoothHci::rfKill(int block) {
  int fd;
  char on = (block)?'1':'0';
  if (findRfKill() != 0) return 0;

  fd = open(rfkill_state_, O_WRONLY);
  if (fd < 0) {
    ALOGE( "Unable to open /dev/rfkill");
    return -1;
  }
  ssize_t len;
  WRITE_NO_INTR(len = write(fd, &on, 1));
  if (len < 0) {
    ALOGE( "Failed to change rfkill state");
    ::close(fd);
    return -1;
  }
  ::close(fd);
  return 0;
}

class BluetoothDeathRecipient : public hidl_death_recipient {
 public:
  BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}

  virtual void serviceDied(
      uint64_t /*cookie*/,
      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
    ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
    has_died_ = true;
    mHci->close();
  }
  sp<IBluetoothHci> mHci;
  bool getHasDied() const { return has_died_; }
  void setHasDied(bool has_died) { has_died_ = has_died; }

 private:
  bool has_died_;
};

BluetoothHci::BluetoothHci()
    : death_recipient_(new BluetoothDeathRecipient(this)) {}

Return<void> BluetoothHci::initialize(
    const ::android::sp<IBluetoothHciCallbacks>& cb) {
  ALOGI("BluetoothHci::initialize()");
  if (cb == nullptr) {
    ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
    return Void();
  }

  death_recipient_->setHasDied(false);
  cb->linkToDeath(death_recipient_, 0);
  int hci_fd = openBtHci();
  auto hidl_status = cb->initializationComplete(
          hci_fd > 0 ? Status::SUCCESS : Status::INITIALIZATION_ERROR);
  if (!hidl_status.isOk()) {
      ALOGE("VendorInterface -> Unable to call initializationComplete(ERR)");
  }
  hci::H4Protocol* h4_hci = new hci::H4Protocol(
      hci_fd,
      [cb](const hidl_vec<uint8_t>& packet) { cb->hciEventReceived(packet); },
      [cb](const hidl_vec<uint8_t>& packet) { cb->aclDataReceived(packet); },
      [cb](const hidl_vec<uint8_t>& packet) { cb->scoDataReceived(packet); });

  fd_watcher_.WatchFdForNonBlockingReads(
          hci_fd, [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
  hci_handle_ = h4_hci;

  unlink_cb_ = [cb](sp<BluetoothDeathRecipient>& death_recipient) {
    if (death_recipient->getHasDied())
      ALOGI("Skipping unlink call, service died.");
    else
      cb->unlinkToDeath(death_recipient);
  };

  return Void();
}

Return<void> BluetoothHci::close() {
  ALOGI("BluetoothHci::close()");
  unlink_cb_(death_recipient_);
  fd_watcher_.StopWatchingFileDescriptors();

  if (hci_handle_ != nullptr) {
    delete hci_handle_;
    hci_handle_ = nullptr;
  }
  closeBtHci();
  return Void();
}

Return<void> BluetoothHci::sendHciCommand(const hidl_vec<uint8_t>& command) {
  sendDataToController(HCI_DATA_TYPE_COMMAND, command);
  return Void();
}

Return<void> BluetoothHci::sendAclData(const hidl_vec<uint8_t>& data) {
  sendDataToController(HCI_DATA_TYPE_ACL, data);
  return Void();
}

Return<void> BluetoothHci::sendScoData(const hidl_vec<uint8_t>& data) {
  sendDataToController(HCI_DATA_TYPE_SCO, data);
  return Void();
}

void BluetoothHci::sendDataToController(const uint8_t type,
                                        const hidl_vec<uint8_t>& data) {
  hci_handle_->Send(type, data.data(), data.size());
}

IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* /* name */) {
  return new BluetoothHci();
}

}  // namespace btlinux
}  // namespace V1_0
}  // namespace bluetooth
}  // namespace hardware
}  // namespace android
