1 
2 // Copyright 2018 The Fuchsia Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #pragma once
7 
8 #include <string.h>
9 
10 #include <ddk/protocol/i2c.h>
11 #include <lib/sync/completion.h>
12 #include <zircon/assert.h>
13 #include <zircon/compiler.h>
14 #include <zircon/types.h>
15 
16 __BEGIN_CDECLS;
17 
18 typedef struct {
19     size_t length;
20     bool is_read;
21     bool stop;
22 } __PACKED i2c_rpc_op_t;
23 
24 // Writes and reads data on an i2c channel. If both write_length and read_length
25 // are greater than zero, this call will perform a write operation immediately followed
26 // by a read operation with no other traffic occurring on the bus in between.
27 // If read_length is zero, then i2c_write_read will only perform a write operation,
28 // and if write_length is zero, then it will only perform a read operation.
29 // The results of the operation are returned asynchronously via the transact_cb.
30 // The cookie parameter can be used to pass your own private data to the transact_cb callback.
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)31 static inline void i2c_write_read(const i2c_protocol_t* i2c, const void* write_buf,
32                                   size_t write_length, size_t read_length,
33                                   i2c_transact_callback transact_cb, void* cookie) {
34     i2c_op_t ops[2];
35     size_t count = 0;
36     if (write_length) {
37         ops[count].data_buffer = (void*)write_buf;
38         ops[count].data_size = (uint32_t)write_length;
39         ops[count].is_read = false;
40         ops[count].stop = !read_length;
41         count++;
42     }
43     if (read_length) {
44         ops[count].data_buffer = NULL;
45         ops[count].data_size = (uint32_t)read_length;
46         ops[count].is_read = true;
47         ops[count].stop = true;
48         count++;
49     }
50     i2c_transact(i2c, ops, count, transact_cb, cookie);
51 }
52 
53 typedef struct {
54     sync_completion_t completion;
55     void* read_buf;
56     size_t read_length;
57     zx_status_t result;
58 } i2c_write_read_ctx_t;
59 
i2c_write_read_sync_cb(void * cookie,zx_status_t status,const i2c_op_t * ops,size_t cnt)60 static inline void i2c_write_read_sync_cb(void* cookie, zx_status_t status, const i2c_op_t* ops,
61                                           size_t cnt) {
62     i2c_write_read_ctx_t* ctx = (i2c_write_read_ctx_t*)cookie;
63     ctx->result = status;
64     if (status == ZX_OK && ctx->read_buf && ctx->read_length) {
65         ZX_DEBUG_ASSERT(cnt == 1);
66         memcpy(ctx->read_buf, ops[0].data_buffer, ctx->read_length);
67     }
68 
69     sync_completion_signal(&ctx->completion);
70 }
71 
i2c_write_read_sync(const i2c_protocol_t * i2c,const void * write_buf,size_t write_length,void * read_buf,size_t read_length)72 static inline zx_status_t i2c_write_read_sync(const i2c_protocol_t* i2c, const void* write_buf,
73                                               size_t write_length, void* read_buf,
74                                               size_t read_length) {
75     i2c_write_read_ctx_t ctx;
76     sync_completion_reset(&ctx.completion);
77     ctx.read_buf = read_buf;
78     ctx.read_length = read_length;
79 
80     i2c_write_read(i2c, write_buf, write_length, read_length, i2c_write_read_sync_cb, &ctx);
81     zx_status_t status = sync_completion_wait(&ctx.completion, ZX_TIME_INFINITE);
82     if (status == ZX_OK) {
83         return ctx.result;
84     } else {
85         return status;
86     }
87 }
88 
i2c_write_sync(const i2c_protocol_t * i2c,const void * write_buf,size_t write_length)89 static inline zx_status_t i2c_write_sync(const i2c_protocol_t* i2c, const void* write_buf,
90                                          size_t write_length) {
91     return i2c_write_read_sync(i2c, write_buf, write_length, NULL, 0);
92 }
93 
i2c_read_sync(const i2c_protocol_t * i2c,void * read_buf,size_t read_length)94 static inline zx_status_t i2c_read_sync(const i2c_protocol_t* i2c, void* read_buf,
95                                         size_t read_length) {
96     return i2c_write_read_sync(i2c, NULL, 0, read_buf, read_length);
97 }
98 
99 __END_CDECLS;
100