blob: 661fe0a8d602c39b1d548cc6425e134653475c7e [file] [log] [blame]
// Copyright 2022 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.
#include <lib/sync/condition.h>
#include <lib/sync/mutex.h>
#include <acpica/acpi.h>
// Semaphore implementation using condvar + mutex.
struct AcpiSemaphore {
public:
explicit AcpiSemaphore(uint32_t initial_count) : count_(initial_count) {}
void Wait(uint32_t units) {
sync_mutex_lock(&mutex_);
while (count_ < units) {
sync_condition_wait(&condition_, &mutex_);
}
sync_mutex_unlock(&mutex_);
}
ACPI_STATUS WaitWithDeadline(uint32_t units, zx_time_t deadline) {
zx_status_t result = sync_mutex_timedlock(&mutex_, deadline);
if (result == ZX_ERR_TIMED_OUT) {
return AE_TIME;
}
sync_mutex_assert_held(&mutex_);
while (result != ZX_ERR_TIMED_OUT && count_ < units && zx_clock_get_monotonic() < deadline) {
result = sync_condition_timedwait(&condition_, &mutex_, deadline);
}
if (result == ZX_ERR_TIMED_OUT) {
sync_mutex_unlock(&mutex_);
return AE_TIME;
}
count_ -= units;
sync_mutex_unlock(&mutex_);
return AE_OK;
}
void Signal(uint32_t units) {
sync_mutex_lock(&mutex_);
count_ += units;
if (units == 1) {
sync_condition_signal(&condition_);
} else {
sync_condition_broadcast(&condition_);
}
sync_mutex_unlock(&mutex_);
}
private:
sync_condition_t condition_;
sync_mutex_t mutex_;
uint32_t count_ __TA_GUARDED(mutex_);
};
/**
* @brief Create a semaphore.
*
* @param MaxUnits The maximum number of units this semaphore will be required
* to accept
* @param InitialUnits The initial number of units to be assigned to the
* semaphore.
* @param OutHandle A pointer to a locaton where a handle to the semaphore is
* to be returned.
*
* @return AE_OK The semaphore was successfully created.
* @return AE_BAD_PARAMETER The InitialUnits is invalid or the OutHandle
* pointer is NULL.
* @return AE_NO_MEMORY Insufficient memory to create the semaphore.
*/
ACPI_STATUS AcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_SEMAPHORE* OutHandle) {
AcpiSemaphore* sem = new AcpiSemaphore(InitialUnits);
if (!sem) {
return AE_NO_MEMORY;
}
*OutHandle = sem;
return AE_OK;
}
/**
* @brief Delete a semaphore.
*
* @param Handle A handle to a semaphore objected that was returned by a
* previous call to AcpiOsCreateSemaphore.
*
* @return AE_OK The semaphore was successfully deleted.
*/
ACPI_STATUS AcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle) {
delete Handle;
return AE_OK;
}
/**
* @brief Wait for units from a semaphore.
*
* @param Handle A handle to a semaphore objected that was returned by a
* previous call to AcpiOsCreateSemaphore.
* @param Units The number of units the caller is requesting.
* @param Timeout How long the caller is willing to wait for the requested
* units, in milliseconds. A value of -1 indicates that the caller
* is willing to wait forever. Timeout may be 0.
*
* @return AE_OK The requested units were successfully received.
* @return AE_BAD_PARAMETER The Handle is invalid.
* @return AE_TIME The units could not be acquired within the specified time.
*/
ACPI_STATUS AcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout) {
if (Timeout == UINT16_MAX) {
Handle->Wait(Units);
return AE_OK;
}
zx_time_t deadline = zx_deadline_after(ZX_MSEC(Timeout));
return Handle->WaitWithDeadline(Units, deadline);
}
/**
* @brief Send units to a semaphore.
*
* @param Handle A handle to a semaphore objected that was returned by a
* previous call to AcpiOsCreateSemaphore.
* @param Units The number of units to send to the semaphore.
*
* @return AE_OK The semaphore was successfully signaled.
* @return AE_BAD_PARAMETER The Handle is invalid.
*/
ACPI_STATUS AcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units) {
Handle->Signal(Units);
return AE_OK;
}