forked from teledyne-e2v/ModuleControl
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathi2c.cpp
More file actions
374 lines (299 loc) · 9.91 KB
/
i2c.cpp
File metadata and controls
374 lines (299 loc) · 9.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "i2c.hpp"
/* I2C default delay */
#define I2C_DEFAULT_DELAY 1
/* I2C internal address max length */
#define INT_ADDR_MAX_BYTES 4
/* I2C page max bytes */
#define PAGE_MAX_BYTES 4096
#define GET_I2C_DELAY(delay) ((delay) == 0 ? I2C_DEFAULT_DELAY : (delay))
#define GET_I2C_FLAGS(tenbit, flags) ((tenbit) ? ((flags) | I2C_M_TEN) : (flags))
#define GET_WRITE_SIZE(addr, remain, page_bytes) ((addr) + (remain) > (page_bytes) ? (page_bytes) - (addr) : remain)
static void i2c_delay(unsigned char delay);
/*
** @brief : Open i2c bus
** #bus_name : i2c bus name such as: /dev/i2c-1
** @return : failed return -1, success return i2c bus fd
*/
int i2c_open(const char *bus_name)
{
int fd;
/* Open i2c-bus devcice */
if ((fd = open(bus_name, O_RDWR)) == -1)
{
return -1;
}
return fd;
}
void i2c_close(int bus)
{
close(bus);
}
/*
** @brief : Initialize I2CDevice with defualt value
** #device : I2CDevice struct
*/
void i2c_init_device(I2CDevice *device)
{
/* 7 bit device address */
device->tenbit = 0;
/* 1ms delay */
device->delay = 1;
/* 8 bytes per page */
device->page_bytes = 8;
/* 1 byte internal(word) address */
device->iaddr_bytes = 1;
}
/*
** @brief : Get I2CDevice struct desc
** #device : I2CDevice struct
** #buf : Description message buffer
** #size : #buf size
** @return : return i2c device desc
*/
char *i2c_get_device_desc(const I2CDevice *device, char *buf, size_t size)
{
memset(buf, 0, size);
snprintf(buf, size, "Device address: 0x%x, tenbit: %s, internal(word) address: %d bytes, page max %d bytes, delay: %dms",
device->addr, device->tenbit ? "True" : "False", device->iaddr_bytes, device->page_bytes, device->delay);
return buf;
}
/*
** i2c_ioctl_read/write
** I2C bus top layer interface to operation different i2c devide
** This function will call XXX:ioctl system call and will be related
** i2c-dev.c i2cdev_ioctl to operation i2c device.
** 1. it can choice ignore or not ignore i2c bus ack signal (flags set I2C_M_IGNORE_NAK)
** 2. it can choice ignore or not ignore i2c internal address
**
*/
ssize_t i2c_ioctl_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len)
{
struct i2c_msg ioctl_msg[2];
struct i2c_rdwr_ioctl_data ioctl_data;
unsigned char addr[INT_ADDR_MAX_BYTES];
unsigned short flags = GET_I2C_FLAGS(device->tenbit, device->flags);
memset(addr, 0, sizeof(addr));
memset(ioctl_msg, 0, sizeof(ioctl_msg));
memset(&ioctl_data, 0, sizeof(ioctl_data));
/* Target have internal address */
if (device->iaddr_bytes)
{
i2c_iaddr_convert(iaddr, device->iaddr_bytes, addr);
/* First message is write internal address */
ioctl_msg[0].len = device->iaddr_bytes;
ioctl_msg[0].addr = device->addr;
ioctl_msg[0].buf = addr;
ioctl_msg[0].flags = flags;
/* Second message is read data */
ioctl_msg[1].len = len;
ioctl_msg[1].addr = device->addr;
ioctl_msg[1].buf = static_cast<unsigned char *>(buf);
ioctl_msg[1].flags = flags | I2C_M_RD;
/* Package to i2c message to operation i2c device */
ioctl_data.nmsgs = 2;
ioctl_data.msgs = ioctl_msg;
}
/* Target did not have internal address */
else
{
/* Direct send read data message */
ioctl_msg[0].len = len;
ioctl_msg[0].addr = device->addr;
ioctl_msg[0].buf = static_cast<unsigned char *>(buf);
ioctl_msg[0].flags = flags | I2C_M_RD;
/* Package to i2c message to operation i2c device */
ioctl_data.nmsgs = 1;
ioctl_data.msgs = ioctl_msg;
}
/* Using ioctl interface operation i2c device */
if (ioctl(device->bus, I2C_RDWR, (unsigned long)&ioctl_data) == -1)
{
perror("Ioctl read i2c error:");
return -1;
}
return len;
}
ssize_t i2c_ioctl_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len)
{
ssize_t remain = len;
size_t size = 0, cnt = 0;
const unsigned char *buffer = (unsigned char *)buf;
unsigned char delay = GET_I2C_DELAY(device->delay);
unsigned short flags = GET_I2C_FLAGS(device->tenbit, device->flags);
struct i2c_msg ioctl_msg;
struct i2c_rdwr_ioctl_data ioctl_data;
unsigned char tmp_buf[PAGE_MAX_BYTES + INT_ADDR_MAX_BYTES];
while (remain > 0)
{
size = GET_WRITE_SIZE(iaddr % device->page_bytes, remain, device->page_bytes);
/* Convert i2c internal address */
memset(tmp_buf, 0, sizeof(tmp_buf));
i2c_iaddr_convert(iaddr, device->iaddr_bytes, tmp_buf);
/* Connect write data after device internal address */
memcpy(tmp_buf + device->iaddr_bytes, buffer, size);
/* Fill kernel ioctl i2c_msg */
memset(&ioctl_msg, 0, sizeof(ioctl_msg));
memset(&ioctl_data, 0, sizeof(ioctl_data));
ioctl_msg.len = device->iaddr_bytes + size;
ioctl_msg.addr = device->addr;
ioctl_msg.buf = tmp_buf;
ioctl_msg.flags = flags;
ioctl_data.nmsgs = 1;
ioctl_data.msgs = &ioctl_msg;
if (ioctl(device->bus, I2C_RDWR, (unsigned long)&ioctl_data) == -1)
{
perror("Ioctl write i2c error:");
return -1;
}
/* XXX: Must have a little time delay */
i2c_delay(delay);
cnt += size;
iaddr += size;
buffer += size;
remain -= size;
}
return cnt;
}
/*
** @brief : read #len bytes data from #device #iaddr to #buf
** #device : I2CDevice struct, must call i2c_device_init first
** #iaddr : i2c_device internal address will read data from this address, no address set zero
** #buf : i2c data will read to here
** #len : how many data to read, lenght must less than or equal to buf size
** @return : success return read data length, failed -1
*/
ssize_t i2c_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len)
{
ssize_t cnt;
unsigned char addr[INT_ADDR_MAX_BYTES];
unsigned char delay = GET_I2C_DELAY(device->delay);
/* Set i2c slave address */
if (i2c_select(device->bus, device->addr, device->tenbit) == -1)
{
return -1;
}
/* Convert i2c internal address */
memset(addr, 0, sizeof(addr));
i2c_iaddr_convert(iaddr, device->iaddr_bytes, addr);
/* Write internal address to devide */
if (write(device->bus, addr, device->iaddr_bytes) != device->iaddr_bytes)
{
perror("Write i2c internal address error");
return -1;
}
/* Wait a while */
i2c_delay(delay);
/* Read count bytes data from int_addr specify address */
if ((cnt = read(device->bus, buf, len)) == -1)
{
perror("Read i2c data error");
return -1;
}
return cnt;
}
/*
** @brief : write #buf data to i2c #device #iaddr address
** #device : I2CDevice struct, must call i2c_device_init first
** #iaddr : i2c_device internal address, no address set zero
** #buf : data will write to i2c device
** #len : buf data length without '/0'
** @return : success return write data length, failed -1
*/
ssize_t i2c_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len)
{
ssize_t remain = len;
size_t cnt = 0, size = 0;
const unsigned char *buffer = (unsigned char *)buf;
unsigned char delay = GET_I2C_DELAY(device->delay);
unsigned char tmp_buf[PAGE_MAX_BYTES + INT_ADDR_MAX_BYTES];
/* Set i2c slave address */
if (i2c_select(device->bus, device->addr, device->tenbit) == -1)
{
return -1;
}
/* Once only can write less than 4 byte */
while (remain > 0)
{
size = GET_WRITE_SIZE(iaddr % device->page_bytes, remain, device->page_bytes);
/* Convert i2c internal address */
memset(tmp_buf, 0, sizeof(tmp_buf));
i2c_iaddr_convert(iaddr, device->iaddr_bytes, tmp_buf);
/* Copy data to tmp_buf */
memcpy(tmp_buf + device->iaddr_bytes, buffer, size);
/* Write to buf content to i2c device length is address length and write buffer length */
if (static_cast<unsigned int>(write(device->bus, tmp_buf, device->iaddr_bytes + size)) != device->iaddr_bytes + size)
{
perror("I2C write error:");
return -1;
}
/* XXX: Must have a little time delay */
i2c_delay(delay);
/* Move to next #size bytes */
cnt += size;
iaddr += size;
buffer += size;
remain -= size;
}
return cnt;
}
/*
** @brief : i2c internal address convert
** #iaddr : i2c device internal address
** #len : i2c device internal address length
** #addr : save convert address
*/
void i2c_iaddr_convert(unsigned int iaddr, unsigned int len, unsigned char *addr)
{
union
{
unsigned int iaddr;
unsigned char caddr[INT_ADDR_MAX_BYTES];
} convert;
/* I2C internal address order is big-endian, same with network order */
convert.iaddr = htonl(iaddr);
/* Copy address to addr buffer */
int i = len - 1;
int j = INT_ADDR_MAX_BYTES - 1;
while (i >= 0 && j >= 0)
{
addr[i--] = convert.caddr[j--];
}
}
/*
** @brief : Select i2c address @i2c bus
** #bus : i2c bus fd
** #dev_addr : i2c device address
** #tenbit : i2c device address is tenbit
** #return : success return 0, failed return -1
*/
int i2c_select(int bus, unsigned long dev_addr, unsigned long tenbit)
{
/* Set i2c device address bit */
if (ioctl(bus, I2C_TENBIT, tenbit))
{
perror("Set I2C_TENBIT failed");
return -1;
}
/* Set i2c device as slave ans set it address */
if (ioctl(bus, I2C_SLAVE, dev_addr))
{
perror("Set i2c device address failed");
return -1;
}
return 0;
}
/*
** @brief : i2c delay
** #msec : milliscond to be delay
*/
static void i2c_delay(unsigned char msec)
{
usleep(msec * 1e3);
}