source: rtems/cpukit/dev/i2c/i2c-bus.c @ 5bb5e01

Last change on this file since 5bb5e01 was 5bb5e01, checked in by Christian Mauderer <christian.mauderer@…>, on May 26, 2021 at 2:33:40 PM

i2c: Add non blocking read / write

This adds the possibility to open an I2C bus with O_NONBLOCK (or set it
later via fcntl) to get non-blocking transmissions. This means that if
the bus is busy, a read, write or transfer ioctl will return with a
EAGAIN errno.

  • Property mode set to 100644
File size: 7.0 KB
Line 
1/**
2 * @file
3 *
4 * @brief Inter-Integrated Circuit (I2C) Bus Implementation
5 *
6 * @ingroup I2CBus
7 */
8
9/*
10 * Copyright (c) 2014, 2017 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Dornierstr. 4
14 *  82178 Puchheim
15 *  Germany
16 *  <rtems@embedded-brains.de>
17 *
18 * The license and distribution terms for this file may be
19 * found in the file LICENSE in this distribution or at
20 * http://www.rtems.org/license/LICENSE.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <dev/i2c/i2c.h>
28
29#include <rtems/imfs.h>
30
31#include <stdlib.h>
32#include <string.h>
33
34int i2c_bus_try_obtain(i2c_bus *bus)
35{
36  return rtems_recursive_mutex_try_lock(&bus->mutex);
37}
38
39void i2c_bus_obtain(i2c_bus *bus)
40{
41  rtems_recursive_mutex_lock(&bus->mutex);
42}
43
44void i2c_bus_release(i2c_bus *bus)
45{
46  rtems_recursive_mutex_unlock(&bus->mutex);
47}
48
49int i2c_bus_do_transfer(
50  i2c_bus *bus,
51  i2c_msg *msgs,
52  uint32_t msg_count,
53  uint32_t flags
54)
55{
56  int err;
57  uint32_t i;
58  uint32_t j;
59
60  _Assert(msg_count > 0);
61
62  for (i = 0, j = 0; i < msg_count; ++i) {
63    if ((msgs[i].flags & I2C_M_NOSTART) != 0) {
64      if ((msgs[i].flags & I2C_M_RD) != (msgs[j].flags & I2C_M_RD)) {
65        return -EINVAL;
66      }
67
68      if (msgs[i].addr != msgs[j].addr) {
69        return -EINVAL;
70      }
71    } else {
72      j = i;
73    }
74  }
75
76  if ((flags & I2C_BUS_NOBLOCK) != 0) {
77    if (i2c_bus_try_obtain(bus) != 0) {
78      return -EAGAIN;
79    }
80  } else {
81    i2c_bus_obtain(bus);
82  }
83  err = (*bus->transfer)(bus, msgs, msg_count);
84  i2c_bus_release(bus);
85
86  return err;
87}
88
89int i2c_bus_transfer(i2c_bus *bus, i2c_msg *msgs, uint32_t msg_count)
90{
91  return i2c_bus_do_transfer(bus, msgs, msg_count, 0);
92}
93
94static ssize_t i2c_bus_read(
95  rtems_libio_t *iop,
96  void *buffer,
97  size_t count
98)
99{
100  i2c_bus *bus = IMFS_generic_get_context_by_iop(iop);
101  i2c_msg msg = {
102    .addr = bus->default_address,
103    .flags = I2C_M_RD,
104    .len = (uint16_t) count,
105    .buf = buffer
106  };
107  int err;
108  unsigned flags = 0;
109
110  if (bus->ten_bit_address) {
111    msg.flags |= I2C_M_TEN;
112  }
113
114  if (rtems_libio_iop_is_no_delay(iop)) {
115    flags |= I2C_BUS_NOBLOCK;
116  }
117
118  err = i2c_bus_do_transfer(bus, &msg, 1, flags);
119  if (err == 0) {
120    return msg.len;
121  } else {
122    rtems_set_errno_and_return_minus_one(-err);
123  }
124}
125
126static ssize_t i2c_bus_write(
127  rtems_libio_t *iop,
128  const void *buffer,
129  size_t count
130)
131{
132  i2c_bus *bus = IMFS_generic_get_context_by_iop(iop);
133  i2c_msg msg = {
134    .addr = bus->default_address,
135    .flags = 0,
136    .len = (uint16_t) count,
137    .buf = RTEMS_DECONST(void *, buffer)
138  };
139  int err;
140  unsigned flags = 0;
141
142  if (bus->ten_bit_address) {
143    msg.flags |= I2C_M_TEN;
144  }
145
146  if (rtems_libio_iop_is_no_delay(iop)) {
147    flags |= I2C_BUS_NOBLOCK;
148  }
149
150  err = i2c_bus_do_transfer(bus, &msg, 1, flags);
151  if (err == 0) {
152    return msg.len;
153  } else {
154    rtems_set_errno_and_return_minus_one(-err);
155  }
156}
157
158static int i2c_bus_ioctl(
159  rtems_libio_t *iop,
160  ioctl_command_t command,
161  void *arg
162)
163{
164  i2c_bus *bus = IMFS_generic_get_context_by_iop(iop);
165  i2c_rdwr_ioctl_data *rdwr;
166  int err;
167  unsigned flags = 0;
168
169  switch (command) {
170    case I2C_RDWR:
171      rdwr = arg;
172      if (rdwr->nmsgs > 0) {
173        if (rtems_libio_iop_is_no_delay(iop)) {
174          flags |= I2C_BUS_NOBLOCK;
175        }
176        err = i2c_bus_do_transfer(bus, rdwr->msgs, rdwr->nmsgs, flags);
177      } else {
178        err = 0;
179      }
180      break;
181    case I2C_BUS_OBTAIN:
182      i2c_bus_obtain(bus);
183      err = 0;
184      break;
185    case I2C_BUS_RELEASE:
186      i2c_bus_release(bus);
187      err = 0;
188      break;
189    case I2C_BUS_GET_CONTROL:
190      *(i2c_bus **) arg = bus;
191      err = 0;
192      break;
193    case I2C_FUNCS:
194      *(unsigned long *) arg = bus->functionality;
195      err = 0;
196      break;
197    case I2C_RETRIES:
198      bus->retries = (unsigned long) arg;
199      err = 0;
200      break;
201    case I2C_TIMEOUT:
202      bus->timeout = RTEMS_MILLISECONDS_TO_TICKS(10 * (unsigned long) arg);
203      err = 0;
204      break;
205    case I2C_SLAVE:
206    case I2C_SLAVE_FORCE:
207      bus->default_address = (unsigned long) arg;
208      err = 0;
209      break;
210    case I2C_TENBIT:
211      bus->ten_bit_address = (unsigned long) arg != 0;
212      err = 0;
213      break;
214    case I2C_PEC:
215      bus->use_pec = (unsigned long) arg != 0;
216      err = 0;
217      break;
218    case I2C_BUS_SET_CLOCK:
219      i2c_bus_obtain(bus);
220      err = (*bus->set_clock)(bus, (unsigned long) arg);
221      i2c_bus_release(bus);
222      break;
223    default:
224      err = -ENOTTY;
225      break;
226  }
227
228  if (err == 0) {
229    return 0;
230  } else {
231    rtems_set_errno_and_return_minus_one(-err);
232  }
233}
234
235static const rtems_filesystem_file_handlers_r i2c_bus_handler = {
236  .open_h = rtems_filesystem_default_open,
237  .close_h = rtems_filesystem_default_close,
238  .read_h = i2c_bus_read,
239  .write_h = i2c_bus_write,
240  .ioctl_h = i2c_bus_ioctl,
241  .lseek_h = rtems_filesystem_default_lseek,
242  .fstat_h = IMFS_stat,
243  .ftruncate_h = rtems_filesystem_default_ftruncate,
244  .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
245  .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
246  .fcntl_h = rtems_filesystem_default_fcntl,
247  .kqfilter_h = rtems_filesystem_default_kqfilter,
248  .mmap_h = rtems_filesystem_default_mmap,
249  .poll_h = rtems_filesystem_default_poll,
250  .readv_h = rtems_filesystem_default_readv,
251  .writev_h = rtems_filesystem_default_writev
252};
253
254static void i2c_bus_node_destroy(IMFS_jnode_t *node)
255{
256  i2c_bus *bus;
257
258  bus = IMFS_generic_get_context_by_node(node);
259  (*bus->destroy)(bus);
260
261  IMFS_node_destroy_default(node);
262}
263
264static const IMFS_node_control i2c_bus_node_control = IMFS_GENERIC_INITIALIZER(
265  &i2c_bus_handler,
266  IMFS_node_initialize_generic,
267  i2c_bus_node_destroy
268);
269
270int i2c_bus_register(
271  i2c_bus *bus,
272  const char *bus_path
273)
274{
275  int rv;
276
277  rv = IMFS_make_generic_node(
278    bus_path,
279    S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
280    &i2c_bus_node_control,
281    bus
282  );
283  if (rv != 0) {
284    (*bus->destroy)(bus);
285  }
286
287  return rv;
288}
289
290static int i2c_bus_transfer_default(
291  i2c_bus *bus,
292  i2c_msg *msgs,
293  uint32_t msg_count
294)
295{
296  (void) bus;
297  (void) msgs;
298  (void) msg_count;
299
300  return -EIO;
301}
302
303static int i2c_bus_set_clock_default(i2c_bus *bus, unsigned long clock)
304{
305  (void) bus;
306  (void) clock;
307
308  return -EIO;
309}
310
311static int i2c_bus_do_init(
312  i2c_bus *bus,
313  void (*destroy)(i2c_bus *bus)
314)
315{
316  rtems_recursive_mutex_init(&bus->mutex, "I2C Bus");
317  bus->transfer = i2c_bus_transfer_default;
318  bus->set_clock = i2c_bus_set_clock_default;
319  bus->destroy = destroy;
320
321  return 0;
322}
323
324void i2c_bus_destroy(i2c_bus *bus)
325{
326  rtems_recursive_mutex_destroy(&bus->mutex);
327}
328
329void i2c_bus_destroy_and_free(i2c_bus *bus)
330{
331  i2c_bus_destroy(bus);
332  free(bus);
333}
334
335int i2c_bus_init(i2c_bus *bus)
336{
337  memset(bus, 0, sizeof(*bus));
338
339  return i2c_bus_do_init(bus, i2c_bus_destroy);
340}
341
342i2c_bus *i2c_bus_alloc_and_init(size_t size)
343{
344  i2c_bus *bus = NULL;
345
346  if (size >= sizeof(*bus)) {
347    bus = calloc(1, size);
348    if (bus != NULL) {
349      int rv;
350
351      rv = i2c_bus_do_init(bus, i2c_bus_destroy_and_free);
352      if (rv != 0) {
353        return NULL;
354      }
355    }
356  }
357
358  return bus;
359}
Note: See TracBrowser for help on using the repository browser.