blob: 8d714c8d94042f34ecfbf4ee232cc28d7044e3b7 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_BOARD_DRIVERS_X86_INCLUDE_ACPI_PRIVATE_H_
#define SRC_DEVICES_BOARD_DRIVERS_X86_INCLUDE_ACPI_PRIVATE_H_
#include <fuchsia/hardware/acpi/cpp/banjo.h>
#include <lib/fitx/result.h>
#include <vector>
#include <ddk/binding.h>
#include <ddktl/device.h>
#include <fbl/mutex.h>
#include "resources.h"
#define MAX_NAMESPACE_DEPTH 100
namespace acpi {
namespace internal {
// utility functions used to implement ExtractHidToDevProps and
// ExtractCidToDevProps (below)
static inline uint32_t ExtractPnpIdWord(const ACPI_PNP_DEVICE_ID& id, size_t offset) {
auto buf = reinterpret_cast<const char*>(id.String);
auto buf_len = static_cast<size_t>(id.Length);
if (offset >= buf_len) {
return 0;
}
size_t i;
size_t avail = buf_len - offset;
uint32_t ret = buf[offset];
for (i = 1; i < std::min(avail, sizeof(uint32_t)); ++i) {
ret = (ret << 8) | buf[offset + i];
}
ret <<= (sizeof(uint32_t) - i) * 8;
return ret;
}
template <typename T>
static inline size_t UnusedPropsCount(const T& props, uint32_t propcount) {
ZX_DEBUG_ASSERT(propcount <= std::size(props));
return std::size(props) - std::min<size_t>(propcount, std::size(props));
}
} // namespace internal
// An RAII unique pointer type for resources allocated from the ACPICA library.
template <typename T>
struct UniquePtrDeleter {
void operator()(T* mem) { ACPI_FREE(mem); }
};
template <typename T>
using UniquePtr = std::unique_ptr<T, UniquePtrDeleter<T>>;
// A free standing function which can be used to fetch the Info structure of an
// ACPI device. It returns a fitx::result which either holds a managed pointer
// to the info object, or an ACPI error code in the case of failure.
fitx::result<ACPI_STATUS, UniquePtr<ACPI_DEVICE_INFO>> GetObjectInfo(ACPI_HANDLE obj);
struct DevicePioResource {
explicit DevicePioResource(const resource_io& io)
: base_address{io.minimum}, alignment{io.alignment}, address_length{io.address_length} {}
uint32_t base_address;
uint32_t alignment;
uint32_t address_length;
};
struct DeviceMmioResource {
DeviceMmioResource(bool writeable, uint32_t base_address, uint32_t alignment,
uint32_t address_length)
: writeable{writeable},
base_address{base_address},
alignment{alignment},
address_length{address_length} {}
explicit DeviceMmioResource(const resource_memory_t& mem)
: DeviceMmioResource{mem.writeable, mem.minimum, mem.alignment, mem.address_length} {}
bool writeable;
uint32_t base_address;
uint32_t alignment;
uint32_t address_length;
};
struct DeviceIrqResource {
DeviceIrqResource(const resource_irq irq, int pin_index)
: trigger{irq.trigger},
polarity{irq.polarity},
sharable{irq.sharable},
wake_capable{irq.wake_capable},
pin{static_cast<uint8_t>(irq.pins[pin_index])} {}
uint8_t trigger;
#define ACPI_IRQ_TRIGGER_LEVEL 0
#define ACPI_IRQ_TRIGGER_EDGE 1
uint8_t polarity;
#define ACPI_IRQ_ACTIVE_HIGH 0
#define ACPI_IRQ_ACTIVE_LOW 1
#define ACPI_IRQ_ACTIVE_BOTH 2
uint8_t sharable;
#define ACPI_IRQ_EXCLUSIVE 0
#define ACPI_IRQ_SHARED 1
uint8_t wake_capable;
uint8_t pin;
};
class Device;
using DeviceType = ddk::Device<::acpi::Device>;
class Device : public DeviceType, public ddk::AcpiProtocol<Device, ddk::base_protocol> {
public:
Device(zx_device_t* parent, ACPI_HANDLE acpi_handle, zx_device_t* platform_bus)
: DeviceType{parent}, acpi_handle_{acpi_handle}, platform_bus_{platform_bus} {}
// DDK mix-in impls.
void DdkRelease() { delete this; }
ACPI_HANDLE acpi_handle() const { return acpi_handle_; }
zx_device_t* platform_bus() const { return platform_bus_; }
zx_device_t** mutable_zxdev() { return &zxdev_; }
zx_status_t AcpiGetPio(uint32_t index, zx::resource* out_pio);
zx_status_t AcpiGetMmio(uint32_t index, acpi_mmio* out_mmio);
zx_status_t AcpiMapInterrupt(int64_t which_irq, zx::interrupt* handle);
zx_status_t AcpiGetBti(uint32_t bdf, uint32_t index, zx::bti* bti);
zx_status_t AcpiConnectSysmem(zx::channel connection);
zx_status_t AcpiRegisterSysmemHeap(uint64_t heap, zx::channel connection);
private:
// Handle to the corresponding ACPI node
ACPI_HANDLE acpi_handle_;
zx_device_t* platform_bus_;
mutable fbl::Mutex lock_;
bool got_resources_ = false;
// Port, memory, and interrupt resources from _CRS respectively
std::vector<DevicePioResource> pio_resources_;
std::vector<DeviceMmioResource> mmio_resources_;
std::vector<DeviceIrqResource> irqs_;
zx_status_t ReportCurrentResources();
ACPI_STATUS AddResource(ACPI_RESOURCE*);
};
// A utility function which can be used to invoke the ACPICA library's
// AcpiWalkNamespace function, but with an arbitrary Callable instead of needing
// to use C-style callbacks with context pointers.
enum class WalkDirection {
Descending,
Ascending,
};
template <typename Callable>
ACPI_STATUS WalkNamespace(ACPI_OBJECT_TYPE type, ACPI_HANDLE start_object, uint32_t max_depth,
Callable cbk) {
auto Descent = [](ACPI_HANDLE object, uint32_t level, void* ctx, void**) -> ACPI_STATUS {
return (*static_cast<Callable*>(ctx))(object, level, WalkDirection::Descending);
};
auto Ascent = [](ACPI_HANDLE object, uint32_t level, void* ctx, void**) -> ACPI_STATUS {
return (*static_cast<Callable*>(ctx))(object, level, WalkDirection::Ascending);
};
return ::AcpiWalkNamespace(type, start_object, max_depth, Descent, Ascent, &cbk, nullptr);
}
// A utility function which can be used to invoke the ACPICA library's
// AcpiWalkResources function, but with an arbitrary Callable instead of needing
// to use C-style callbacks with context pointers.
template <typename Callable>
ACPI_STATUS WalkResources(ACPI_HANDLE object, const char* resource_name, Callable cbk) {
auto Thunk = [](ACPI_RESOURCE* res, void* ctx) -> ACPI_STATUS {
return (*static_cast<Callable*>(ctx))(res);
};
return ::AcpiWalkResources(object, const_cast<char*>(resource_name), Thunk, &cbk);
}
// ExtractHidToDevProps and ExtractCidToDevProps
//
// These functions will take an ACPI_DEVICE_INFO structure, and attempt to
// extract the Hardware ID or first available Compatiblity ID from the device
// (if present), and pack the data (space permitting) into an array of device
// properties which exists in an arbitrary structure provided by the user.
//
// The string HID/CID provided by ACPI will be packed into a pair of uint32_t in
// a big-endian fashion, with 0x00 being used as a filler in the case that the
// identifier is too short. Appropriate device property type labels will be
// applied to the dev_props structure.
//
// ACPI IDs which are longer than 8 bytes are currently unsupported as there is
// no good way to represent them with the existing devprops binding scheme.
//
template <typename T>
zx_status_t ExtractHidToDevProps(const ACPI_DEVICE_INFO& info, T& props, uint32_t& propcount) {
// If we have no HID to extract, then just do nothing. This is not
// considered to be an error.
if (!((info.Valid & ACPI_VALID_HID) && (info.HardwareId.Length > 0))) {
return ZX_OK;
}
const ACPI_PNP_DEVICE_ID& pnp_id = info.HardwareId;
// We cannot currently handle any IDs which would be longer than 8 bytes
// (not including the null termination of the string)
if ((pnp_id.Length - 1) > sizeof(uint64_t)) {
return ZX_ERR_INTERNAL;
}
// Make sure we have the space to extract the info.
if (internal::UnusedPropsCount(props, propcount) < 2) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
props[propcount].id = BIND_ACPI_HID_0_3;
props[propcount++].value = internal::ExtractPnpIdWord(pnp_id, 0);
props[propcount].id = BIND_ACPI_HID_4_7;
props[propcount++].value = internal::ExtractPnpIdWord(pnp_id, 4);
return ZX_OK;
}
template <typename T>
zx_status_t ExtractCidToDevProps(const ACPI_DEVICE_INFO& info, T& props, uint32_t& propcount) {
// If we have no CID to extract, then just do nothing. This is not
// considered to be an error.
if (!((info.Valid & ACPI_VALID_CID) && (info.CompatibleIdList.Count > 0) &&
(info.CompatibleIdList.Ids[0].Length > 0))) {
return ZX_OK;
}
const ACPI_PNP_DEVICE_ID& pnp_id = info.CompatibleIdList.Ids[0];
// We cannot currently handle any IDs which would be longer than 8 bytes
// (not including the null termination of the string)
if ((pnp_id.Length - 1) > sizeof(uint64_t)) {
return ZX_ERR_INTERNAL;
}
// Make sure we have the space to extract the info.
if (internal::UnusedPropsCount(props, propcount) < 2) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
props[propcount].id = BIND_ACPI_CID_0_3;
props[propcount++].value = internal::ExtractPnpIdWord(pnp_id, 0);
props[propcount].id = BIND_ACPI_CID_4_7;
props[propcount++].value = internal::ExtractPnpIdWord(pnp_id, 4);
return ZX_OK;
}
} // namespace acpi
// TODO(cja): this is here because of kpci.cc and can be removed once
// kernel pci is out of the tree.
device_add_args_t get_device_add_args(const char* name, ACPI_DEVICE_INFO* info,
std::array<zx_device_prop_t, 4>* out_props);
const zx_protocol_device_t* get_acpi_root_device_proto(void);
#endif // SRC_DEVICES_BOARD_DRIVERS_X86_INCLUDE_ACPI_PRIVATE_H_