// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/ozone/platform/drm/host/drm_device_handle.h"

#include <fcntl.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

#include "base/files/file_path.h"
#include "base/posix/eintr_wrapper.h"
#include "base/threading/thread_restrictions.h"

namespace ui {

namespace {

bool Authenticate(int fd) {
  drm_magic_t magic;
  memset(&magic, 0, sizeof(magic));
  // We need to make sure the DRM device has enough privilege. Use the DRM
  // authentication logic to figure out if the device has enough permissions.
  return !drmGetMagic(fd, &magic) && !drmAuthMagic(fd, magic);
}

}  // namespace

DrmDeviceHandle::DrmDeviceHandle() {
}

DrmDeviceHandle::~DrmDeviceHandle() {
  if (file_.is_valid())
    base::ThreadRestrictions::AssertIOAllowed();
}

bool DrmDeviceHandle::Initialize(const base::FilePath& path) {
  CHECK(path.DirName() == base::FilePath("/dev/dri"));
  base::ThreadRestrictions::AssertIOAllowed();
  bool print_warning = true;
  while (true) {
    file_.reset();
    int fd = HANDLE_EINTR(open(path.value().c_str(), O_RDWR | O_CLOEXEC));
    if (fd < 0) {
      PLOG(ERROR) << "Failed to open " << path.value();
      return false;
    }

    file_.reset(fd);

    if (Authenticate(file_.get()))
      break;

    LOG_IF(WARNING, print_warning) << "Failed to authenticate " << path.value();
    print_warning = false;
    usleep(100000);
  }

  VLOG(1) << "Succeeded authenticating " << path.value();
  return true;
}

bool DrmDeviceHandle::IsValid() const {
  return file_.is_valid();
}

base::ScopedFD DrmDeviceHandle::PassFD() {
  return file_.Pass();
}

}  // namespace ui