diff options
Diffstat (limited to 'cpukit/libi2c')
-rw-r--r-- | cpukit/libi2c/.cvsignore | 2 | ||||
-rw-r--r-- | cpukit/libi2c/Makefile.am | 11 | ||||
-rw-r--r-- | cpukit/libi2c/README_libi2c | 367 | ||||
-rw-r--r-- | cpukit/libi2c/libi2c.c | 778 | ||||
-rw-r--r-- | cpukit/libi2c/libi2c.h | 510 |
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 |