blob: f5bf571bd2a134d0a3df395f5e028ce4495ac866 [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 "i2c-bus.h"
#include <stdlib.h>
#include <threads.h>
#include <ddk/debug.h>
#include <fbl/auto_lock.h>
#include <fbl/unique_ptr.h>
#include <zircon/listnode.h>
#include <zircon/threads.h>
namespace i2c {
I2cBus::I2cBus(ddk::I2cImplProtocolProxy i2c_impl, uint32_t bus_id)
: i2c_impl_(i2c_impl), bus_id_(bus_id) {
list_initialize(&queued_txns_);
list_initialize(&free_txns_);
sync_completion_reset(&txn_signal_);
}
zx_status_t I2cBus::Start() {
auto status = i2c_impl_.GetMaxTransferSize(bus_id_, &max_transfer_size_);
if (status != ZX_OK) {
return status;
}
char name[32];
snprintf(name, sizeof(name), "I2cBus[%u]", bus_id_);
auto thunk = [](void* arg) -> int { return static_cast<I2cBus*>(arg)->I2cThread(); };
thrd_create_with_name(&thread_, thunk, this, name);
return ZX_OK;
}
void I2cBus::Complete(I2cTxn* txn, zx_status_t status, const uint8_t* data,
size_t data_length) {
/*
struct {
rpc_i2c_rsp_t i2c;
uint8_t data[I2C_MAX_TRANSFER_SIZE] = {};
} resp = {
.i2c = {
.header = {
.txid = txn->txid,
.status = status,
},
.max_transfer = 0,
.complete_cb = txn->complete_cb,
.cookie = txn->cookie,
},
};
if (status == ZX_OK) {
memcpy(resp.data, data, data_length);
}
auto length = static_cast<uint32_t>(sizeof(resp.i2c) + data_length);
status = zx_channel_write(txn->channel_handle, 0, &resp, length, nullptr, 0);
if (status != ZX_OK) {
zxlogf(ERROR, "platform_i2c_read_complete: zx_channel_write failed %d\n", status);
}
*/
}
int I2cBus::I2cThread() {
fbl::AllocChecker ac;
fbl::unique_ptr<uint8_t> read_buffer(new (&ac) uint8_t[max_transfer_size_]);
if (!ac.check()) {
zxlogf(ERROR, "%s could not allocate read_buffer\n", __FUNCTION__);
return 0;
}
while (1) {
sync_completion_wait(&txn_signal_, ZX_TIME_INFINITE);
sync_completion_reset(&txn_signal_);
I2cTxn* txn;
mutex_.Acquire();
while ((txn = list_remove_head_type(&queued_txns_, I2cTxn, node)) != nullptr) {
mutex_.Release();
auto status = i2c_impl_.Transact(bus_id_, txn->address, txn->write_buffer,
txn->write_length, read_buffer.get(), txn->read_length);
size_t actual = (status == ZX_OK ? txn->read_length : 0);
Complete(txn, status, read_buffer.get(), actual);
mutex_.Acquire();
list_add_tail(&free_txns_, &txn->node);
}
mutex_.Release();
}
return 0;
}
zx_status_t I2cBus::Transact(const void* write_buf, size_t write_length, size_t read_length,
i2c_complete_cb complete_cb, void* cookie) {
/*
const size_t write_length = req->write_length;
const size_t read_length = req->read_length;
if (write_length > max_transfer_size_ || read_length > max_transfer_size_) {
return ZX_ERR_INVALID_ARGS;
}
fbl::AutoLock lock(&mutex_);
I2cTxn* txn = list_remove_head_type(&free_txns_, I2cTxn, node);
if (!txn) {
// add space for write buffer
txn = static_cast<I2cTxn*>(calloc(1, sizeof(I2cTxn) + max_transfer_size_));
}
if (!txn) {
return ZX_ERR_NO_MEMORY;
}
txn->address = address;
txn->write_length = write_length;
txn->read_length = read_length;
memcpy(txn->write_buffer, write_buf, write_length);
txn->txid = txid;
txn->complete_cb = req->complete_cb;
txn->cookie = req->cookie;
txn->channel_handle = channel_handle;
list_add_tail(&queued_txns_, &txn->node);
sync_completion_signal(&txn_signal_);
return ZX_OK;
*/
return -1;
}
} // namespace i2c