vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / common / i2c.c
blobc828a9614e6e6cbe502072f954ca2c621f278906
1 /*
2 * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved.
3 * Copyright 2003, Thomas Kurschel. All Rights Reserved.
4 * Distributed under the terms of the MIT License.
5 */
7 /*!
8 I2C protocol
9 */
11 #include "i2c.h"
13 #include <KernelExport.h>
14 #include <OS.h>
15 #include <string.h>
18 //#define TRACE_I2C
19 #ifdef TRACE_I2C
20 #ifdef __cplusplus
21 extern "C"
22 #endif
23 void _sPrintf(const char *format, ...);
24 # define TRACE(x...) _sPrintf("I2C: " x)
25 #else
26 # define TRACE(x...) ;
27 #endif
30 /*!
31 I2c timings, rounded up (Philips 1995 i2c bus specification, p20)
34 //! Timing for standard mode i2c (100kHz max)
35 const static i2c_timing kTiming100k = {
36 .buf = 5,
37 .hd_sta = 4,
38 .low = 5,
39 .high = 4,
40 .su_sta = 5,
41 .hd_dat = 1,
42 .su_dat = 2,
43 .r = 2,
44 .f = 2,
45 .su_sto = 4,
47 // these are unspecified, use half a clock cycle as a safe guess
48 .start_timeout = 5,
49 .byte_timeout = 5,
50 .bit_timeout = 5,
51 .ack_start_timeout = 5,
52 .ack_timeout = 5
55 //! Timing for fast mode i2c (400kHz max)
56 const static i2c_timing kTiming400k = {
57 .buf = 2,
58 .hd_sta = 1,
59 .low = 2,
60 .high = 1,
61 .su_sta = 1,
62 .hd_dat = 0,
63 .su_dat = 1,
64 .r = 1,
65 .f = 1,
66 .su_sto = 1,
68 // these are unspecified, use half a clock cycle as a safe guess
69 .start_timeout = 2,
70 .byte_timeout = 2,
71 .bit_timeout = 2,
72 .ack_start_timeout = 2,
73 .ack_timeout = 2
77 /*!
78 There's no spin in user space, but we need it to wait a couple
79 of microseconds only
80 (in this case, snooze has much too much overhead)
82 void
83 spin(bigtime_t delay)
85 bigtime_t startTime = system_time();
87 while (system_time() - startTime < delay)
92 //! Wait until slave releases clock signal ("clock stretching")
93 static status_t
94 wait_for_clk(const i2c_bus *bus, bigtime_t timeout)
96 bigtime_t startTime;
98 // wait for clock signal to raise
99 spin(bus->timing.r);
101 startTime = system_time();
103 while (true) {
104 int clk, data;
106 bus->get_signals(bus->cookie, &clk, &data);
107 if (clk != 0)
108 return B_OK;
110 if (system_time() - startTime > timeout) {
111 TRACE("%s: Timeout waiting on clock (r)\n");
112 return B_TIMEOUT;
115 spin(bus->timing.r);
120 //! Send start or repeated start condition
121 static status_t
122 send_start_condition(const i2c_bus *bus)
124 status_t status;
126 bus->set_signals(bus->cookie, 1, 1);
128 status = wait_for_clk(bus, bus->timing.start_timeout);
129 if (status != B_OK) {
130 TRACE("%s: Timeout sending start condition\n", __func__);
131 return status;
134 spin(bus->timing.su_sta);
135 bus->set_signals(bus->cookie, 1, 0);
136 spin(bus->timing.hd_sta);
137 bus->set_signals(bus->cookie, 0, 0);
138 spin(bus->timing.f);
140 return B_OK;
144 //! Send stop condition
145 static status_t
146 send_stop_condition(const i2c_bus *bus)
148 status_t status;
150 bus->set_signals(bus->cookie, 0, 0);
151 spin(bus->timing.r);
152 bus->set_signals(bus->cookie, 1, 0);
154 // a slave may wait for us, so let elapse the acknowledge timeout
155 // to make the slave release bus control
156 status = wait_for_clk(bus, bus->timing.ack_timeout);
157 if (status != B_OK) {
158 TRACE("%s: Timeout sending stop condition\n", __func__);
159 return status;
162 spin(bus->timing.su_sto);
163 bus->set_signals(bus->cookie, 1, 1);
164 spin(bus->timing.buf);
166 return B_OK;
170 //! Send one bit
171 static status_t
172 send_bit(const i2c_bus *bus, uint8 bit, int timeout)
174 status_t status;
176 //TRACE("send_bit(bit = %d)\n", bit & 1);
178 bus->set_signals(bus->cookie, 0, bit & 1);
179 spin(bus->timing.su_dat);
180 bus->set_signals(bus->cookie, 1, bit & 1);
182 status = wait_for_clk(bus, timeout);
183 if (status != B_OK) {
184 TRACE("%s: Timeout when sending next bit\n", __func__);
185 return status;
188 spin(bus->timing.high);
189 bus->set_signals(bus->cookie, 0, bit & 1);
190 spin(bus->timing.f + bus->timing.low);
192 return B_OK;
196 //! Send acknowledge and wait for reply
197 static status_t
198 send_acknowledge(const i2c_bus *bus)
200 status_t status;
201 bigtime_t startTime;
203 // release data so slave can modify it
204 bus->set_signals(bus->cookie, 0, 1);
205 spin(bus->timing.su_dat);
206 bus->set_signals(bus->cookie, 1, 1);
208 status = wait_for_clk(bus, bus->timing.ack_start_timeout);
209 if (status != B_OK) {
210 TRACE("%s: Timeout when sending acknowledge\n", __func__);
211 return status;
214 // data and clock is high, now wait for slave to pull data low
215 // (according to spec, this can happen any time once clock is high)
216 startTime = system_time();
218 while (true) {
219 int clk, data;
221 bus->get_signals(bus->cookie, &clk, &data);
223 if (data == 0)
224 break;
226 if (system_time() - startTime > bus->timing.ack_timeout) {
227 TRACE("%s: slave didn't acknowledge byte within ack_timeout: %ld\n",
228 __func__, bus->timing.ack_timeout);
229 return B_TIMEOUT;
232 spin(bus->timing.r);
235 TRACE("send_acknowledge(): Success!\n");
237 // make sure we've waited at least t_high
238 spin(bus->timing.high);
240 bus->set_signals(bus->cookie, 0, 1);
241 spin(bus->timing.f + bus->timing.low);
243 return B_OK;
247 //! Send byte and wait for acknowledge if <ackowledge> is true
248 static status_t
249 send_byte(const i2c_bus *bus, uint8 byte, bool acknowledge)
251 int i;
253 //TRACE("%s: (byte = %x)\n", __func__, byte);
255 for (i = 7; i >= 0; --i) {
256 status_t status = send_bit(bus, byte >> i,
257 i == 7 ? bus->timing.byte_timeout : bus->timing.bit_timeout);
258 if (status != B_OK)
259 return status;
262 if (acknowledge)
263 return send_acknowledge(bus);
265 return B_OK;
269 //! Send slave address, obeying 10-bit addresses and general call addresses
270 static status_t
271 send_slave_address(const i2c_bus *bus, int slaveAddress, bool isWrite)
273 status_t status;
275 TRACE("%s: 0x%X\n", __func__, slaveAddress);
276 status = send_byte(bus, (slaveAddress & 0xfe) | !isWrite, true);
277 if (status != B_OK)
278 return status;
280 // there are the following special cases if the first byte looks like:
281 // - 0000 0000 - general call address (second byte with address follows)
282 // - 0000 0001 - start byte
283 // - 0000 001x - CBus address
284 // - 0000 010x - address reserved for different bus format
285 // - 0000 011x |
286 // - 0000 1xxx |-> reserved
287 // - 1111 1xxx |
288 // - 1111 0xxx - 10 bit address (second byte contains remaining 8 bits)
290 // the lsb is 0 for write and 1 for read (except for general call address)
291 if ((slaveAddress & 0xff) != 0 && (slaveAddress & 0xf8) != 0xf0)
292 return B_OK;
294 return send_byte(bus, slaveAddress >> 8, true);
295 // send second byte if required
299 //! Receive one bit
300 static status_t
301 receive_bit(const i2c_bus *bus, bool *bit, int timeout)
303 status_t status;
304 int clk, data;
306 bus->set_signals(bus->cookie, 1, 1);
307 // release clock
309 // wait for slave to raise clock
310 status = wait_for_clk(bus, timeout);
311 if (status != B_OK) {
312 TRACE("%s: Timeout waiting for bit sent by slave\n", __func__);
313 return status;
316 bus->get_signals(bus->cookie, &clk, &data);
317 // sample data
319 spin(bus->timing.high);
320 // leave clock high for minimal time
322 bus->set_signals(bus->cookie, 0, 1);
323 // pull clock low so slave waits for us before next bit
325 spin(bus->timing.f + bus->timing.low);
326 // let it settle and leave it low for minimal time
327 // to make sure slave has finished bit transmission too
329 *bit = data;
330 return B_OK;
335 Send positive acknowledge afterwards if <acknowledge> is true,
336 else send negative one
338 static status_t
339 receive_byte(const i2c_bus *bus, uint8 *resultByte, bool acknowledge)
341 uint8 byte = 0;
342 int i;
344 // pull clock low to let slave wait for us
345 bus->set_signals(bus->cookie, 0, 1);
347 for (i = 7; i >= 0; i--) {
348 bool bit;
350 status_t status = receive_bit(bus, &bit,
351 i == 7 ? bus->timing.byte_timeout : bus->timing.bit_timeout);
352 if (status != B_OK)
353 return status;
355 byte = (byte << 1) | bit;
358 //SHOW_FLOW(3, "%x ", byte);
360 *resultByte = byte;
362 return send_bit(bus, acknowledge ? 0 : 1, bus->timing.bit_timeout);
366 //! Send multiple bytes
367 static status_t
368 send_bytes(const i2c_bus *bus, const uint8 *writeBuffer, ssize_t writeLength)
370 TRACE("%s: (length = %ld)\n", __func__, writeLength);
372 for (; writeLength > 0; --writeLength, ++writeBuffer) {
373 status_t status = send_byte(bus, *writeBuffer, true);
374 if (status != B_OK)
375 return status;
378 return B_OK;
382 //! Receive multiple bytes
383 static status_t
384 receive_bytes(const i2c_bus *bus, uint8 *readBuffer, ssize_t readLength)
386 TRACE("%s: (length = %ld)\n", __func__, readLength);
388 for (; readLength > 0; --readLength, ++readBuffer) {
389 status_t status = receive_byte(bus, readBuffer, readLength > 1);
390 if (status != B_OK)
391 return status;
394 return B_OK;
398 // #pragma mark - exported functions
401 //! Combined i2c send+receive format
402 status_t
403 i2c_send_receive(const i2c_bus *bus, int slaveAddress, const uint8 *writeBuffer,
404 size_t writeLength, uint8 *readBuffer, size_t readLength)
406 status_t status = send_start_condition(bus);
407 if (status != B_OK)
408 return status;
410 status = send_slave_address(bus, slaveAddress, true);
411 if (status != B_OK)
412 goto err;
414 status = send_bytes(bus, writeBuffer, writeLength);
415 if (status != B_OK)
416 goto err;
418 status = send_start_condition(bus);
419 if (status != B_OK)
420 return status;
422 status = send_slave_address(bus, slaveAddress, false);
423 if (status != B_OK)
424 goto err;
426 status = receive_bytes(bus, readBuffer, readLength);
427 if (status != B_OK)
428 goto err;
430 return send_stop_condition(bus);
432 err:
433 TRACE("%s: Cancelling transmission\n", __func__);
434 send_stop_condition(bus);
435 return status;
439 void
440 i2c_get100k_timing(i2c_timing *timing)
442 // AKA standard i2c mode
443 memcpy(timing, &kTiming100k, sizeof(i2c_timing));
447 void
448 i2c_get400k_timing(i2c_timing *timing)
450 // AKA fast i2c mode
451 memcpy(timing, &kTiming400k, sizeof(i2c_timing));