BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / dvb / cx23882 / i2c_core.c
blobfa7f63a0bfe3d5c5b24777eae185f41c3488ebe9
1 /*
2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify,
8 * merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
25 #include <stdlib.h>
26 #include <string.h>
27 #include <KernelExport.h>
28 #include "i2c_core.h"
30 #define TRACE_I2C
31 #ifdef TRACE_I2C
32 #define TRACE dprintf
33 #else
34 #define TRACE(a...)
35 #endif
37 static status_t i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack);
38 static status_t i2c_readbyte(i2c_bus *bus, uint8 *pbyte);
39 static status_t i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size);
40 static status_t i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size);
43 struct _i2c_bus
45 void *cookie;
46 bigtime_t delay;
47 bigtime_t timeout;
48 i2c_set_scl set_scl;
49 i2c_set_sda set_sda;
50 i2c_get_scl get_scl;
51 i2c_get_sda get_sda;
52 sem_id sem;
56 i2c_bus *
57 i2c_create_bus(void *cookie,
58 int frequency,
59 bigtime_t timeout,
60 i2c_set_scl set_scl,
61 i2c_set_sda set_sda,
62 i2c_get_scl get_scl,
63 i2c_get_sda get_sda)
65 i2c_bus *bus = malloc(sizeof(i2c_bus));
66 if (!bus)
67 return NULL;
69 bus->sem = create_sem(1, "i2c bus access");
70 if (bus->sem < 0) {
71 free(bus);
72 return NULL;
75 bus->cookie = cookie;
76 bus->delay = 1000000 / frequency;
77 if (bus->delay == 0)
78 bus->delay = 1;
79 bus->timeout = timeout;
80 bus->set_scl = set_scl;
81 bus->set_sda = set_sda;
82 bus->get_scl = get_scl;
83 bus->get_sda = get_sda;
85 set_scl(cookie, 1);
86 set_sda(cookie, 1);
88 return bus;
92 void
93 i2c_delete_bus(i2c_bus *bus)
95 if (!bus)
96 return;
97 delete_sem(bus->sem);
98 free(bus);
102 static inline void
103 set_sda_low(i2c_bus *bus)
105 bus->set_sda(bus->cookie, 0);
106 snooze(bus->delay);
110 static inline void
111 set_sda_high(i2c_bus *bus)
113 bus->set_sda(bus->cookie, 1);
114 snooze(bus->delay);
118 static inline void
119 set_scl_low(i2c_bus *bus)
121 bus->set_scl(bus->cookie, 0);
122 snooze(bus->delay);
126 static inline status_t
127 set_scl_high(i2c_bus *bus)
129 bigtime_t end = system_time() + bus->timeout;
130 bus->set_scl(bus->cookie, 1);
131 while (0 == bus->get_scl(bus->cookie)) {
132 if (system_time() > end)
133 return B_TIMED_OUT;
134 snooze(5);
136 snooze(bus->delay);
137 return B_OK;
141 static inline void
142 i2c_start(i2c_bus *bus)
144 set_sda_low(bus);
145 set_scl_low(bus);
149 static inline void
150 i2c_stop(i2c_bus *bus)
152 set_sda_low(bus);
153 set_scl_high(bus);
154 set_sda_high(bus);
158 static inline status_t
159 i2c_start_address(i2c_bus *bus, int address, int read /* 1 = read, 0 = write */)
161 status_t res;
162 uint8 addr;
163 int ack;
164 int i;
166 // TRACE("i2c_start_address: enter\n");
168 addr = (address << 1) | (read & 1);
169 for (i = 0; i < 5; i++) {
170 i2c_start(bus);
171 res = i2c_writebyte(bus, addr, &ack);
172 if (res == B_OK) {
173 if (ack)
174 break;
175 res = B_ERROR;
177 if (res == B_TIMED_OUT)
178 break;
179 i2c_stop(bus);
180 snooze(50);
183 // TRACE("i2c_start_address: %s, ack %d, i %d\n", strerror(res), ack, i);
184 return res;
188 /* write one byte and the ack/nack
189 * return values:
190 * B_OK => byte transmitted
191 * B_ERROR => arbitration lost
192 * B_TIMED_OUT => time out
194 status_t
195 i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack)
197 int has_arbitration;
199 int i;
200 has_arbitration = 1;
201 for (i = 7; i >= 0; i--) {
202 int bit = (byte >> i) & 1;
203 if (has_arbitration) {
204 if (bit)
205 set_sda_high(bus);
206 else
207 set_sda_low(bus);
209 if (set_scl_high(bus) != B_OK) {
210 set_sda_high(bus); // avoid blocking the bus
211 TRACE("i2c_writebyte timeout at bit %d\n", i);
212 return B_TIMED_OUT;
214 if (has_arbitration) {
215 if (bit == 1 && 0 == bus->get_sda(bus->cookie)) {
216 has_arbitration = 0;
217 // TRACE("i2c_writebyte lost arbitration at bit %d\n", i);
220 set_scl_low(bus);
222 set_sda_high(bus);
223 if (set_scl_high(bus) != B_OK) {
224 TRACE("i2c_writebyte timeout at ack\n");
225 return B_TIMED_OUT;
227 *ack = 0 == bus->get_sda(bus->cookie);
228 set_scl_low(bus);
230 return has_arbitration ? B_OK : B_ERROR;
234 /* read one byte, don't generate ack
236 status_t
237 i2c_readbyte(i2c_bus *bus, uint8 *pbyte)
239 int i;
240 uint8 byte;
242 set_sda_high(bus);
243 byte = 0;
244 for (i = 7; i >= 0; i--) {
245 if (set_scl_high(bus) != B_OK) {
246 TRACE("i2c_readbyte timeout at bit %d\n", i);
247 return B_TIMED_OUT;
249 byte = (byte << 1) | bus->get_sda(bus->cookie);
250 set_scl_low(bus);
252 *pbyte = byte;
253 return B_OK;
257 status_t
258 i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size)
260 status_t status;
261 uint8 *bytes;
263 if (size <= 0)
264 return B_BAD_VALUE;
266 status = i2c_start_address(bus, address, 1 /* 1 = read, 0 = write */);
267 if (status != B_OK) {
268 TRACE("i2c_read: error on i2c_start_address: %s\n", strerror(status));
269 return status;
272 bytes = data;
273 while (size > 0) {
274 if (i2c_readbyte(bus, bytes) != B_OK) { // timeout
275 TRACE("i2c_read: timeout error on byte %ld\n", bytes - (uint8 *)data);
276 return B_TIMED_OUT;
278 if (size > 0)
279 set_sda_low(bus); // ack
280 else
281 set_sda_high(bus); // nack
283 if (set_scl_high(bus) != B_OK) {
284 set_sda_high(bus); // avoid blocking the bus
285 TRACE("i2c_read: timeout at ack\n");
286 return B_TIMED_OUT;
288 set_scl_low(bus);
289 set_sda_high(bus);
291 size--;
292 bytes++;
295 i2c_stop(bus);
297 return B_OK;
301 status_t
302 i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size)
304 const uint8 *bytes;
305 status_t status;
306 int ack;
308 if (size <= 0)
309 return B_BAD_VALUE;
311 status = i2c_start_address(bus, address, 0 /* 1 = read, 0 = write */);
312 if (status != B_OK) {
313 TRACE("i2c_write: error on i2c_start_address: %s\n", strerror(status));
314 return status;
317 bytes = data;
318 while (size > 0) {
319 status = i2c_writebyte(bus, *bytes, &ack);
320 if (status == B_TIMED_OUT) {
321 TRACE("i2c_write: timeout error on byte %ld\n", bytes - (uint8 *)data);
322 return B_TIMED_OUT;
324 if (status != B_OK) {
325 TRACE("i2c_write: arbitration lost on byte %ld\n", bytes - (uint8 *)data);
326 break;
328 if (!ack) {
329 TRACE("i2c_write: error, got NACK on byte %ld\n", bytes - (uint8 *)data);
330 break;
332 bytes++;
333 size--;
335 i2c_stop(bus);
337 if (status != B_OK)
338 return status;
340 return ack ? B_OK : B_ERROR;
344 status_t
345 i2c_read(i2c_bus *bus, int address, void *data, int size)
347 status_t res;
348 acquire_sem(bus->sem);
349 res = i2c_read_unlocked(bus, address, data, size);
350 release_sem(bus->sem);
351 return res;
355 status_t
356 i2c_write(i2c_bus *bus, int address, const void *data, int size)
358 status_t res;
359 acquire_sem(bus->sem);
360 res = i2c_write_unlocked(bus, address, data, size);
361 release_sem(bus->sem);
362 return res;
366 status_t
367 i2c_xfer(i2c_bus *bus, int address,
368 const void *write_data, int write_size,
369 void *read_data, int read_size)
371 status_t res;
373 acquire_sem(bus->sem);
375 if (write_data != 0 && write_size != 0) {
376 res = i2c_write_unlocked(bus, address, write_data, write_size);
377 if (res != B_OK) {
378 release_sem(bus->sem);
379 return res;
383 if (read_data != 0 && read_size != 0) {
384 res = i2c_read_unlocked(bus, address, read_data, read_size);
385 if (res != B_OK) {
386 release_sem(bus->sem);
387 return res;
391 release_sem(bus->sem);
393 return B_OK;