i2c: flatten structure - remove 'interface'
[platform/core/api/peripheral-io.git] / src / peripheral_i2c.c
1 /*
2  * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <sys/file.h>
19 #include <sys/ioctl.h>
20 #include <system_info.h>
21
22 #include "peripheral_io.h"
23 #include "peripheral_interface_common.h"
24 #include "peripheral_log.h"
25
26 #define PERIPHERAL_IO_I2C_FEATURE "http://tizen.org/feature/peripheral_io.i2c"
27
28 #define I2C_FEATURE_UNKNOWN -1
29 #define I2C_FEATURE_FALSE    0
30 #define I2C_FEATURE_TRUE     1
31
32 #define I2C_SLAVE       0x0703  /* Use this slave address */
33 #define I2C_SMBUS       0x0720  /* SMBus transfer */
34
35 /* i2c_smbus_xfer read or write markers */
36 #define I2C_SMBUS_READ  1
37 #define I2C_SMBUS_WRITE 0
38
39 /* SMBus transaction types */
40 #define I2C_SMBUS_QUICK             0
41 #define I2C_SMBUS_BYTE              1
42 #define I2C_SMBUS_BYTE_DATA         2
43 #define I2C_SMBUS_WORD_DATA         3
44
45 /*
46  * Data for SMBus Messages
47  */
48 #define I2C_SMBUS_BLOCK_MAX     32      /* As specified in SMBus standard */
49
50 union i2c_smbus_data {
51         uint8_t byte;
52         uint16_t word;
53         uint8_t block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
54                                /* and one more for user-space compatibility */
55 };
56
57 /* This is the structure as used in the I2C_SMBUS ioctl call */
58 struct i2c_smbus_ioctl_data {
59         uint8_t read_write;
60         uint8_t command;
61         uint32_t size;
62         union i2c_smbus_data *data;
63 };
64
65 static int i2c_feature = I2C_FEATURE_UNKNOWN;
66
67 static bool __is_feature_supported(void)
68 {
69         int ret = SYSTEM_INFO_ERROR_NONE;
70         bool feature = false;
71
72         if (i2c_feature == I2C_FEATURE_UNKNOWN) {
73                 ret = system_info_get_platform_bool(PERIPHERAL_IO_I2C_FEATURE, &feature);
74                 RETVM_IF(ret != SYSTEM_INFO_ERROR_NONE, false, "Failed to get system info");
75
76                 i2c_feature = (feature ? I2C_FEATURE_TRUE : I2C_FEATURE_FALSE);
77         }
78
79         return (i2c_feature == I2C_FEATURE_TRUE ? true : false);
80 }
81
82 static inline void cleanup_handlep(peripheral_i2c_h *handle) {
83         if (*handle != NULL) {
84                 if ((*handle)->fd != -1)
85                         close((*handle)->fd);
86                 free(*handle);
87         }
88 }
89
90 int peripheral_i2c_open_flags(int bus, int address, peripheral_open_flags_e flags, peripheral_i2c_h *i2c)
91 {
92         int ret = PERIPHERAL_ERROR_NONE;
93         int lock_type = LOCK_EX;
94
95 #define DEV_PATH_BASE "/dev/i2c-"
96         char path[sizeof(DEV_PATH_BASE "0000000000")] = {0, };  /* space for /dev/i2c-%d */
97
98         RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "I2C feature is not supported");
99         RETVM_IF(i2c == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid i2c handle");
100         RETVM_IF(bus < 0 || address < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid parameter");
101         RETVM_IF(flags != PERIPHERAL_OPEN_FLAGS_PRIVATE && flags != PERIPHERAL_OPEN_FLAGS_SHARED,
102                         PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid flags");
103
104         __attribute__ ((cleanup(cleanup_handlep))) peripheral_i2c_h handle = NULL;
105
106         handle = (peripheral_i2c_h)malloc(sizeof(struct _peripheral_i2c_s));
107         if (handle == NULL) {
108                 _E("Failed to allocate peripheral_i2c_h");
109                 return PERIPHERAL_ERROR_OUT_OF_MEMORY;
110         }
111
112         snprintf(path, sizeof path, DEV_PATH_BASE "%d", bus);
113         handle->fd = open(path, O_RDWR | O_CLOEXEC);
114         CHECK_ERROR(handle->fd < 0);
115
116         ret = ioctl(handle->fd, I2C_SLAVE, address);
117         CHECK_ERROR(ret);
118
119         if (flags == PERIPHERAL_OPEN_FLAGS_SHARED)
120                 lock_type = LOCK_SH;
121
122         if (flock(handle->fd, lock_type | LOCK_NB)) {
123                 if (errno == EWOULDBLOCK) {
124                         _E("bus : %d, address : 0x%x is not available", bus, address);
125                         return PERIPHERAL_ERROR_RESOURCE_BUSY;
126                 } else {
127                         _E("bus : %d, address : 0x%x flock() error: %d", bus, address, errno);
128                         return PERIPHERAL_ERROR_IO_ERROR;
129                 }
130         }
131
132         *i2c = handle;
133         handle = NULL;
134
135         return PERIPHERAL_ERROR_NONE;
136 }
137
138 int peripheral_i2c_open(int bus, int address, peripheral_i2c_h *i2c)
139 {
140         return peripheral_i2c_open_flags(bus, address, PERIPHERAL_OPEN_FLAGS_PRIVATE, i2c);
141 }
142
143 int peripheral_i2c_close(peripheral_i2c_h i2c)
144 {
145         RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "I2C feature is not supported");
146         RETVM_IF(i2c == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "i2c handle is NULL");
147
148         cleanup_handlep(&i2c);
149
150         return PERIPHERAL_ERROR_NONE;
151 }
152
153 int peripheral_i2c_read(peripheral_i2c_h i2c, uint8_t *data_out, uint32_t length)
154 {
155         RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "I2C feature is not supported");
156         RETVM_IF(i2c == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "i2c handle is NULL");
157         RETVM_IF(data_out == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid parameter");
158
159         int ret = read(i2c->fd, data_out, length);
160         if (ret == length)
161                 return PERIPHERAL_ERROR_NONE;
162
163         struct i2c_smbus_ioctl_data data_arg;
164         union i2c_smbus_data data;
165
166         memset(&data, 0x0, sizeof(data.block));
167
168         data_arg.read_write = I2C_SMBUS_READ;
169         data_arg.size = I2C_SMBUS_BYTE;
170         data_arg.data = &data;
171         data_arg.command = *data_out;
172
173         ret = ioctl(i2c->fd, I2C_SMBUS, &data_arg);
174         CHECK_ERROR(ret != 0);
175
176         *data_out = data.byte;
177
178         return PERIPHERAL_ERROR_NONE;
179 }
180
181 int peripheral_i2c_write(peripheral_i2c_h i2c, uint8_t *data_in, uint32_t length)
182 {
183         RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "I2C feature is not supported");
184         RETVM_IF(i2c == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "i2c handle is NULL");
185         RETVM_IF(data_in == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid parameter");
186
187         int ret = write(i2c->fd, data_in, length);
188         if (ret == length)
189                 return PERIPHERAL_ERROR_NONE;
190
191         struct i2c_smbus_ioctl_data data_arg;
192         union i2c_smbus_data data;
193
194         memset(&data, 0x0, sizeof(data.block));
195
196         data_arg.read_write = I2C_SMBUS_WRITE;
197         data_arg.size = I2C_SMBUS_BYTE;
198         data_arg.data = &data;
199         data_arg.command = *data_in;
200
201         ret = ioctl(i2c->fd, I2C_SMBUS, &data_arg);
202         CHECK_ERROR(ret != 0);
203
204         return PERIPHERAL_ERROR_NONE;
205 }
206
207 int peripheral_i2c_read_register_byte(peripheral_i2c_h i2c, uint8_t reg, uint8_t *data_out)
208 {
209         RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "I2C feature is not supported");
210         RETVM_IF(i2c == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "i2c handle is NULL");
211         RETVM_IF(data_out == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid parameter");
212
213         int ret;
214
215         struct i2c_smbus_ioctl_data data_arg;
216         union i2c_smbus_data data;
217
218         memset(&data, 0x0, sizeof(data.block));
219
220         data_arg.read_write = I2C_SMBUS_READ;
221         data_arg.size = I2C_SMBUS_BYTE_DATA;
222         data_arg.data = &data;
223         data_arg.command = reg;
224
225         ret = ioctl(i2c->fd, I2C_SMBUS, &data_arg);
226         CHECK_ERROR(ret != 0);
227
228         *data_out = data.byte;
229
230         return PERIPHERAL_ERROR_NONE;
231 }
232
233 int peripheral_i2c_write_register_byte(peripheral_i2c_h i2c, uint8_t reg, uint8_t data_in)
234 {
235         RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "I2C feature is not supported");
236         RETVM_IF(i2c == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "i2c handle is NULL");
237
238         int ret;
239
240         struct i2c_smbus_ioctl_data data_arg;
241         union i2c_smbus_data data;
242
243         memset(&data, 0x0, sizeof(data.block));
244
245         data_arg.read_write = I2C_SMBUS_WRITE;
246         data_arg.size = I2C_SMBUS_BYTE_DATA;
247         data_arg.data = &data;
248         data_arg.command = reg;
249
250         data.byte = data_in;
251
252         ret = ioctl(i2c->fd, I2C_SMBUS, &data_arg);
253         CHECK_ERROR(ret != 0);
254
255         return PERIPHERAL_ERROR_NONE;
256 }
257
258 int peripheral_i2c_read_register_word(peripheral_i2c_h i2c, uint8_t reg, uint16_t *data_out)
259 {
260         RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "I2C feature is not supported");
261         RETVM_IF(i2c == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "i2c handle is NULL");
262         RETVM_IF(data_out == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid parameter");
263
264         int ret;
265
266         struct i2c_smbus_ioctl_data data_arg;
267         union i2c_smbus_data data;
268
269         memset(&data, 0x0, sizeof(data.block));
270
271         data_arg.read_write = I2C_SMBUS_READ;
272         data_arg.size = I2C_SMBUS_WORD_DATA;
273         data_arg.data = &data;
274         data_arg.command = reg;
275
276         ret = ioctl(i2c->fd, I2C_SMBUS, &data_arg);
277         CHECK_ERROR(ret != 0);
278
279         *data_out = data.word;
280
281         return PERIPHERAL_ERROR_NONE;
282 }
283
284 int peripheral_i2c_write_register_word(peripheral_i2c_h i2c, uint8_t reg, uint16_t data_in)
285 {
286         RETVM_IF(__is_feature_supported() == false, PERIPHERAL_ERROR_NOT_SUPPORTED, "I2C feature is not supported");
287         RETVM_IF(i2c == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "i2c handle is NULL");
288
289         int ret;
290
291         struct i2c_smbus_ioctl_data data_arg;
292         union i2c_smbus_data data;
293
294         memset(&data, 0x0, sizeof(data.block));
295
296         data_arg.read_write = I2C_SMBUS_WRITE;
297         data_arg.size = I2C_SMBUS_WORD_DATA;
298         data_arg.data = &data;
299         data_arg.command = reg;
300
301         data.word = data_in;
302
303         ret = ioctl(i2c->fd, I2C_SMBUS, &data_arg);
304         CHECK_ERROR(ret != 0);
305
306         return PERIPHERAL_ERROR_NONE;
307 }