blob: 366cd4a3b63e05759c9484c63cede47af70446a8 [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.
#include "ltr-578als.h"
#include <endian.h>
#include <memory>
#include <ddk/driver.h>
#include <ddk/platform-defs.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include "src/devices/light-sensor/drivers/lite-on/ltr-578als-bind.h"
namespace {
// These are the register values used by the existing Cleo code.
constexpr uint8_t kMainCtrlAddress = 0x00;
constexpr uint8_t kPsActiveBit = 0x01;
constexpr uint8_t kAlsActiveBit = 0x02;
constexpr uint8_t kPsLedAddress = 0x01;
constexpr uint8_t kPsLedFreq60Khz = 0x30;
constexpr uint8_t kPsLedCurrent100Ma = 0x06;
constexpr uint8_t kPsPulsesAddress = 0x02;
constexpr uint8_t kPsMeasRateAddress = 0x03;
constexpr uint8_t kPsMeasRate11Bit = 0x18;
constexpr uint8_t kPsMeasRate50Ms = 0x04;
constexpr uint8_t kAlsMeasRateAddress = 0x04;
constexpr uint8_t kAlsMeasRate18Bit = 0x20;
constexpr uint8_t kAlsMeasRate100Ms = 0x02;
constexpr uint8_t kAlsGainAddress = 0x05;
constexpr uint8_t kAlsGain1 = 0x00;
constexpr uint8_t kDefaultRegValues[][2] = {
{kMainCtrlAddress, kPsActiveBit | kAlsActiveBit},
{kPsLedAddress, kPsLedFreq60Khz | kPsLedCurrent100Ma},
{kPsPulsesAddress, 16},
{kPsMeasRateAddress, kPsMeasRate11Bit | kPsMeasRate50Ms},
{kAlsMeasRateAddress, kAlsMeasRate18Bit | kAlsMeasRate100Ms},
{kAlsGainAddress, kAlsGain1},
};
constexpr uint8_t kPsDataAddress = 0x08;
constexpr uint8_t kAlsDataAddress = 0x0d;
enum PacketKeys {
kPacketKeyPoll,
kPacketKeyStop,
kPacketKeyConfigure,
};
} // namespace
namespace light {
zx_status_t Ltr578Als::GetInputReport(ltr_578als_input_rpt_t* report) {
report->rpt_id = LTR_578ALS_RPT_ID_INPUT;
uint32_t light_data = 0;
uint16_t proximity_data = 0;
zx_status_t status;
{
fbl::AutoLock lock(&i2c_lock_);
status = i2c_.ReadSync(kAlsDataAddress, reinterpret_cast<uint8_t*>(&light_data), 3);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to read ambient light registers", __FILE__);
return status;
}
status = i2c_.ReadSync(kPsDataAddress, reinterpret_cast<uint8_t*>(&proximity_data), 2);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to read proximity registers", __FILE__);
return status;
}
}
report->ambient_light = le32toh(light_data);
report->proximity = le16toh(proximity_data);
return ZX_OK;
}
zx_status_t Ltr578Als::Create(void* ctx, zx_device_t* parent) {
i2c_protocol_t i2c;
auto status = device_get_protocol(parent, ZX_PROTOCOL_I2C, &i2c);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to get ZX_PROTOCOL_I2C", __FILE__);
return status;
}
zx::port port;
status = zx::port::create(0, &port);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to create port", __FILE__);
return status;
}
fbl::AllocChecker ac;
std::unique_ptr<Ltr578Als> device(new (&ac) Ltr578Als(parent, &i2c, std::move(port)));
if (!ac.check()) {
zxlogf(ERROR, "%s: Ltr578Als alloc failed", __FILE__);
return ZX_ERR_NO_MEMORY;
}
if ((status = device->Init()) != ZX_OK) {
return status;
}
if ((status = device->DdkAdd("ltr-578als")) != ZX_OK) {
zxlogf(ERROR, "%s: DdkAdd failed", __FILE__);
return status;
}
__UNUSED auto* dummy = device.release();
return ZX_OK;
}
zx_status_t Ltr578Als::Init() {
{
fbl::AutoLock lock(&i2c_lock_);
for (size_t i = 0; i < countof(kDefaultRegValues); i++) {
zx_status_t status = i2c_.WriteSync(kDefaultRegValues[i], sizeof(kDefaultRegValues[i]));
if (status != ZX_OK) {
zxlogf(ERROR, "%s: Failed to configure sensors", __FILE__);
return status;
}
}
}
return ZX_OK;
}
zx_status_t Ltr578Als::HidbusQuery(uint32_t options, hid_info_t* out_info) {
out_info->dev_num = 0;
out_info->device_class = HID_DEVICE_CLASS_OTHER;
out_info->boot_device = false;
return ZX_OK;
}
zx_status_t Ltr578Als::HidbusGetDescriptor(hid_description_type_t desc_type, void* out_data_buffer,
size_t data_size, size_t* out_data_actual) {
const uint8_t* desc;
size_t desc_size = get_ltr_578als_report_desc(&desc);
if (data_size < desc_size) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
memcpy(out_data_buffer, desc, desc_size);
*out_data_actual = desc_size;
return ZX_OK;
}
zx_status_t Ltr578Als::HidbusGetReport(hid_report_type_t rpt_type, uint8_t rpt_id,
void* out_data_buffer, size_t data_size,
size_t* out_data_actual) {
if (rpt_type == HID_REPORT_TYPE_INPUT && rpt_id == LTR_578ALS_RPT_ID_INPUT) {
if (data_size < sizeof(ltr_578als_input_rpt_t)) {
return ZX_ERR_INVALID_ARGS;
}
zx_status_t status = GetInputReport(reinterpret_cast<ltr_578als_input_rpt_t*>(out_data_buffer));
if (status != ZX_OK) {
return status;
}
*out_data_actual = sizeof(ltr_578als_input_rpt_t);
} else if (rpt_type == HID_REPORT_TYPE_FEATURE && rpt_id == LTR_578ALS_RPT_ID_FEATURE) {
if (data_size < sizeof(ltr_578als_feature_rpt_t)) {
return ZX_ERR_INVALID_ARGS;
}
ltr_578als_feature_rpt_t* report = reinterpret_cast<ltr_578als_feature_rpt_t*>(out_data_buffer);
report->rpt_id = LTR_578ALS_RPT_ID_FEATURE;
report->interval_ms = simple_hid_.GetReportInterval();
*out_data_actual = sizeof(ltr_578als_feature_rpt_t);
} else {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t Ltr578Als::HidbusSetReport(hid_report_type_t rpt_type, uint8_t rpt_id,
const void* data_buffer, size_t data_size) {
if (rpt_type != HID_REPORT_TYPE_FEATURE || rpt_id != LTR_578ALS_RPT_ID_FEATURE) {
return ZX_ERR_NOT_SUPPORTED;
}
if (data_size < sizeof(ltr_578als_feature_rpt_t)) {
return ZX_ERR_INVALID_ARGS;
}
const ltr_578als_feature_rpt_t* report =
reinterpret_cast<const ltr_578als_feature_rpt_t*>(data_buffer);
return simple_hid_.SetReportInterval(report->interval_ms);
}
zx_status_t Ltr578Als::HidbusGetIdle(uint8_t rpt_id, uint8_t* out_duration) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Ltr578Als::HidbusSetIdle(uint8_t rpt_id, uint8_t duration) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Ltr578Als::HidbusGetProtocol(hid_protocol_t* out_protocol) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Ltr578Als::HidbusSetProtocol(hid_protocol_t protocol) { return ZX_ERR_NOT_SUPPORTED; }
static constexpr zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = Ltr578Als::Create;
return ops;
}();
} // namespace light
ZIRCON_DRIVER(ltr_578als, light::driver_ops, "zircon", "0.1");