1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "platform-i2c.h" 6 7#include <ddk/debug.h> 8#include <fbl/array.h> 9#include <fbl/auto_lock.h> 10#include <fbl/unique_ptr.h> 11#include <stdlib.h> 12#include <threads.h> 13#include <zircon/assert.h> 14#include <zircon/listnode.h> 15#include <zircon/threads.h> 16 17namespace platform_bus { 18 19PlatformI2cBus::PlatformI2cBus(i2c_impl_protocol_t* i2c, uint32_t bus_id) 20 : i2c_(i2c), bus_id_(bus_id) { 21 22 list_initialize(&queued_txns_); 23 list_initialize(&free_txns_); 24 sync_completion_reset(&txn_signal_); 25} 26 27zx_status_t PlatformI2cBus::Start() { 28 auto status = i2c_.GetMaxTransferSize(bus_id_, &max_transfer_); 29 if (status != ZX_OK) { 30 return status; 31 } 32 if (max_transfer_ > I2C_MAX_TRANSFER_SIZE) { 33 max_transfer_ = I2C_MAX_TRANSFER_SIZE; 34 } 35 36 char name[32]; 37 snprintf(name, sizeof(name), "PlatformI2cBus[%u]", bus_id_); 38 auto thunk = [](void* arg) -> int { return static_cast<PlatformI2cBus*>(arg)->I2cThread(); }; 39 thrd_create_with_name(&thread_, thunk, this, name); 40 41 return ZX_OK; 42} 43 44void PlatformI2cBus::Complete(I2cTxn* txn, zx_status_t status, const uint8_t* resp_buffer, 45 size_t resp_length) { 46 rpc_i2c_rsp_t* i2c = (rpc_i2c_rsp_t*)(resp_buffer); 47 i2c->header.txid = txn->txid; 48 i2c->header.status = status; 49 i2c->max_transfer = 0; 50 i2c->transact_cb = txn->transact_cb; 51 i2c->cookie = txn->cookie; 52 status = zx_channel_write(txn->channel_handle, 0, resp_buffer, 53 static_cast<uint32_t>(resp_length), nullptr, 0); 54 if (status != ZX_OK) { 55 zxlogf(ERROR, "platform_i2c_read_complete: zx_channel_write failed %d\n", status); 56 } 57} 58 59int PlatformI2cBus::I2cThread() { 60 fbl::AllocChecker ac; 61 fbl::Array<uint8_t> read_buffer(new (&ac) uint8_t[PROXY_MAX_TRANSFER_SIZE], 62 PROXY_MAX_TRANSFER_SIZE); 63 if (!ac.check()) { 64 zxlogf(ERROR, "%s could not allocate read_buffer\n", __FUNCTION__); 65 return 0; 66 } 67 68 while (1) { 69 sync_completion_wait(&txn_signal_, ZX_TIME_INFINITE); 70 sync_completion_reset(&txn_signal_); 71 72 I2cTxn* txn; 73 74 mutex_.Acquire(); 75 while ((txn = list_remove_head_type(&queued_txns_, I2cTxn, node)) != nullptr) { 76 mutex_.Release(); 77 auto rpc_ops = reinterpret_cast<i2c_rpc_op_t*>(txn + 1); 78 auto p_writes = reinterpret_cast<uint8_t*>(rpc_ops) + 79 txn->cnt * sizeof(i2c_rpc_op_t); 80 uint8_t* p_reads = read_buffer.get() + sizeof(rpc_i2c_rsp_t); 81 82 ZX_ASSERT(txn->cnt < I2C_MAX_RW_OPS); 83 i2c_impl_op_t ops[I2C_MAX_RW_OPS]; 84 for (size_t i = 0; i < txn->cnt; ++i) { 85 // Same address for all ops, since there is one address per channel. 86 ops[i].address = txn->address; 87 ops[i].length = rpc_ops[i].length; 88 ops[i].is_read = rpc_ops[i].is_read; 89 ops[i].stop = rpc_ops[i].stop; 90 if (ops[i].is_read) { 91 ops[i].buf = p_reads; 92 p_reads += ops[i].length; 93 } else { 94 ops[i].buf = p_writes; 95 p_writes += ops[i].length; 96 } 97 } 98 auto status = i2c_.Transact(bus_id_, ops, txn->cnt); 99 size_t actual = status == ZX_OK ? p_reads - read_buffer.get() : sizeof(rpc_i2c_rsp_t); 100 Complete(txn, status, read_buffer.get(), actual); 101 102 mutex_.Acquire(); 103 list_add_tail(&free_txns_, &txn->node); 104 } 105 mutex_.Release(); 106 } 107 return 0; 108} 109 110zx_status_t PlatformI2cBus::Transact(uint32_t txid, rpc_i2c_req_t* req, uint16_t address, 111 zx_handle_t channel_handle) { 112 i2c_rpc_op_t* ops = reinterpret_cast<i2c_rpc_op_t*>(req + 1); 113 114 size_t writes_length = 0; 115 for (size_t i = 0; i < req->cnt; ++i) { 116 if (ops[i].length == 0 || ops[i].length > max_transfer_) { 117 return ZX_ERR_INVALID_ARGS; 118 } 119 if (!ops[i].is_read) { 120 writes_length += ops[i].length; 121 } 122 } 123 // Add space for requests and writes data. 124 size_t req_length = sizeof(I2cTxn) + req->cnt * sizeof(i2c_rpc_op_t) + writes_length; 125 if (req_length >= PROXY_MAX_TRANSFER_SIZE) { 126 return ZX_ERR_INVALID_ARGS; 127 } 128 129 fbl::AutoLock lock(&mutex_); 130 131 I2cTxn* txn = list_remove_head_type(&free_txns_, I2cTxn, node); 132 if (txn && txn->length < req_length) { 133 free(txn); 134 txn = nullptr; 135 } 136 137 if (!txn) { 138 // add space for write buffer 139 txn = static_cast<I2cTxn*>(calloc(1, req_length)); 140 txn->length = req_length; 141 } 142 if (!txn) { 143 return ZX_ERR_NO_MEMORY; 144 } 145 146 txn->address = address; 147 txn->txid = txid; 148 txn->transact_cb = req->transact_cb; 149 txn->cookie = req->cookie; 150 txn->channel_handle = channel_handle; 151 txn->cnt = req->cnt; 152 153 auto rpc_ops = reinterpret_cast<i2c_rpc_op_t*>(req + 1); 154 if (req->cnt && !(rpc_ops[req->cnt - 1].stop)) { 155 list_add_tail(&free_txns_, &txn->node); 156 return ZX_ERR_INVALID_ARGS; // no stop in last op in transaction 157 } 158 159 memcpy(txn + 1, req + 1, req->cnt * sizeof(i2c_rpc_op_t) + writes_length); 160 161 list_add_tail(&queued_txns_, &txn->node); 162 sync_completion_signal(&txn_signal_); 163 164 return ZX_OK; 165} 166 167} // namespace platform_bus 168