blob: 9edfeba10617f8edb870817390f5f5884bedd46d [file] [log] [blame]
// Copyright 2019 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 "pwm.h"
#include <lib/ddk/debug.h>
#include <lib/ddk/metadata.h>
#include <fbl/alloc_checker.h>
#include "src/devices/pwm/drivers/pwm/pwm-bind.h"
namespace pwm {
constexpr size_t kMaxConfigBufferSize = 256;
zx_status_t PwmDevice::Create(void* ctx, zx_device_t* parent) {
pwm_impl_protocol_t pwm_proto;
auto status = device_get_protocol(parent, ZX_PROTOCOL_PWM_IMPL, &pwm_proto);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: device_get_protocol failed %d", __FILE__, status);
return status;
}
auto pwm_ids = ddk::GetMetadataArray<pwm_id_t>(parent, DEVICE_METADATA_PWM_IDS);
if (!pwm_ids.is_ok()) {
return pwm_ids.error_value();
}
for (auto pwm_id : *pwm_ids) {
fbl::AllocChecker ac;
std::unique_ptr<PwmDevice> dev(new (&ac) PwmDevice(parent, &pwm_proto, pwm_id));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
char name[20];
snprintf(name, sizeof(name), "pwm-%u", pwm_id.id);
zx_device_prop_t props[] = {
{BIND_PWM_ID, 0, pwm_id.id},
};
status = dev->DdkAdd(
ddk::DeviceAddArgs(name).set_flags(DEVICE_ADD_ALLOW_MULTI_COMPOSITE).set_props(props));
if (status != ZX_OK) {
return status;
}
// dev is now owned by devmgr.
__UNUSED auto ptr = dev.release();
}
return ZX_OK;
}
zx_status_t PwmDevice::PwmGetConfig(pwm_config_t* out_config) {
std::scoped_lock lock(lock_);
return pwm_.GetConfig(id_.id, out_config);
}
zx_status_t PwmDevice::PwmSetConfig(const pwm_config_t* config) {
std::scoped_lock lock(lock_);
if (id_.protect) {
return ZX_ERR_ACCESS_DENIED;
}
return pwm_.SetConfig(id_.id, config);
}
zx_status_t PwmDevice::PwmEnable() {
std::scoped_lock lock(lock_);
if (id_.protect) {
return ZX_ERR_ACCESS_DENIED;
}
return pwm_.Enable(id_.id);
}
zx_status_t PwmDevice::PwmDisable() {
std::scoped_lock lock(lock_);
if (id_.protect) {
return ZX_ERR_ACCESS_DENIED;
}
return pwm_.Disable(id_.id);
}
void PwmDevice::GetConfig(GetConfigRequestView request, GetConfigCompleter::Sync& completer) {
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(kMaxConfigBufferSize);
pwm_config_t config;
config.mode_config_buffer = buffer.get();
config.mode_config_size = kMaxConfigBufferSize;
zx_status_t status = PwmGetConfig(&config);
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
fuchsia_hardware_pwm::wire::PwmConfig result;
result.polarity = config.polarity;
result.period_ns = config.period_ns;
result.duty_cycle = config.duty_cycle;
result.mode_config =
fidl::VectorView<uint8_t>::FromExternal(config.mode_config_buffer, config.mode_config_size);
completer.ReplySuccess(result);
}
void PwmDevice::SetConfig(SetConfigRequestView request, SetConfigCompleter::Sync& completer) {
pwm_config_t new_config;
new_config.polarity = request->config.polarity;
new_config.period_ns = request->config.period_ns;
new_config.duty_cycle = request->config.duty_cycle;
new_config.mode_config_buffer = request->config.mode_config.mutable_data();
new_config.mode_config_size = request->config.mode_config.count();
zx_status_t result = PwmSetConfig(&new_config);
if (result != ZX_OK) {
completer.ReplyError(result);
} else {
completer.ReplySuccess();
}
}
void PwmDevice::Enable(EnableRequestView request, EnableCompleter::Sync& completer) {
zx_status_t result = PwmEnable();
if (result == ZX_OK) {
completer.ReplySuccess();
} else {
completer.ReplyError(result);
}
}
void PwmDevice::Disable(DisableRequestView request, DisableCompleter::Sync& completer) {
zx_status_t result = PwmDisable();
if (result == ZX_OK) {
completer.ReplySuccess();
} else {
completer.ReplyError(result);
}
}
static constexpr zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = PwmDevice::Create;
return ops;
}();
} // namespace pwm
ZIRCON_DRIVER(pwm, pwm::driver_ops, "zircon", "0.1");