summaryrefslogtreecommitdiffstats
path: root/cpukit/libi2c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libi2c')
-rw-r--r--cpukit/libi2c/.cvsignore2
-rw-r--r--cpukit/libi2c/Makefile.am11
-rw-r--r--cpukit/libi2c/README_libi2c367
-rw-r--r--cpukit/libi2c/libi2c.c778
-rw-r--r--cpukit/libi2c/libi2c.h510
5 files changed, 1668 insertions, 0 deletions
diff --git a/cpukit/libi2c/.cvsignore b/cpukit/libi2c/.cvsignore
new file mode 100644
index 0000000000..282522db03
--- /dev/null
+++ b/cpukit/libi2c/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/cpukit/libi2c/Makefile.am b/cpukit/libi2c/Makefile.am
new file mode 100644
index 0000000000..c422529bc8
--- /dev/null
+++ b/cpukit/libi2c/Makefile.am
@@ -0,0 +1,11 @@
+##
+## $Id$
+##
+
+include $(top_srcdir)/automake/compile.am
+
+noinst_LIBRARIES = libi2c.a
+
+libi2c_a_SOURCES = libi2c.c libi2c.h
+
+include $(top_srcdir)/automake/local.am
diff --git a/cpukit/libi2c/README_libi2c b/cpukit/libi2c/README_libi2c
new file mode 100644
index 0000000000..f1a8fd017e
--- /dev/null
+++ b/cpukit/libi2c/README_libi2c
@@ -0,0 +1,367 @@
+#
+# $Id$
+#
+
+=====================
+Copyright and License
+=====================
+
+For Copyright and License of the source code, see the header in
+libi2c.c.
+
+=========
+Overview
+========
+
+This directory contains a general I2C/SPI API library. It offers a
+standard API to I2C or SPI based device drivers, abstracting the low
+level driver (dealing with the I2C/SPI controller hardware of the
+board) from the high-level drivers (dealing with devices connected to
+the I2C or SPI bus).
+
+In most cases throughout this document, i2c and spi devices are
+handled in a similar way. Therefore spi will not be explicitly named
+in every location.
+
+=========
+Features
+=========
+
+ + supports multiple i2c and/or spi busses
+
+ + supports multiple devices connected to each i2c/spi bus
+
+ + handles bus and device registration to the I/O manager
+
+=========
+Structure
+=========
+
+This library defines a layered API to i2c and spi devices. The
+layering is:
+
+ +----------------------------------------+
+ 6| Application |
+ +----------------------------------------+
+ 5| RTEMS I/O Manager |
+ +----------------------------------------+
+ 4|** libi2c OS adaption layer **|
+ +----------------------------------------+
+ 3| high level i2c device driver |
+ | (EEPROM, RTC, ...) |
+ | (e.g. in c/src/libchip/i2c) |
+ +----------------------------------------+
+ 2|** libi2c low level abstraction layer **|
+ +----------------------------------------+
+ 1| i2c controller driver |
+ | (in BSP) |
+ +----------------------------------------+
+
+This document will describe the following interfaces in separate
+sections:
+
+ + the interface between the RTEMS I/O Manager and the libi2c OS
+ interface (5<->4)
+
+ + the interface between the libi2c OS interface and the high level
+ i2c device driver (4<->3)
+
+ + the interface between the high level i2c device driver and the
+ libi2c low level abstraction layer (3<->2)
+
+ + the interface between the libi2c low level abstraction layer and
+ the i2c controller driver (2<->1)
+
+===================================
+Differences between i2c and spi bus
+===================================
+SPI and I2C has many similarities, but also some differences:
+
+- I2C uses inband addressing (the first bits sent select, which slave
+device is addressed) while SPI uses dedicated select lines to address
+a slave device
+
+- SPI supports combined full duplex read-write transactions while I2C
+either sends or receives data from a slave device
+
+- SPI supports a varity of per-slave options, which include:
+ - number of bits per character to transfer
+ - polarity and phase of clock wrt data
+ - clock frequency
+
+The libi2c API defines a superset of functions to handle both flavours
+of serial data transmission, but care should be taken not to use
+features dedicated to the wrong type of serial bus.
+
+
+======================
+Library Initialization
+======================
+Before any libi2c API is used, the library must be initialized. This
+is achived with a call to function
+
+ rtems_libi2c_initialize ().
+
+It creates a global mutex to lock internal data structures and
+registers the OS adaption layer to the RTEMS I/O manager.
+
+Any subsequent call to this function will be silently ignored.
+
+Typically the BSP startup code will perform this initialization.
+
+A proper place for initializing the i2c layer and populating it
+with busses and device drivers (see 'Bus Registration' and
+'Device/Driver Registration' below) is the 'predriver_hook'
+where most facilities (such as malloc, libio) are already
+available. Note, however, that 'stdio' is not yet functional
+at this point and all i2c bus and device drivers should carefully
+avoid using stdio so that other drivers which may build on top
+of i2c devices may be initialized properly (this may happen
+just after 'predriver_hook' when stdio is still not available).
+E.g., drivers listed in the configuration table are initialized
+during this step.
+
+Note that while 'libi2c' could be initialized from the rtems
+configuration table like other drivers there is no easy
+way of populating the i2c framework with bus- and device-
+drivers at this point (unless a special 'i2c' configuration
+table describing the bus layout is implemented in the future).
+
+For the time being, we must rely on the BSP (predriver_hook)
+to initialize the i2c system if it is used by other drivers
+(e.g., the RTC driver may have to use a i2c device).
+
+===================
+Bus Registration
+===================
+Each i2c and/or spi bus available must be registerd with a call to
+
+int rtems_libi2c_register_bus (char *name,
+ rtems_libi2c_bus_t * bus)
+
+It registers the bus to the libi2c internal data structures and
+creates a device node in the RTEMS filesystem with the given name. If
+no name is given (name==NULL), then the default name "/dev/i2c" is
+used instead.
+
+With the second calling parameter "rtems_libi2c_bus_t * bus" the
+caller passes in a set of function pointers, which define the entries
+into the i2c controller driver (defined in the BSP).
+
+This call returns an integer bus number, which can be used in
+subsequent calls to register devices attached to this bus (see below).
+
+Typically the BSP startup code will perform this registration for each
+bus available on the board.
+
+==========================
+Device/Driver Registration
+==========================
+Each device attached to an i2c or spi bus must be registered with a
+call to
+
+int
+rtems_libi2c_register_drv (char *name, rtems_libi2c_drv_t * drvtbl,
+ unsigned bus, unsigned i2caddr);
+
+With this call, libi2c is informed, that:
+
+- a device is attached to the given "bus" number (which in fact is the
+return value received from a previous rtems_libi2c_register_bus()
+call) with the address "i2caddr"
+
+- the device is managed by a driver, who's entry functions are listed
+ in "drvtbl"
+
+- the device should be registered with the given "name" in the device
+ tree of the filesystem.
+
+The call will create a proper minor device number, which has the bus
+number and i2c_address encoded. This minor number is the return value
+of the call and is also associated with the filesystem node created
+for this device.
+
+Note: If you have multiple devices of the same type, you must register
+each of them through a separate call (with the same "drvtbl", but
+different name/bus/i2caddr).
+
+====================================================================
+(5<->4) RTEMS I/O Manager and the libi2c OS adaption layer IF
+====================================================================
+
+The RTEMS I/O Manager regards the libi2c OS adaption layer as a normal
+RTEMS Device Driver with one unique major number and a set of minor
+numbers, one for each bus and one for each device attached to one of
+the busses.
+
+Therefore the libi2c OS adaption layer provides the standard calls:
+
+static rtems_driver_address_table libi2c_io_ops = {
+ initialization_entry: i2c_init,
+ open_entry: i2c_open,
+ close_entry: i2c_close,
+ read_entry: i2c_read,
+ write_entry: i2c_write,
+ control_entry: i2c_ioctl
+};
+
+These calls perform some parameter checking and then call the
+appropriate high level i2c device driver function, if available,
+according to the entries in the "drvtbl" passed in the
+rtems_libi2c_register_drv() call.
+
+There are two exceptions: when i2c_read or i2c_write is called with a
+minor number specifying a bus (and not a device attached to the bus),
+then the respective transfer is performed as a raw byte stream
+transfer to the bus.
+
+The main reason for the libi2c OS adaption layer is, that it
+dispatches the RTEMS I/O Manager calls to the proper device driver
+according to the minor number used.
+
+====================================================================
+libi2c OS adaption layer and the high level i2c device driver IF
+====================================================================
+
+Each high level i2c device driver provides a set of functions in the
+rtems_libi2c_drv_t data structure passed the libi2c when the device is
+registered (see "Device registration" above). These function directly match
+the RTEMS I/O Mangers calls "open", "close", "read", "write",
+"control", and they are passed the same arguments. Functions not
+needed may be ommited (and replaced by a NULL pointer in
+rtems_libi2c_drv_t).
+
+======================================================================
+high level i2c device driver and libi2c low level abstraction layer IF
+======================================================================
+libi2c provides a set of functions for the high level drivers. These
+functions are:
+
+rtems_libi2c_send_start();
+rtems_libi2c_send_stop();
+rtems_libi2c_send_addr();
+rtems_libi2c_read_bytes();
+rtems_libi2c_write_bytes();
+rtems_libi2c_start_read_bytes();
+rtems_libi2c_start_write_bytes();
+rtems_libi2c_ioctl();
+
+Please look into libi2c.h for the proper parameters and return codes.
+
+These functions perform the proper i2c operations when called.
+
+A typical access sequence for the I2C bus would be:
+
+rtems_libi2c_send_start();
+rtems_libi2c_send_addr();
+rtems_libi2c_write_bytes();
+rtems_libi2c_send_stop();
+
+Alternatively, the rtems_libi2c_write_bytes() call could be relpaced
+with a
+ rtems_libi2c_read_bytes()
+
+call or a sequence of multiple calls.
+
+Note: rtems_libi2c_send_start() locks the i2c/spi bus used, so no other
+device can use this i2c/spi bus, until rtems_libi2c_send_stop() function
+is called for the same device.
+
+Special provisions for SPI devices:
+===================================
+For SPI devices and their drivers, the libi2c interface is used
+slightly differently:
+
+rtems_libi2c_send_start() will lock access to the SPI bus, but has no
+effect on the hardware bus interface.
+
+rtems_libi2c_ioctl(...,RTEMS_LIBI2C_IOCTL_SET_TFRMODE,...) will set
+the transfer mode (bit rate, clock phase and polaritiy, bits per
+char...) according to the rtems_libi2c_tfr_mode_t structure passed in.
+
+rtems_libi2c_send_addr() will activate the proper select line to
+address a certain SPI device. The correspondance between an address
+and the select line pulled is BSP specific.
+
+rtems_libi2c_send_stop(); will deactivate the address line and unlock
+the bus.
+
+A typical access sequence for the SPI bus would be:
+
+rtems_libi2c_send_start();
+rtems_libi2c_ioctl(...,RTEMS_LIBI2C_IOCTL_SET_TFRMODE,...);
+rtems_libi2c_send_addr();
+rtems_libi2c_write_bytes();
+rtems_libi2c_send_stop();
+
+Alternatively, the rtems_libi2c_write_bytes() call could be relpaced
+with a
+ rtems_libi2c_read_bytes()
+or a
+ rtems_libi2c_ioctl(...,RTEMS_LIBI2C_IOCTL_READ_WRITE,...)
+call or a sequence of multiple calls.
+
+====================================================================
+libi2c low level abstraction layer and i2c controller driver IF
+====================================================================
+Each low level i2c/spi driver must provide a set of bus_ops functions
+as defined in the rtems_libi2c_bus_ops_t structure.
+
+typedef struct rtems_libi2c_bus_ops_
+{
+ /* Initialize the bus; might be called again to reset the bus driver */
+ rtems_status_code (*init) (rtems_libi2c_bus_t * bushdl);
+ /* Send start condition */
+ rtems_status_code (*send_start) (rtems_libi2c_bus_t * bushdl);
+ /* Send stop condition */
+ rtems_status_code (*send_stop) (rtems_libi2c_bus_t * bushdl);
+ /* initiate transfer from (rw!=0) or to a device */
+ rtems_status_code (*send_addr) (rtems_libi2c_bus_t * bushdl,
+ uint32_t addr, int rw);
+ /* read a number of bytes */
+ int (*read_bytes) (rtems_libi2c_bus_t * bushdl, unsigned char *bytes,
+ int nbytes);
+ /* write a number of bytes */
+ int (*write_bytes) (rtems_libi2c_bus_t * bushdl, unsigned char *bytes,
+ int nbytes);
+ /* ioctl misc functions */
+ int (*ioctl) (rtems_libi2c_bus_t * bushdl,
+ int cmd,
+ void *buffer;
+ );
+} rtems_libi2c_bus_ops_t;
+
+Each of these functions performs the corresponding function to the i2c
+bus.
+
+Special provisions for SPI devices:
+===================================
+For SPI busses, special behaviour is required:
+
+(*send_start) (rtems_libi2c_bus_t * bushdl)
+ normally is an empty function.
+
+ (*send_addr) (rtems_libi2c_bus_t * bushdl, uint32_t addr, int rw)
+ will activate the SPI select line matching to addr.
+
+(*send_stop) (rtems_libi2c_bus_t * bushdl)
+ will deactivate the SPI select line
+
+(*ioctl(...,RTEMS_LIBI2C_IOCTL_SET_TFRMODE,...)
+ will set the transfer mode (bit rate, clock phase and
+ polaritiy, bits per char...) according to the
+ rtems_libi2c_tfr_mode_t structure passed in.
+
+(*ioctl(...,RTEMS_LIBI2C_IOCTL_READ_WRITE,...)
+ will send and receive data at the same time.
+
+Note:
+
+- low-level I2C drivers normally are specific to the master
+device, but independent from the board hardware. So in many cases they
+can totally reside in libcpu or libchip.
+
+- low-level SPI drivers are mostly board independent, but the
+ addressing is board/BSP dependent. Therefore the (*send_start),
+ (*send_addr) and (*send_stop) functions are typically defined in the
+ BSP. The rest of the functions can reside in libcpu or libchip.
diff --git a/cpukit/libi2c/libi2c.c b/cpukit/libi2c/libi2c.c
new file mode 100644
index 0000000000..1f88968aab
--- /dev/null
+++ b/cpukit/libi2c/libi2c.c
@@ -0,0 +1,778 @@
+/* $Id$ */
+
+/* libi2c Implementation */
+
+/*
+ * Authorship
+ * ----------
+ * This software was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2005,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * This software was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+/*
+ * adaptations to also handle SPI devices
+ * by Thomas Doerfler, embedded brains GmbH, Puchheim, Germany
+ */
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <rtems/bspIo.h>
+#include <rtems/libio.h>
+
+#include <rtems/libi2c.h>
+
+#define DRVNM "libi2c: "
+
+#define MAX_NO_BUSSES 8 /* Also limited by the macro building minor numbers */
+#define MAX_NO_DRIVERS 16 /* Number of high level drivers we support */
+
+#define MINOR2ADDR(minor) ((minor)&((1<<10)-1))
+#define MINOR2BUS(minor) (((minor)>>10)&7)
+#define MINOR2DRV(minor) ((minor)>>13)
+
+/* Check the 'minor' argument, i.e., verify that
+ * we have a driver connected
+ */
+#define DECL_CHECKED_BH(b, bh, m, s)\
+ unsigned b = MINOR2BUS(m); \
+ rtems_libi2c_bus_t *bh; \
+ if ( b >= MAX_NO_BUSSES || 0 == (bh=busses[b].bush) ) { \
+ return s RTEMS_INVALID_NUMBER; \
+ }
+
+#define DECL_CHECKED_DRV(d, b, m) \
+ unsigned d = MINOR2DRV(m); \
+ unsigned b = MINOR2BUS(m); \
+ if ( b >= MAX_NO_BUSSES || 0 == busses[b].bush \
+ || d > MAX_NO_DRIVERS || (d && 0 == drvs[d-1].drv )) {\
+ return RTEMS_INVALID_NUMBER; \
+ }
+
+#define DISPATCH(rval, entry, dflt) \
+ do { \
+ const rtems_driver_address_table *ops = drvs[--drv].drv->ops; \
+ rval = ops->entry ? ops->entry(major,minor,arg) : dflt; \
+ } while (0)
+
+
+rtems_device_major_number rtems_libi2c_major;
+
+static bool is_initialized = false;
+
+static struct i2cbus
+{
+ rtems_libi2c_bus_t *bush;
+ volatile rtems_id mutex; /* lock this across start -> stop */
+ volatile bool started;
+ char *name;
+} busses[MAX_NO_BUSSES] = { { NULL, RTEMS_ID_NONE, false, NULL } };
+
+static struct
+{
+ rtems_libi2c_drv_t *drv;
+} drvs[MAX_NO_DRIVERS] = { { NULL } };
+
+static rtems_id libmutex = RTEMS_ID_NONE;
+
+#define LOCK(m) assert(!rtems_semaphore_obtain((m), RTEMS_WAIT, RTEMS_NO_TIMEOUT))
+#define UNLOCK(m) rtems_semaphore_release((m))
+
+#define LIBLOCK() LOCK(libmutex)
+#define LIBUNLOCK() UNLOCK(libmutex)
+
+#define MUTEX_ATTS \
+ ( RTEMS_PRIORITY \
+ | RTEMS_BINARY_SEMAPHORE \
+ |RTEMS_INHERIT_PRIORITY \
+ |RTEMS_NO_PRIORITY_CEILING \
+ |RTEMS_LOCAL )
+
+/* During early stages of life, stdio is not available */
+
+static void
+safe_printf (const char *fmt, ...)
+{
+va_list ap;
+
+ va_start(ap, fmt);
+ if ( _System_state_Is_up( _System_state_Get() ) )
+ vfprintf( stderr, fmt, ap );
+ else
+ vprintk( fmt, ap );
+ va_end(ap);
+}
+
+static rtems_status_code
+mutexCreate (rtems_name nm, rtems_id *pm)
+{
+ rtems_status_code sc;
+
+ if (RTEMS_SUCCESSFUL !=
+ (sc = rtems_semaphore_create (nm, 1, MUTEX_ATTS, 0, pm))) {
+ if ( _System_state_Is_up( _System_state_Get() ) )
+ rtems_error (sc, DRVNM "Unable to create mutex\n");
+ else
+ printk (DRVNM "Unable to create mutex (status code %i)\n", sc);
+ }
+ return sc;
+}
+
+/* Lock a bus avoiding to have a mutex, which is mostly
+ * unused, hanging around all the time. We just create
+ * and delete it on the fly...
+ *
+ * ASSUMES: argument checked by caller
+ */
+
+static void
+lock_bus (int busno)
+{
+ struct i2cbus *bus = &busses[busno];
+
+ if (bus->mutex == RTEMS_ID_NONE) {
+ /*
+ * Nobody is holding the bus mutex - it's not there. Create it on the fly.
+ */
+ LIBLOCK ();
+ if (bus->mutex == RTEMS_ID_NONE) {
+ rtems_id m = RTEMS_ID_NONE;
+ rtems_status_code sc = mutexCreate (
+ rtems_build_name ('i', '2', 'c', '0' + busno),
+ &m
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ LIBUNLOCK ();
+ rtems_panic (DRVNM "Unable to create bus lock");
+ return;
+ }
+ bus->mutex = m;
+ }
+ LIBUNLOCK ();
+ }
+
+ /* Now lock this bus */
+ LOCK (bus->mutex);
+}
+
+static void
+unlock_bus (int busno)
+{
+ struct i2cbus *bus = &busses[busno];
+ UNLOCK (bus->mutex);
+}
+
+/* Note that 'arg' is always passed in as NULL */
+rtems_status_code
+rtems_i2c_init (rtems_device_major_number major, rtems_device_minor_number minor,
+ void *arg)
+{
+ rtems_status_code rval;
+ /* No busses or drivers can be registered at this point;
+ * avoid the macro aborting with an error
+ DECL_CHECKED_DRV (drv, busno, minor)
+ */
+
+ rval = mutexCreate (rtems_build_name ('l', 'I', '2', 'C'), &libmutex);
+
+ if ( RTEMS_SUCCESSFUL == rval ) {
+ is_initialized = true;
+ rtems_libi2c_major = major;
+ } else {
+ libmutex = RTEMS_ID_NONE;
+ }
+ return rval;
+}
+
+rtems_status_code
+rtems_i2c_open (rtems_device_major_number major, rtems_device_minor_number minor,
+ void *arg)
+{
+ rtems_status_code rval;
+ DECL_CHECKED_DRV (drv, busno, minor)
+
+ if (0 == drv) {
+ rval = RTEMS_SUCCESSFUL;
+ } else {
+ DISPATCH (rval, open_entry, RTEMS_SUCCESSFUL);
+ }
+ return rval;
+}
+
+rtems_status_code
+rtems_i2c_close (rtems_device_major_number major, rtems_device_minor_number minor,
+ void *arg)
+{
+ rtems_status_code rval;
+ DECL_CHECKED_DRV (drv, busno, minor)
+
+ if (0 == drv) {
+ rval = RTEMS_SUCCESSFUL;
+ } else {
+ DISPATCH (rval, close_entry, RTEMS_SUCCESSFUL);
+ }
+ return rval;
+}
+
+rtems_status_code
+rtems_i2c_read (rtems_device_major_number major, rtems_device_minor_number minor,
+ void *arg)
+{
+ int rval; /* int so we can check for negative value */
+ rtems_libio_rw_args_t *rwargs = arg;
+ DECL_CHECKED_DRV (drv, busno, minor)
+
+ if (0 == rwargs->count) {
+ rwargs->bytes_moved = 0;
+ return RTEMS_SUCCESSFUL;
+ }
+
+ if (0 == drv) {
+ rval =
+ rtems_libi2c_start_read_bytes (minor, (unsigned char *) rwargs->buffer,
+ rwargs->count);
+ if (rval >= 0) {
+ rwargs->bytes_moved = rval;
+ rtems_libi2c_send_stop (minor);
+ rval = RTEMS_SUCCESSFUL;
+ } else {
+ rval = -rval;
+ }
+ } else {
+ DISPATCH (rval, read_entry, RTEMS_NOT_IMPLEMENTED);
+ }
+ return rval;
+}
+
+rtems_status_code
+rtems_i2c_write (rtems_device_major_number major, rtems_device_minor_number minor,
+ void *arg)
+{
+ int rval; /* int so we can check for negative value */
+ rtems_libio_rw_args_t *rwargs = arg;
+ DECL_CHECKED_DRV (drv, busno, minor)
+
+ if (0 == rwargs->count) {
+ rwargs->bytes_moved = 0;
+ return RTEMS_SUCCESSFUL;
+ }
+
+ if (0 == drv) {
+ rval =
+ rtems_libi2c_start_write_bytes (minor, (unsigned char *) rwargs->buffer,
+ rwargs->count);
+ if (rval >= 0) {
+ rwargs->bytes_moved = rval;
+ rtems_libi2c_send_stop (minor);
+ rval = RTEMS_SUCCESSFUL;
+ } else {
+ rval = -rval;
+ }
+ } else {
+ DISPATCH (rval, write_entry, RTEMS_NOT_IMPLEMENTED);
+ }
+ return rval;
+}
+
+rtems_status_code
+rtems_i2c_ioctl (rtems_device_major_number major, rtems_device_minor_number minor,
+ void *arg)
+{
+ rtems_status_code rval;
+ DECL_CHECKED_DRV (drv, busno, minor)
+
+ if (0 == drv) {
+ rval = RTEMS_NOT_IMPLEMENTED;
+ } else {
+ DISPATCH (rval, control_entry, RTEMS_NOT_IMPLEMENTED);
+ }
+ return rval;
+}
+
+
+/* Our ops just dispatch to the registered drivers */
+const rtems_driver_address_table rtems_libi2c_io_ops = {
+ initialization_entry: rtems_i2c_init,
+ open_entry: rtems_i2c_open,
+ close_entry: rtems_i2c_close,
+ read_entry: rtems_i2c_read,
+ write_entry: rtems_i2c_write,
+ control_entry: rtems_i2c_ioctl,
+};
+
+int
+rtems_libi2c_initialize (void)
+{
+ rtems_status_code sc;
+
+ if (is_initialized) {
+ /*
+ * already called before? then skip this step
+ */
+ return 0;
+ }
+
+ /* rtems_io_register_driver does NOT currently check nor report back
+ * the return code of the 'init' operation, so we cannot
+ * rely on return code since it may seem OK even if the driver 'init;
+ * op failed.
+ * Let 'init' handle 'is_initialized'...
+ */
+ sc = rtems_io_register_driver (0, &rtems_libi2c_io_ops, &rtems_libi2c_major);
+ if (RTEMS_SUCCESSFUL != sc) {
+ safe_printf(
+ DRVNM "Claiming driver slot failed (rtems status code %i)\n",
+ sc);
+ if (libmutex != RTEMS_ID_NONE) {
+ rtems_semaphore_delete (libmutex);
+ }
+ libmutex = RTEMS_ID_NONE;
+ is_initialized = false;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+rtems_libi2c_register_bus (const char *name, rtems_libi2c_bus_t * bus)
+{
+ int i;
+ rtems_status_code err;
+ size_t length = (name ? strlen (name) + 1 : 20);
+ char *nmcpy = malloc(length);
+ char tmp, *chpt;
+ struct stat sbuf;
+
+ if (nmcpy == NULL) {
+ safe_printf ( DRVNM "No memory\n");
+ return -RTEMS_NO_MEMORY;
+ }
+
+ strncpy (nmcpy, name ? name : "/dev/i2c", length);
+
+ /* check */
+ if ('/' != *nmcpy) {
+ safe_printf ( DRVNM "Bad name: must be an absolute path starting with '/'\n");
+ free( nmcpy );
+ return -RTEMS_INVALID_NAME;
+ }
+ /* file must not exist */
+ if (!stat (nmcpy, &sbuf)) {
+ safe_printf ( DRVNM "Bad name: file exists already\n");
+ free( nmcpy );
+ return -RTEMS_INVALID_NAME;
+ }
+
+ /* we already verified that there is at least one '/' */
+ chpt = strrchr (nmcpy, '/') + 1;
+ tmp = *chpt;
+ *chpt = 0;
+ i = stat (nmcpy, &sbuf);
+ *chpt = tmp;
+ if (i) {
+ safe_printf ( DRVNM "Get %s status failed: %s\n",
+ nmcpy, strerror(errno));
+ free( nmcpy );
+ return -RTEMS_INVALID_NAME;
+ }
+ /* should be a directory since name terminates in '/' */
+
+
+ if (libmutex == RTEMS_ID_NONE) {
+ safe_printf ( DRVNM "Library not initialized\n");
+ free( nmcpy );
+ return -RTEMS_NOT_DEFINED;
+ }
+
+ if (bus == NULL || bus->size < sizeof (*bus)) {
+ safe_printf ( DRVNM "No bus-ops or size too small -- misconfiguration?\n");
+ free( nmcpy );
+ return -RTEMS_NOT_CONFIGURED;
+ }
+
+ LIBLOCK ();
+ for (i = 0; i < MAX_NO_BUSSES; i++) {
+ if (!busses[i].bush) {
+ /* found a free slot */
+ busses[i].bush = bus;
+ busses[i].mutex = RTEMS_ID_NONE;
+ busses[i].started = false;
+
+ if (!name)
+ sprintf (nmcpy + strlen (nmcpy), "%i", i);
+
+ if ((err = busses[i].bush->ops->init (busses[i].bush))) {
+ /* initialization failed */
+ i = -err;
+ } else {
+ busses[i].name = nmcpy;;
+ nmcpy = 0;
+ }
+
+ break;
+ }
+ }
+ LIBUNLOCK ();
+
+ if (i >= MAX_NO_BUSSES) {
+ i = -RTEMS_TOO_MANY;
+ }
+
+ free (nmcpy);
+
+ return i;
+}
+
+static int
+not_started (int busno)
+{
+ int rval;
+ lock_bus (busno);
+ rval = !busses[busno].started;
+ unlock_bus (busno);
+ return rval;
+}
+
+rtems_status_code
+rtems_libi2c_send_start (rtems_device_minor_number minor)
+{
+ int rval;
+ DECL_CHECKED_BH (busno, bush, minor, +)
+
+ lock_bus (busno);
+ rval = bush->ops->send_start (bush);
+
+ /* if this failed or is not the first start, unlock */
+ if (rval || busses[busno].started) {
+ /* HMM - what to do if the 1st start failed ?
+ * try to reset...
+ */
+ if (!busses[busno].started) {
+ /* just in case the bus driver fiddles with errno */
+ int errno_saved = errno;
+ bush->ops->init (bush);
+ errno = errno_saved;
+ } else if (rval) {
+ /* failed restart */
+ rtems_libi2c_send_stop (minor);
+ }
+ unlock_bus (busno);
+ } else {
+ /* successful 1st start; keep bus locked until stop is sent */
+ busses[busno].started = true;
+ }
+ return rval;
+}
+
+rtems_status_code
+rtems_libi2c_send_stop (rtems_device_minor_number minor)
+{
+ rtems_status_code rval;
+ DECL_CHECKED_BH (busno, bush, minor, +)
+
+ if (not_started (busno))
+ return RTEMS_NOT_OWNER_OF_RESOURCE;
+
+ rval = bush->ops->send_stop (bush);
+
+ busses[busno].started = false;
+
+ unlock_bus (busno);
+ return rval;
+}
+
+rtems_status_code
+rtems_libi2c_send_addr (rtems_device_minor_number minor, int rw)
+{
+ rtems_status_code sc;
+ DECL_CHECKED_BH (busno, bush, minor, +)
+
+ if (not_started (busno))
+ return RTEMS_NOT_OWNER_OF_RESOURCE;
+
+ sc = bush->ops->send_addr (bush, MINOR2ADDR (minor), rw);
+ if (RTEMS_SUCCESSFUL != sc)
+ rtems_libi2c_send_stop (minor);
+ return sc;
+}
+
+int
+rtems_libi2c_read_bytes (rtems_device_minor_number minor,
+ unsigned char *bytes,
+ int nbytes)
+{
+ int sc;
+ DECL_CHECKED_BH (busno, bush, minor, -)
+
+ if (not_started (busno))
+ return -RTEMS_NOT_OWNER_OF_RESOURCE;
+
+ sc = bush->ops->read_bytes (bush, bytes, nbytes);
+ if (sc < 0)
+ rtems_libi2c_send_stop (minor);
+ return sc;
+}
+
+int
+rtems_libi2c_write_bytes (rtems_device_minor_number minor,
+ const unsigned char *bytes,
+ int nbytes)
+{
+ int sc;
+ DECL_CHECKED_BH (busno, bush, minor, -)
+
+ if (not_started (busno))
+ return -RTEMS_NOT_OWNER_OF_RESOURCE;
+
+ sc = bush->ops->write_bytes (bush, (unsigned char *)bytes, nbytes);
+ if (sc < 0)
+ rtems_libi2c_send_stop (minor);
+ return sc;
+}
+
+int
+rtems_libi2c_ioctl (rtems_device_minor_number minor,
+ int cmd,
+ ...)
+{
+ va_list ap;
+ int sc = 0;
+ void *args;
+ bool is_started = false;
+ DECL_CHECKED_BH (busno, bush, minor, -)
+
+ va_start(ap, cmd);
+ args = va_arg(ap, void *);
+
+ switch(cmd) {
+ /*
+ * add ioctls defined for this level here:
+ */
+
+ case RTEMS_LIBI2C_IOCTL_GET_DRV_T:
+ /*
+ * query driver table entry
+ */
+ *(rtems_libi2c_drv_t **)args = (drvs[MINOR2DRV(minor)-1].drv);
+ break;
+
+ case RTEMS_LIBI2C_IOCTL_START_TFM_READ_WRITE:
+ if (not_started (busno)) {
+ va_end(ap);
+ return -RTEMS_NOT_OWNER_OF_RESOURCE;
+ }
+
+ /*
+ * address device, then set transfer mode and perform read_write transfer
+ */
+ /*
+ * perform start/address
+ */
+ if (sc == 0) {
+ sc = rtems_libi2c_send_start (minor);
+ is_started = (sc == 0);
+ }
+ /*
+ * set tfr mode
+ */
+ if (sc == 0) {
+ sc = bush->ops->ioctl
+ (bush,
+ RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
+ &((rtems_libi2c_tfm_read_write_t *)args)->tfr_mode);
+ }
+ /*
+ * perform read_write
+ */
+ if (sc == 0) {
+ sc = bush->ops->ioctl
+ (bush,
+ RTEMS_LIBI2C_IOCTL_READ_WRITE,
+ &((rtems_libi2c_tfm_read_write_t *)args)->rd_wr);
+ }
+ if ((sc < 0) && (is_started)) {
+ rtems_libi2c_send_stop (minor);
+ }
+ break;
+ default:
+ sc = bush->ops->ioctl (bush, cmd, args);
+ break;
+ }
+ va_end(ap);
+ return sc;
+}
+
+static int
+do_s_rw (rtems_device_minor_number minor,
+ unsigned char *bytes,
+ int nbytes,
+ int rw)
+{
+ rtems_status_code sc;
+ rtems_libi2c_bus_t *bush;
+ int status;
+
+ if ((sc = rtems_libi2c_send_start (minor)))
+ return -sc;
+
+ /* at this point, we hold the bus and are sure the minor number is valid */
+ bush = busses[MINOR2BUS (minor)].bush;
+
+ if ((sc = bush->ops->send_addr (bush, MINOR2ADDR (minor), rw))) {
+ rtems_libi2c_send_stop (minor);
+ return -sc;
+ }
+
+ if (rw)
+ status = bush->ops->read_bytes (bush, bytes, nbytes);
+ else
+ status = bush->ops->write_bytes (bush, bytes, nbytes);
+
+ if (status < 0) {
+ rtems_libi2c_send_stop (minor);
+ }
+ return status;
+}
+
+int
+rtems_libi2c_start_read_bytes (rtems_device_minor_number minor,
+ unsigned char *bytes,
+ int nbytes)
+{
+ return do_s_rw (minor, bytes, nbytes, 1);
+}
+
+int
+rtems_libi2c_start_write_bytes (rtems_device_minor_number minor,
+ const unsigned char *bytes,
+ int nbytes)
+{
+ return do_s_rw (minor, (unsigned char *)bytes, nbytes, 0);
+}
+
+int
+rtems_libi2c_register_drv (const char *name, rtems_libi2c_drv_t * drvtbl,
+ unsigned busno, unsigned i2caddr)
+{
+ int i;
+ rtems_status_code err;
+ rtems_device_minor_number minor;
+
+ if (libmutex == RTEMS_ID_NONE) {
+ safe_printf ( DRVNM "Library not initialized\n");
+ return -RTEMS_NOT_DEFINED;
+ }
+
+ if (name && strchr (name, '/')) {
+ safe_printf ( DRVNM "Invalid name: '%s' -- must not contain '/'\n", name);
+ return -RTEMS_INVALID_NAME;
+ }
+
+ if (busno >= MAX_NO_BUSSES || !busses[busno].bush || i2caddr >= 1 << 10) {
+ errno = EINVAL;
+ return -RTEMS_INVALID_NUMBER;
+ }
+
+ if (drvtbl == NULL || drvtbl->size < sizeof (*drvtbl)) {
+ safe_printf ( DRVNM "No driver table or size too small -- misconfiguration?\n");
+ return -RTEMS_NOT_CONFIGURED;
+ }
+
+ /* allocate slot */
+ LIBLOCK ();
+ for (i = 0; i < MAX_NO_DRIVERS; i++) {
+ /* driver # 0 is special, it is the built-in raw driver */
+ if (!drvs[i].drv) {
+ char *str;
+ dev_t dev;
+ uint32_t mode;
+
+ /* found a free slot; encode slot + 1 ! */
+ minor = ((i + 1) << 13) | RTEMS_LIBI2C_MAKE_MINOR (busno, i2caddr);
+
+ if (name) {
+ size_t length = strlen (busses[busno].name) + strlen (name) + 2;
+ str = malloc (length);
+ snprintf (str, length, "%s.%s", busses[busno].name, name);
+
+ dev = rtems_filesystem_make_dev_t (rtems_libi2c_major, minor);
+
+ mode = 0111 | S_IFCHR;
+ if (drvtbl->ops->read_entry)
+ mode |= 0444;
+ if (drvtbl->ops->write_entry)
+ mode |= 0222;
+
+ /* note that 'umask' is applied to 'mode' */
+ if (mknod (str, mode, dev)) {
+ safe_printf( DRVNM
+ "Creating device node failed: %s; you can try to do it manually...\n",
+ strerror (errno));
+ }
+
+ free (str);
+ }
+
+ drvs[i].drv = drvtbl;
+
+ if (drvtbl->ops->initialization_entry)
+ err =
+ drvs[i].drv->ops->initialization_entry (rtems_libi2c_major, minor,
+ 0);
+ else
+ err = RTEMS_SUCCESSFUL;
+
+ LIBUNLOCK ();
+ return err ? -err : minor;
+ }
+ }
+ LIBUNLOCK ();
+ return -RTEMS_TOO_MANY;
+}
diff --git a/cpukit/libi2c/libi2c.h b/cpukit/libi2c/libi2c.h
new file mode 100644
index 0000000000..89edcb95d1
--- /dev/null
+++ b/cpukit/libi2c/libi2c.h
@@ -0,0 +1,510 @@
+/**
+ * @file
+ *
+ * @ingroup libi2c
+ *
+ * @brief I2C library.
+ */
+
+#ifndef _RTEMS_LIBI2C_H
+#define _RTEMS_LIBI2C_H
+/*$Id$*/
+
+/*
+ * Authorship
+ * ----------
+ * This software was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2005,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * This software was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+#include <rtems.h>
+
+#include <rtems/io.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup libi2c I2C Library
+ *
+ * @brief I2C library.
+ *
+ * @{
+ */
+
+/* Simple I2C driver API */
+
+/* Initialize the libary - may fail if no semaphore or no driver slot is available */
+extern int rtems_libi2c_initialize (void);
+
+/* Alternatively to rtems_libi2c_initialize() the library can also be
+ * initialized by means of a traditional driver table entry containing
+ * the following entry points:
+ */
+extern rtems_status_code
+rtems_i2c_init (
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg);
+
+extern rtems_status_code
+rtems_i2c_open (
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg);
+
+extern rtems_status_code
+rtems_i2c_close (
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg);
+
+extern rtems_status_code
+rtems_i2c_read (
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg);
+
+extern rtems_status_code
+rtems_i2c_write (
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg);
+
+extern rtems_status_code
+rtems_i2c_ioctl (
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg);
+
+extern const rtems_driver_address_table rtems_libi2c_io_ops;
+
+/* Unfortunately, if you want to add this driver to
+ * a RTEMS configuration table then you need all the
+ * members explicitly :-( (Device_driver_table should
+ * hold pointers to rtems_driver_address_tables rather
+ * than full structs).
+ *
+ * The difficulty is that adding this driver to the
+ * configuration table is not enough; you still need
+ * to populate the framework with low-level bus-driver(s)
+ * and high-level drivers and/or device-files...
+ *
+ * Currently the preferred way is having the BSP
+ * call 'rtems_libi2c_initialize' followed by
+ * 'rtems_libi2c_register_bus' and
+ * 'rtems_libi2c_register_drv' and/or
+ * 'mknod' (for 'raw' device nodes)
+ * from the 'pretasking_hook'.
+ */
+#define RTEMS_LIBI2C_DRIVER_TABLE_ENTRY \
+{ \
+ initialization_entry: rtems_i2c_init, \
+ open_entry: rtems_i2c_open, \
+ close_entry: rtems_i2c_close, \
+ read_entry: rtems_i2c_read, \
+ write_entry: rtems_i2c_write, \
+ control_entry: rtems_i2c_ioctl, \
+}
+
+/* Bus Driver API
+ *
+ * Bus drivers provide access to low-level i2c functions
+ * such as 'send start', 'send address', 'get bytes' etc.
+ */
+
+/* first field must be a pointer to ops; driver
+ * may add its own fields after this.
+ * the struct that is registered with the library
+ * is not copied; a pointer will we passed
+ * to the callback functions (ops).
+ */
+typedef struct rtems_libi2c_bus_t_
+{
+ const struct rtems_libi2c_bus_ops_ *ops;
+ int size; /* size of whole structure */
+} rtems_libi2c_bus_t;
+
+/* Access functions a low level driver must provide;
+ *
+ * All of these, except read_bytes and write_bytes
+ * return RTEMS_SUCCESSFUL on success and an error status
+ * otherwise. The read and write ops return the number
+ * of chars read/written or -(status code) on error.
+ */
+typedef struct rtems_libi2c_bus_ops_
+{
+ /* Initialize the bus; might be called again to reset the bus driver */
+ rtems_status_code (*init) (rtems_libi2c_bus_t * bushdl);
+ /* Send start condition */
+ rtems_status_code (*send_start) (rtems_libi2c_bus_t * bushdl);
+ /* Send stop condition */
+ rtems_status_code (*send_stop) (rtems_libi2c_bus_t * bushdl);
+ /* initiate transfer from (rw!=0) or to a device */
+ rtems_status_code (*send_addr) (rtems_libi2c_bus_t * bushdl,
+ uint32_t addr, int rw);
+ /* read a number of bytes */
+ int (*read_bytes) (rtems_libi2c_bus_t * bushdl, unsigned char *bytes,
+ int nbytes);
+ /* write a number of bytes */
+ int (*write_bytes) (rtems_libi2c_bus_t * bushdl, unsigned char *bytes,
+ int nbytes);
+ /* ioctl misc functions */
+ int (*ioctl) (rtems_libi2c_bus_t * bushdl,
+ int cmd,
+ void *buffer
+ );
+} rtems_libi2c_bus_ops_t;
+
+
+/*
+ * Register a lowlevel driver
+ *
+ * TODO: better description
+ *
+ * This allocates a major number identifying *this* driver
+ * (i.e., libi2c) and the minor number encodes a bus# and a i2c address.
+ *
+ * The name will be registered in the filesystem (parent
+ * directories must exist, also IMFS filesystem must exist see
+ * CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM). It may be NULL in which case
+ * the library will pick a default.
+ *
+ * RETURNS: bus # (>=0) or -1 on error (errno set).
+ */
+
+extern int rtems_libi2c_register_bus (const char *name, rtems_libi2c_bus_t * bus);
+
+extern rtems_device_major_number rtems_libi2c_major;
+
+#define RTEMS_LIBI2C_MAKE_MINOR(busno, i2caddr) \
+ ((((busno)&((1<<3)-1))<<10) | ((i2caddr)&((1<<10)-1)))
+
+/* After the library is initialized, a major number is available.
+ * As soon as a low-level bus driver is registered (above routine
+ * returns a 'busno'), a device node can be created in the filesystem
+ * with a major/minor number pair of
+ *
+ * rtems_libi2c_major / RTEMS_LIBI2C_MAKE_MINOR(busno, i2caddr)
+ *
+ * and a 'raw' hi-level driver is then attached to this device
+ * node.
+ * This 'raw' driver has very simple semantics:
+ *
+ * 'open' sends a start condition
+ * 'read'/'write' address the device identified by the i2c bus# and address
+ * encoded in the minor number and read or write, respectively
+ * a stream of bytes from or to the device. Every time the
+ * direction is changed, a 're-start' condition followed by
+ * an 'address' cycle is generated on the i2c bus.
+ * 'close' sends a stop condition.
+ *
+ * Hence, using the 'raw' driver, e.g., 100 bytes at offset 0x200 can be
+ * read from an EEPROM by the following pseudo-code:
+ *
+ * mknod("/dev/i2c-54", mode, MKDEV(rtems_libi2c_major, RTEMS_LIBI2C_MAKE_MINOR(0,0x54)))
+ *
+ * int fd;
+ * char off[2]={0x02,0x00};
+ *
+ * fd = open("/dev/i2c-54",O_RDWR);
+ * write(fd,off,2);
+ * read(fd,buf,100);
+ * close(fd);
+ *
+ */
+
+/* Higher Level Driver API
+ *
+ * Higher level drivers know how to deal with specific i2c
+ * devices (independent of the bus interface chip) and provide
+ * an abstraction, i.e., the usual read/write/ioctl access.
+ *
+ * Using the above example, such a high level driver could
+ * prevent the user from issuing potentially destructive write
+ * operations (the aforementioned EEPROM interprets any 3rd
+ * and following byte written to the device as data, i.e., the
+ * contents could easily be changed!).
+ * The correct 'read-pointer offset' programming could be
+ * implemented in 'open' and 'ioctl' of a high-level driver and
+ * the user would then only have to perform harmless read
+ * operations, e.g.,
+ *
+ * fd = open("/dev/i2c.eeprom",O_RDONLY) / * opens and sets EEPROM read pointer * /
+ * ioctl(fd, IOCTL_SEEK, 0x200) / * repositions the read pointer * /
+ * read(fd, buf, 100)
+ * close(fd)
+ *
+ */
+
+/* struct provided at driver registration. The driver may store
+ * private data behind the mandatory first fields but the size
+ * must be set to the size of the entire struct, e.g.,
+ *
+ * struct driver_pvt {
+ * rtems_libi2c_drv_t pub;
+ * struct { ... } pvt;
+ * } my_driver = {
+ * { ops: my_ops,
+ * size: sizeof(my_driver)
+ * },
+ * { ...};
+ * };
+ *
+ * A pointer to this struct is passed to the callback ops.
+ */
+
+typedef struct rtems_libi2c_drv_t_
+{
+ const rtems_driver_address_table *ops; /* the driver ops */
+ int size; /* size of whole structure (including appended private data) */
+} rtems_libi2c_drv_t;
+
+/*
+ * The high level driver must be registered with a particular
+ * bus number and i2c address.
+ *
+ * The registration procedure also creates a filesystem node,
+ * i.e., the returned minor number is not really needed.
+ *
+ * If the 'name' argument is NULL, no filesystem node is
+ * created (but this can be done 'manually' using rtems_libi2c_major
+ * and the return value of this routine).
+ *
+ * RETURNS minor number (FYI) or -1 on failure
+ */
+extern int
+rtems_libi2c_register_drv (const char *name, rtems_libi2c_drv_t * drvtbl,
+ unsigned bus, unsigned i2caddr);
+
+/* Operations available to high level drivers */
+
+/* NOTES: The bus a device is attached to is LOCKED from the first send_start
+ * until send_stop is executed!
+ *
+ * Bus tenure MUST NOT span multiple system calls - otherwise, a single
+ * thread could get into the protected sections (or would deadlock if the
+ * mutex was not nestable).
+ * E.g., consider what happens if 'open' sends a 'start' and 'close'
+ * sends a 'stop' (i.e., the bus mutex would be locked in 'open' and
+ * released in 'close'. A single thread could try to open two devices
+ * on the same bus and would either deadlock or nest into the bus mutex
+ * and potentially mess up the i2c messages.
+ *
+ * The correct way is to *always* relinquish the i2c bus (i.e., send 'stop'
+ * from any driver routine prior to returning control to the caller.
+ * Consult the implementation of the generic driver routines (open, close, ...)
+ * below or the examples in i2c-2b-eeprom.c and i2c-2b-ds1621.c
+ *
+ * Drivers just pass the minor number on to these routines...
+ */
+extern rtems_status_code rtems_libi2c_send_start (rtems_device_minor_number minor);
+
+extern rtems_status_code rtems_libi2c_send_stop (rtems_device_minor_number minor);
+
+extern rtems_status_code
+rtems_libi2c_send_addr (rtems_device_minor_number minor, int rw);
+
+/* the read/write routines return the number of bytes transferred
+ * or -(status_code) on error.
+ */
+extern int
+rtems_libi2c_read_bytes (rtems_device_minor_number minor,
+ unsigned char *bytes, int nbytes);
+
+extern int
+rtems_libi2c_write_bytes (rtems_device_minor_number minor,
+ const unsigned char *bytes, int nbytes);
+
+/* Send start, send address and read bytes */
+extern int
+rtems_libi2c_start_read_bytes (rtems_device_minor_number minor,
+ unsigned char *bytes,
+ int nbytes);
+
+/* Send start, send address and write bytes */
+extern int
+rtems_libi2c_start_write_bytes (rtems_device_minor_number minor,
+ const unsigned char *bytes,
+ int nbytes);
+
+
+/* call misc iocontrol function */
+extern int
+rtems_libi2c_ioctl (rtems_device_minor_number minor,
+ int cmd,
+ ...);
+/*
+ * NOTE: any low-level driver ioctl returning a negative
+ * result for release the bus (perform a STOP condition)
+ */
+/*******************************
+ * defined IOCTLs:
+ *******************************/
+#define RTEMS_LIBI2C_IOCTL_READ_WRITE 1
+/*
+ * retval = rtems_libi2c_ioctl(rtems_device_minor_number minor,
+ * RTEMS_LIBI2C_IOCTL_READ_WRITE,
+ * rtems_libi2c_read_write_t *arg);
+ *
+ * This call performs a simultanous read/write transfer,
+ * which is possible (and sometimes needed) for SPI devices
+ *
+ * arg is a pointer to a rd_wr info data structure
+ *
+ * This call is only needed for SPI devices
+ */
+#define RTEMS_LIBI2C_IOCTL_START_TFM_READ_WRITE 2
+/*
+ * retval = rtems_libi2c_ioctl(rtems_device_minor_number minor,
+ * RTEMS_LIBI2C_IOCTL_START_READ_WRITE,
+ * unsigned char *rd_buffer,
+ * const unsigned char *wr_buffer,
+ * int byte_cnt,
+ * const rtems_libi2c_tfr_mode_t *tfr_mode_ptr);
+ *
+ * This call addresses a slave and then:
+ * - sets the proper transfer mode,
+ * - performs a simultanous read/write transfer,
+ * (which is possible and sometimes needed for SPI devices)
+ * NOTE: - if rd_buffer is NULL, receive data will be dropped
+ * - if wr_buffer is NULL, bytes with content 0 will transmitted
+ *
+ * rd_buffer is a pointer to a receive buffer (or NULL)
+ * wr_buffer is a pointer to the data to be sent (or NULL)
+ *
+ * This call is only needed for SPI devices
+ */
+
+#define RTEMS_LIBI2C_IOCTL_SET_TFRMODE 3
+/*
+ * retval = rtems_libi2c_ioctl(rtems_device_minor_number minor,
+ * RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
+ * const rtems_libi2c_tfr_mode_t *tfr_mode_ptr);
+ *
+ * This call sets an SPI device to the transfer mode needed (baudrate etc.)
+ *
+ * tfr_mode is a pointer to a structure defining the SPI transfer mode needed
+ * (see below).
+ *
+ * This call is only needed for SPI devices
+ */
+
+#define RTEMS_LIBI2C_IOCTL_GET_DRV_T 4
+
+/*
+ * retval = rtems_libi2c_ioctl(rtems_device_minor_number minor,
+ * RTEMS_LIBI2C_IOCTL_GET_DRV_T,
+ * const rtems_libi2c_drv_t *drv_t_ptr);
+ *
+ * This call allows the a high-level driver to query its driver table entry,
+ * including its private data appended to it during creation of the entry
+ *
+ */
+
+/**
+ * @brief IO control command for asynchronous read and write.
+ *
+ * @see rtems_libi2c_read_write_done_t and rtems_libi2c_read_write_async_t.
+ *
+ * @warning This is work in progress!
+ */
+#define RTEMS_LIBI2C_IOCTL_READ_WRITE_ASYNC 5
+
+/*
+ * argument data structures for IOCTLs defined above
+ */
+typedef struct {
+ unsigned char *rd_buf;
+ const unsigned char *wr_buf;
+ int byte_cnt;
+} rtems_libi2c_read_write_t;
+
+typedef struct {
+ uint32_t baudrate; /* maximum bits per second */
+ /* only valid for SPI drivers: */
+ uint8_t bits_per_char; /* how many bits per byte/word/longword? */
+ bool lsb_first; /* true: send LSB first */
+ bool clock_inv; /* true: inverted clock (high active) */
+ bool clock_phs; /* true: clock starts toggling at start of data tfr */
+ uint32_t idle_char; /* This character will be continuously transmitted in read only functions */
+} rtems_libi2c_tfr_mode_t;
+
+typedef struct {
+ rtems_libi2c_tfr_mode_t tfr_mode;
+ rtems_libi2c_read_write_t rd_wr;
+} rtems_libi2c_tfm_read_write_t;
+
+/**
+ * @brief Notification function type for asynchronous read and write.
+ *
+ * @see RTEMS_LIBI2C_IOCTL_READ_WRITE_ASYNC and
+ * rtems_libi2c_read_write_async_t.
+ *
+ * @warning This is work in progress!
+ */
+typedef void (*rtems_libi2c_read_write_done_t) \
+ ( int /* return value */, int /* nbytes */, void * /* arg */);
+
+/**
+ * @brief IO command data for asynchronous read and write.
+ *
+ * @see RTEMS_LIBI2C_IOCTL_READ_WRITE_ASYNC and
+ * rtems_libi2c_read_write_done_t.
+ *
+ * @warning This is work in progress!
+ */
+typedef struct {
+ unsigned char *rd_buf;
+ const unsigned char *wr_buf;
+ int byte_cnt;
+ rtems_libi2c_read_write_done_t done;
+ void *arg;
+} rtems_libi2c_read_write_async_t;
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif