blob: c93397d18c5a5cff3450fafc4431de74b4e38829 [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 "i2c-child.h"
#include <lib/sync/completion.h>
#include <threads.h>
#include <zircon/types.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/metadata.h>
#include <ddk/metadata/i2c.h>
#include <fbl/alloc_checker.h>
#include <fbl/mutex.h>
namespace i2c {
void I2cChild::Transfer(fidl::VectorView<bool> segments_is_write,
fidl::VectorView<fidl::VectorView<uint8_t>> write_segments_data,
fidl::VectorView<uint8_t> read_segments_length,
TransferCompleter::Sync& completer) {
if (segments_is_write.count() < 1) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
auto op_list = std::make_unique<i2c_op_t[]>(segments_is_write.count());
size_t write_cnt = 0;
size_t read_cnt = 0;
for (size_t i = 0; i < segments_is_write.count(); ++i) {
if (segments_is_write[i]) {
if (write_cnt >= write_segments_data.count()) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
op_list[i].data_buffer = write_segments_data[write_cnt].data();
op_list[i].data_size = write_segments_data[write_cnt].count();
op_list[i].is_read = false;
op_list[i].stop = false;
write_cnt++;
} else {
if (read_cnt >= read_segments_length.count()) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
op_list[i].data_buffer = nullptr; // unused.
op_list[i].data_size = read_segments_length[read_cnt];
op_list[i].is_read = true;
op_list[i].stop = false;
read_cnt++;
}
}
op_list[segments_is_write.count() - 1].stop = true;
if (write_segments_data.count() != write_cnt) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
if (read_segments_length.count() != read_cnt) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
struct Ctx {
sync_completion_t done = {};
TransferCompleter::Sync* completer;
} ctx;
ctx.completer = &completer;
auto callback = [](void* ctx, zx_status_t status, const i2c_op_t* op_list, size_t op_count) {
auto ctx2 = static_cast<Ctx*>(ctx);
if (status == ZX_OK) {
auto reads = std::make_unique<fidl::VectorView<uint8_t>[]>(op_count);
for (size_t i = 0; i < op_count; ++i) {
reads[i].set_data(
fidl::unowned_ptr(static_cast<uint8_t*>(const_cast<void*>(op_list[i].data_buffer))));
reads[i].set_count(op_list[i].data_size);
}
fidl::VectorView<fidl::VectorView<uint8_t>> all_reads(fidl::unowned_ptr(reads.get()),
op_count);
ctx2->completer->ReplySuccess(std::move(all_reads));
} else {
ctx2->completer->ReplyError(status);
}
sync_completion_signal(&ctx2->done);
};
bus_->Transact(address_, op_list.get(), segments_is_write.count(), callback, &ctx);
sync_completion_wait(&ctx.done, zx::duration::infinite().get());
}
void I2cChild::I2cTransact(const i2c_op_t* op_list, size_t op_count, i2c_transact_callback callback,
void* cookie) {
bus_->Transact(address_, op_list, op_count, callback, cookie);
}
zx_status_t I2cChild::I2cGetMaxTransferSize(size_t* out_size) {
*out_size = bus_->max_transfer();
return ZX_OK;
}
zx_status_t I2cChild::I2cGetInterrupt(uint32_t flags, zx::interrupt* out_irq) {
// This is only used by the Intel I2C driver.
// TODO: Pass these interrupt numbers from intel-i2c.
if (address_ == 0xa) {
// Please do not use get_root_resource() in new code. See fxbug.dev/31358.
zx_status_t status = zx::interrupt::create(zx::resource(get_root_resource()), 0x1f,
ZX_INTERRUPT_MODE_LEVEL_LOW, out_irq);
if (status != ZX_OK) {
return status;
}
return ZX_OK;
} else if (address_ == 0x49) {
// Please do not use get_root_resource() in new code. See fxbug.dev/31358.
zx_status_t status = zx::interrupt::create(zx::resource(get_root_resource()), 0x33,
ZX_INTERRUPT_MODE_LEVEL_LOW, out_irq);
if (status != ZX_OK) {
return status;
}
return ZX_OK;
} else if (address_ == 0x10) {
// Acer12
// Please do not use get_root_resource() in new code. See fxbug.dev/31358.
zx_status_t status = zx::interrupt::create(zx::resource(get_root_resource()), 0x1f,
ZX_INTERRUPT_MODE_LEVEL_LOW, out_irq);
if (status != ZX_OK) {
return status;
}
return ZX_OK;
} else if (address_ == 0x50) {
// Please do not use get_root_resource() in new code. See fxbug.dev/31358.
zx_status_t status = zx::interrupt::create(zx::resource(get_root_resource()), 0x18,
ZX_INTERRUPT_MODE_EDGE_LOW, out_irq);
if (status != ZX_OK) {
return status;
}
return ZX_OK;
} else if (address_ == 0x15) {
// Please do not use get_root_resource() in new code. See fxbug.dev/31358.
zx_status_t status = zx::interrupt::create(zx::resource(get_root_resource()), 0x2b,
ZX_INTERRUPT_MODE_EDGE_LOW, out_irq);
if (status != ZX_OK) {
return status;
}
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
void I2cChild::DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); }
void I2cChild::DdkRelease() { delete this; }
} // namespace i2c