source: rtems/cpukit/dev/i2c/i2c-bus.c @ 369328f0

4.115
Last change on this file since 369328f0 was 41c5f1b7, checked in by Sebastian Huber <sebastian.huber@…>, on 11/07/14 at 12:47:39

Add I2C driver framework

This I2C driver framework has some major differences compared to libi2c.

  • It is compatible to the Linux I2C user-space API.
  • It uses generic IMFS nodes and thus reduces the levels of indirection.
  • The drivers don't have to mess around with minor numbers to get their state information.
  • No arbitrary bus controller model is assumed. The main task of an I2C bus controller driver is to process I2C messages. How this is done is private to the driver.
  • Scatter/gather operations are supported (I2C_M_NOSTART).
  • Property mode set to 100644
File size: 6.8 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 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#if 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
34void i2c_bus_obtain(i2c_bus *bus)
35{
36  rtems_status_code sc;
37
38  sc = rtems_semaphore_obtain(bus->mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
39  _Assert(sc == RTEMS_SUCCESSFUL);
40  (void) sc;
41}
42
43void i2c_bus_release(i2c_bus *bus)
44{
45  rtems_status_code sc;
46
47  sc = rtems_semaphore_release(bus->mutex);
48  _Assert(sc == RTEMS_SUCCESSFUL);
49  (void) sc;
50}
51
52int i2c_bus_transfer(i2c_bus *bus, i2c_msg *msgs, uint32_t msg_count)
53{
54  int err;
55  uint32_t i;
56  uint32_t j;
57
58  _Assert(msg_count > 0);
59
60  for (i = 0, j = 0; i < msg_count; ++i) {
61    if ((msgs[i].flags & I2C_M_NOSTART) != 0) {
62      if ((msgs[i].flags & I2C_M_RD) != (msgs[j].flags & I2C_M_RD)) {
63        return -EINVAL;
64      }
65
66      if (msgs[i].addr != msgs[j].addr) {
67        return -EINVAL;
68      }
69    } else {
70      j = i;
71    }
72  }
73
74  i2c_bus_obtain(bus);
75  err = (*bus->transfer)(bus, msgs, msg_count);
76  i2c_bus_release(bus);
77
78  return err;
79}
80
81static ssize_t i2c_bus_read(
82  rtems_libio_t *iop,
83  void *buffer,
84  size_t count
85)
86{
87  i2c_bus *bus = IMFS_generic_get_context_by_iop(iop);
88  i2c_msg msg = {
89    .addr = bus->default_address,
90    .flags = I2C_M_RD,
91    .len = (uint16_t) count,
92    .buf = buffer
93  };
94  int err;
95
96  if (bus->ten_bit_address) {
97    msg.flags |= I2C_M_TEN;
98  }
99
100  err = i2c_bus_transfer(bus, &msg, 1);
101  if (err == 0) {
102    return msg.len;
103  } else {
104    rtems_set_errno_and_return_minus_one(-err);
105  }
106}
107
108static ssize_t i2c_bus_write(
109  rtems_libio_t *iop,
110  const void *buffer,
111  size_t count
112)
113{
114  i2c_bus *bus = IMFS_generic_get_context_by_iop(iop);
115  i2c_msg msg = {
116    .addr = bus->default_address,
117    .flags = 0,
118    .len = (uint16_t) count,
119    .buf = RTEMS_DECONST(void *, buffer)
120  };
121  int err;
122
123  if (bus->ten_bit_address) {
124    msg.flags |= I2C_M_TEN;
125  }
126
127  err = i2c_bus_transfer(bus, &msg, 1);
128  if (err == 0) {
129    return msg.len;
130  } else {
131    rtems_set_errno_and_return_minus_one(-err);
132  }
133}
134
135static int i2c_bus_ioctl(
136  rtems_libio_t *iop,
137  ioctl_command_t command,
138  void *arg
139)
140{
141  i2c_bus *bus = IMFS_generic_get_context_by_iop(iop);
142  i2c_rdwr_ioctl_data *rdwr;
143  int err;
144
145  switch (command) {
146    case I2C_RDWR:
147      rdwr = arg;
148      if (rdwr->nmsgs > 0) {
149        err = i2c_bus_transfer(bus, rdwr->msgs, rdwr->nmsgs);
150      } else {
151        err = 0;
152      }
153      break;
154    case I2C_BUS_OBTAIN:
155      i2c_bus_obtain(bus);
156      err = 0;
157      break;
158    case I2C_BUS_RELEASE:
159      i2c_bus_release(bus);
160      err = 0;
161      break;
162    case I2C_BUS_GET_CONTROL:
163      *(i2c_bus **) arg = bus;
164      err = 0;
165      break;
166    case I2C_FUNCS:
167      *(unsigned long *) arg = bus->functionality;
168      err = 0;
169      break;
170    case I2C_RETRIES:
171      bus->retries = (unsigned long) arg;
172      err = 0;
173      break;
174    case I2C_TIMEOUT:
175      bus->timeout = RTEMS_MILLISECONDS_TO_TICKS(10 * (unsigned long) arg);
176      err = 0;
177      break;
178    case I2C_SLAVE:
179    case I2C_SLAVE_FORCE:
180      bus->default_address = (unsigned long) arg;
181      err = 0;
182      break;
183    case I2C_TENBIT:
184      bus->ten_bit_address = (unsigned long) arg != 0;
185      err = 0;
186      break;
187    case I2C_PEC:
188      bus->use_pec = (unsigned long) arg != 0;
189      err = 0;
190      break;
191    case I2C_BUS_SET_CLOCK:
192      i2c_bus_obtain(bus);
193      err = (*bus->set_clock)(bus, (unsigned long) arg);
194      i2c_bus_release(bus);
195      break;
196    default:
197      err = -ENOTTY;
198      break;
199  }
200
201  if (err == 0) {
202    return 0;
203  } else {
204    rtems_set_errno_and_return_minus_one(-err);
205  }
206}
207
208static const rtems_filesystem_file_handlers_r i2c_bus_handler = {
209  .open_h = rtems_filesystem_default_open,
210  .close_h = rtems_filesystem_default_close,
211  .read_h = i2c_bus_read,
212  .write_h = i2c_bus_write,
213  .ioctl_h = i2c_bus_ioctl,
214  .lseek_h = rtems_filesystem_default_lseek,
215  .fstat_h = IMFS_stat,
216  .ftruncate_h = rtems_filesystem_default_ftruncate,
217  .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
218  .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
219  .fcntl_h = rtems_filesystem_default_fcntl,
220  .kqfilter_h = rtems_filesystem_default_kqfilter,
221  .poll_h = rtems_filesystem_default_poll,
222  .readv_h = rtems_filesystem_default_readv,
223  .writev_h = rtems_filesystem_default_writev
224};
225
226static IMFS_jnode_t *i2c_bus_node_destroy(IMFS_jnode_t *node)
227{
228  i2c_bus *bus;
229
230  bus = IMFS_generic_get_context_by_node(node);
231  (*bus->destroy)(bus);
232
233  return node;
234}
235
236static const IMFS_node_control i2c_bus_node_control = {
237  .imfs_type = IMFS_GENERIC,
238  .handlers = &i2c_bus_handler,
239  .node_initialize = IMFS_node_initialize_generic,
240  .node_remove = IMFS_node_remove_default,
241  .node_destroy = i2c_bus_node_destroy
242};
243
244int i2c_bus_register(
245  i2c_bus *bus,
246  const char *bus_path
247)
248{
249  int rv;
250
251  rv = IMFS_make_generic_node(
252    bus_path,
253    S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
254    &i2c_bus_node_control,
255    bus
256  );
257  if (rv != 0) {
258    (*bus->destroy)(bus);
259  }
260
261  return rv;
262}
263
264static int i2c_bus_transfer_default(
265  i2c_bus *bus,
266  i2c_msg *msgs,
267  uint32_t msg_count
268)
269{
270  (void) bus;
271  (void) msgs;
272  (void) msg_count;
273
274  return -EIO;
275}
276
277static int i2c_bus_set_clock_default(i2c_bus *bus, unsigned long clock)
278{
279  (void) bus;
280  (void) clock;
281
282  return -EIO;
283}
284
285static int i2c_bus_do_init(
286  i2c_bus *bus,
287  void (*destroy)(i2c_bus *bus)
288)
289{
290  rtems_status_code sc;
291
292  sc = rtems_semaphore_create(
293    rtems_build_name('I', '2', 'C', ' '),
294    1,
295    RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
296    0,
297    &bus->mutex
298  );
299  if (sc != RTEMS_SUCCESSFUL) {
300    (*destroy)(bus);
301
302    rtems_set_errno_and_return_minus_one(ENOMEM);
303  }
304
305  bus->transfer = i2c_bus_transfer_default;
306  bus->set_clock = i2c_bus_set_clock_default;
307  bus->destroy = destroy;
308
309  return 0;
310}
311
312void i2c_bus_destroy(i2c_bus *bus)
313{
314  rtems_status_code sc;
315
316  sc = rtems_semaphore_delete(bus->mutex);
317  _Assert(sc == RTEMS_SUCCESSFUL);
318  (void) sc;
319}
320
321void i2c_bus_destroy_and_free(i2c_bus *bus)
322{
323  i2c_bus_destroy(bus);
324  free(bus);
325}
326
327int i2c_bus_init(i2c_bus *bus)
328{
329  memset(bus, 0, sizeof(*bus));
330
331  return i2c_bus_do_init(bus, i2c_bus_destroy);
332}
333
334i2c_bus *i2c_bus_alloc_and_init(size_t size)
335{
336  i2c_bus *bus = NULL;
337
338  if (size >= sizeof(*bus)) {
339    bus = calloc(1, size);
340    if (bus != NULL) {
341      int rv;
342
343      rv = i2c_bus_do_init(bus, i2c_bus_destroy_and_free);
344      if (rv != 0) {
345        return NULL;
346      }
347    }
348  }
349
350  return bus;
351}
Note: See TracBrowser for help on using the repository browser.