blob: 6c0189e9d39a574feec3e7b4053b2762ebaea35c [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 <ddk/debug.h>
#include <ddk/metadata.h>
#include <fbl/alloc_checker.h>
#include "src/devices/pwm/drivers/pwm/pwm-bind.h"
namespace pwm {
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;
}
size_t metadata_size;
status = device_get_metadata_size(parent, DEVICE_METADATA_PWM_IDS, &metadata_size);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: device_get_metadata_size failed %d", __FILE__, status);
return status;
}
auto pwm_count = metadata_size / sizeof(pwm_id_t);
fbl::AllocChecker ac;
std::unique_ptr<pwm_id_t[]> pwm_ids(new (&ac) pwm_id_t[pwm_count]);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
size_t actual;
status =
device_get_metadata(parent, DEVICE_METADATA_PWM_IDS, pwm_ids.get(), metadata_size, &actual);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: device_get_metadata failed %d", __FILE__, status);
return status;
}
if (actual != metadata_size) {
zxlogf(ERROR, "%s: device_get_metadata size error %d", __FILE__, status);
return ZX_ERR_INTERNAL;
}
for (uint32_t i = 0; i < pwm_count; i++) {
auto pwm_id = pwm_ids[i];
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) {
return pwm_.GetConfig(id_.id, out_config);
}
zx_status_t PwmDevice::PwmSetConfig(const pwm_config_t* config) {
if (id_.protect) {
return ZX_ERR_ACCESS_DENIED;
}
return pwm_.SetConfig(id_.id, config);
}
zx_status_t PwmDevice::PwmEnable() {
if (id_.protect) {
return ZX_ERR_ACCESS_DENIED;
}
return pwm_.Enable(id_.id);
}
zx_status_t PwmDevice::PwmDisable() {
if (id_.protect) {
return ZX_ERR_ACCESS_DENIED;
}
return pwm_.Disable(id_.id);
}
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");