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

4.11
Last change on this file since cf36b70 was cf36b70, checked in by Sebastian Huber <sebastian.huber@…>, on Dec 31, 2014 at 9:56:05 AM

IMFS: Replace node union with individual struct

This reduces the average node size.

Add and use IMFS_GENERIC_INITIALIZER().

  • Property mode set to 100644
File size: 6.7 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 void 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  IMFS_node_destroy_default(node);
234}
235
236static const IMFS_node_control i2c_bus_node_control = IMFS_GENERIC_INITIALIZER(
237  &i2c_bus_handler,
238  IMFS_node_initialize_generic,
239  i2c_bus_node_destroy
240);
241
242int i2c_bus_register(
243  i2c_bus *bus,
244  const char *bus_path
245)
246{
247  int rv;
248
249  rv = IMFS_make_generic_node(
250    bus_path,
251    S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
252    &i2c_bus_node_control,
253    bus
254  );
255  if (rv != 0) {
256    (*bus->destroy)(bus);
257  }
258
259  return rv;
260}
261
262static int i2c_bus_transfer_default(
263  i2c_bus *bus,
264  i2c_msg *msgs,
265  uint32_t msg_count
266)
267{
268  (void) bus;
269  (void) msgs;
270  (void) msg_count;
271
272  return -EIO;
273}
274
275static int i2c_bus_set_clock_default(i2c_bus *bus, unsigned long clock)
276{
277  (void) bus;
278  (void) clock;
279
280  return -EIO;
281}
282
283static int i2c_bus_do_init(
284  i2c_bus *bus,
285  void (*destroy)(i2c_bus *bus)
286)
287{
288  rtems_status_code sc;
289
290  sc = rtems_semaphore_create(
291    rtems_build_name('I', '2', 'C', ' '),
292    1,
293    RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
294    0,
295    &bus->mutex
296  );
297  if (sc != RTEMS_SUCCESSFUL) {
298    (*destroy)(bus);
299
300    rtems_set_errno_and_return_minus_one(ENOMEM);
301  }
302
303  bus->transfer = i2c_bus_transfer_default;
304  bus->set_clock = i2c_bus_set_clock_default;
305  bus->destroy = destroy;
306
307  return 0;
308}
309
310void i2c_bus_destroy(i2c_bus *bus)
311{
312  rtems_status_code sc;
313
314  sc = rtems_semaphore_delete(bus->mutex);
315  _Assert(sc == RTEMS_SUCCESSFUL);
316  (void) sc;
317}
318
319void i2c_bus_destroy_and_free(i2c_bus *bus)
320{
321  i2c_bus_destroy(bus);
322  free(bus);
323}
324
325int i2c_bus_init(i2c_bus *bus)
326{
327  memset(bus, 0, sizeof(*bus));
328
329  return i2c_bus_do_init(bus, i2c_bus_destroy);
330}
331
332i2c_bus *i2c_bus_alloc_and_init(size_t size)
333{
334  i2c_bus *bus = NULL;
335
336  if (size >= sizeof(*bus)) {
337    bus = calloc(1, size);
338    if (bus != NULL) {
339      int rv;
340
341      rv = i2c_bus_do_init(bus, i2c_bus_destroy_and_free);
342      if (rv != 0) {
343        return NULL;
344      }
345    }
346  }
347
348  return bus;
349}
Note: See TracBrowser for help on using the repository browser.