blob: e5492b567f4ff8f4d0f1384e8ec4eae391b1ac7d [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.
#ifndef ZIRCON_SYSTEM_DEV_LIB_DEVICE_PROTOCOL_I2C_INCLUDE_LIB_DEVICE_PROTOCOL_I2C_H_
#define ZIRCON_SYSTEM_DEV_LIB_DEVICE_PROTOCOL_I2C_INCLUDE_LIB_DEVICE_PROTOCOL_I2C_H_
#include <lib/sync/completion.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <ddk/protocol/i2c.h>
__BEGIN_CDECLS
// Writes and reads data on an i2c channel. If both write_length and read_length
// are greater than zero, this call will perform a write operation immediately followed
// by a read operation with no other traffic occurring on the bus in between.
// If read_length is zero, then i2c_write_read will only perform a write operation,
// and if write_length is zero, then it will only perform a read operation.
// The results of the operation are returned asynchronously via the transact_cb.
// The cookie parameter can be used to pass your own private data to the transact_cb callback.
static inline void i2c_write_read(const i2c_protocol_t* i2c, const void* write_buf,
size_t write_length, size_t read_length,
i2c_transact_callback transact_cb, void* cookie) {
i2c_op_t ops[2];
size_t count = 0;
if (write_length) {
ops[count].data_buffer = (void*)write_buf;
ops[count].data_size = (uint32_t)write_length;
ops[count].is_read = false;
ops[count].stop = !read_length;
count++;
}
if (read_length) {
ops[count].data_buffer = NULL;
ops[count].data_size = (uint32_t)read_length;
ops[count].is_read = true;
ops[count].stop = true;
count++;
}
i2c_transact(i2c, ops, count, transact_cb, cookie);
}
typedef struct i2c_write_read_ctx {
sync_completion_t completion;
void* read_buf;
size_t read_length;
zx_status_t result;
#if defined(__cplusplus)
i2c_write_read_ctx() : read_buf(nullptr), read_length(0), result(ZX_ERR_INTERNAL) {}
#endif
} i2c_write_read_ctx_t;
#if !defined(__cplusplus)
#define I2C_WRITE_READ_CTX_INIT \
((i2c_write_read_ctx_t){SYNC_COMPLETION_INIT, NULL, 0, ZX_ERR_INTERNAL})
#endif
static inline void i2c_write_read_sync_cb(void* cookie, zx_status_t status, const i2c_op_t* ops,
size_t cnt) {
i2c_write_read_ctx_t* ctx = (i2c_write_read_ctx_t*)cookie;
ctx->result = status;
if (status == ZX_OK && ctx->read_buf && ctx->read_length) {
ZX_DEBUG_ASSERT(cnt == 1);
memcpy(ctx->read_buf, ops[0].data_buffer, ctx->read_length);
}
sync_completion_signal(&ctx->completion);
}
static inline zx_status_t i2c_write_read_sync(const i2c_protocol_t* i2c, const void* write_buf,
size_t write_length, void* read_buf,
size_t read_length) {
#if !defined(__cplusplus)
i2c_write_read_ctx_t ctx = I2C_WRITE_READ_CTX_INIT;
#else
i2c_write_read_ctx_t ctx;
#endif
sync_completion_reset(&ctx.completion);
ctx.read_buf = read_buf;
ctx.read_length = read_length;
i2c_write_read(i2c, write_buf, write_length, read_length, i2c_write_read_sync_cb, &ctx);
zx_status_t status = sync_completion_wait(&ctx.completion, ZX_TIME_INFINITE);
if (status == ZX_OK) {
return ctx.result;
} else {
return status;
}
}
static inline zx_status_t i2c_write_sync(const i2c_protocol_t* i2c, const void* write_buf,
size_t write_length) {
return i2c_write_read_sync(i2c, write_buf, write_length, NULL, 0);
}
static inline zx_status_t i2c_read_sync(const i2c_protocol_t* i2c, void* read_buf,
size_t read_length) {
return i2c_write_read_sync(i2c, NULL, 0, read_buf, read_length);
}
__END_CDECLS
#endif // ZIRCON_SYSTEM_DEV_LIB_DEVICE_PROTOCOL_I2C_INCLUDE_LIB_DEVICE_PROTOCOL_I2C_H_