aboutsummaryrefslogtreecommitdiff
path: root/src/drivers/device
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/device')
-rw-r--r--src/drivers/device/cdev.cpp398
-rw-r--r--src/drivers/device/device.cpp227
-rw-r--r--src/drivers/device/device.h447
-rw-r--r--src/drivers/device/i2c.cpp204
-rw-r--r--src/drivers/device/i2c.h138
-rw-r--r--src/drivers/device/module.mk42
-rw-r--r--src/drivers/device/pio.cpp74
-rw-r--r--src/drivers/device/spi.cpp160
-rw-r--r--src/drivers/device/spi.h114
9 files changed, 1804 insertions, 0 deletions
diff --git a/src/drivers/device/cdev.cpp b/src/drivers/device/cdev.cpp
new file mode 100644
index 000000000..422321850
--- /dev/null
+++ b/src/drivers/device/cdev.cpp
@@ -0,0 +1,398 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file cdev.cpp
+ *
+ * Character device base class.
+ */
+
+#include "device.h"
+
+#include <sys/ioctl.h>
+#include <arch/irq.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef CONFIG_DISABLE_POLL
+# error This driver is not compatible with CONFIG_DISABLE_POLL
+#endif
+
+namespace device
+{
+
+/* how much to grow the poll waiter set each time it has to be increased */
+static const unsigned pollset_increment = 0;
+
+/*
+ * The standard NuttX operation dispatch table can't call C++ member functions
+ * directly, so we have to bounce them through this dispatch table.
+ */
+static int cdev_open(struct file *filp);
+static int cdev_close(struct file *filp);
+static ssize_t cdev_read(struct file *filp, char *buffer, size_t buflen);
+static ssize_t cdev_write(struct file *filp, const char *buffer, size_t buflen);
+static off_t cdev_seek(struct file *filp, off_t offset, int whence);
+static int cdev_ioctl(struct file *filp, int cmd, unsigned long arg);
+static int cdev_poll(struct file *filp, struct pollfd *fds, bool setup);
+
+/**
+ * Character device indirection table.
+ *
+ * Every cdev we register gets the same function table; we use the private data
+ * field in the inode to store the instance pointer.
+ *
+ * Note that we use the GNU extension syntax here because we don't get designated
+ * initialisers in gcc 4.6.
+ */
+const struct file_operations CDev::fops = {
+open : cdev_open,
+close : cdev_close,
+read : cdev_read,
+write : cdev_write,
+seek : cdev_seek,
+ioctl : cdev_ioctl,
+poll : cdev_poll,
+};
+
+CDev::CDev(const char *name,
+ const char *devname,
+ int irq) :
+ // base class
+ Device(name, irq),
+ // public
+ // protected
+ // private
+ _devname(devname),
+ _registered(false),
+ _open_count(0)
+{
+ for (unsigned i = 0; i < _max_pollwaiters; i++)
+ _pollset[i] = nullptr;
+}
+
+CDev::~CDev()
+{
+ if (_registered)
+ unregister_driver(_devname);
+}
+
+int
+CDev::init()
+{
+ int ret = OK;
+
+ // base class init first
+ ret = Device::init();
+
+ if (ret != OK)
+ goto out;
+
+ // now register the driver
+ ret = register_driver(_devname, &fops, 0666, (void *)this);
+
+ if (ret != OK)
+ goto out;
+
+ _registered = true;
+
+out:
+ return ret;
+}
+
+/*
+ * Default implementations of the character device interface
+ */
+int
+CDev::open(struct file *filp)
+{
+ int ret = OK;
+
+ lock();
+ /* increment the open count */
+ _open_count++;
+
+ if (_open_count == 1) {
+
+ /* first-open callback may decline the open */
+ ret = open_first(filp);
+
+ if (ret != OK)
+ _open_count--;
+ }
+
+ unlock();
+
+ return ret;
+}
+
+int
+CDev::open_first(struct file *filp)
+{
+ return OK;
+}
+
+int
+CDev::close(struct file *filp)
+{
+ int ret = OK;
+
+ lock();
+
+ if (_open_count > 0) {
+ /* decrement the open count */
+ _open_count--;
+
+ /* callback cannot decline the close */
+ if (_open_count == 0)
+ ret = close_last(filp);
+
+ } else {
+ ret = -EBADF;
+ }
+
+ unlock();
+
+ return ret;
+}
+
+int
+CDev::close_last(struct file *filp)
+{
+ return OK;
+}
+
+ssize_t
+CDev::read(struct file *filp, char *buffer, size_t buflen)
+{
+ return -ENOSYS;
+}
+
+ssize_t
+CDev::write(struct file *filp, const char *buffer, size_t buflen)
+{
+ return -ENOSYS;
+}
+
+off_t
+CDev::seek(struct file *filp, off_t offset, int whence)
+{
+ return -ENOSYS;
+}
+
+int
+CDev::ioctl(struct file *filp, int cmd, unsigned long arg)
+{
+ switch (cmd) {
+
+ /* fetch a pointer to the driver's private data */
+ case DIOC_GETPRIV:
+ *(void **)(uintptr_t)arg = (void *)this;
+ return OK;
+ }
+
+ return -ENOTTY;
+}
+
+int
+CDev::poll(struct file *filp, struct pollfd *fds, bool setup)
+{
+ int ret = OK;
+
+ /*
+ * Lock against pollnotify() (and possibly other callers)
+ */
+ lock();
+
+ if (setup) {
+ /*
+ * Save the file pointer in the pollfd for the subclass'
+ * benefit.
+ */
+ fds->priv = (void *)filp;
+
+ /*
+ * Handle setup requests.
+ */
+ ret = store_poll_waiter(fds);
+
+ if (ret == OK) {
+
+ /*
+ * Check to see whether we should send a poll notification
+ * immediately.
+ */
+ fds->revents |= fds->events & poll_state(filp);
+
+ /* yes? post the notification */
+ if (fds->revents != 0)
+ sem_post(fds->sem);
+ }
+
+ } else {
+ /*
+ * Handle a teardown request.
+ */
+ ret = remove_poll_waiter(fds);
+ }
+
+ unlock();
+
+ return ret;
+}
+
+void
+CDev::poll_notify(pollevent_t events)
+{
+ /* lock against poll() as well as other wakeups */
+ irqstate_t state = irqsave();
+
+ for (unsigned i = 0; i < _max_pollwaiters; i++)
+ if (nullptr != _pollset[i])
+ poll_notify_one(_pollset[i], events);
+
+ irqrestore(state);
+}
+
+void
+CDev::poll_notify_one(struct pollfd *fds, pollevent_t events)
+{
+ /* update the reported event set */
+ fds->revents |= fds->events & events;
+
+ /* if the state is now interesting, wake the waiter if it's still asleep */
+ /* XXX semcount check here is a vile hack; counting semphores should not be abused as cvars */
+ if ((fds->revents != 0) && (fds->sem->semcount <= 0))
+ sem_post(fds->sem);
+}
+
+pollevent_t
+CDev::poll_state(struct file *filp)
+{
+ /* by default, no poll events to report */
+ return 0;
+}
+
+int
+CDev::store_poll_waiter(struct pollfd *fds)
+{
+ /*
+ * Look for a free slot.
+ */
+ for (unsigned i = 0; i < _max_pollwaiters; i++) {
+ if (nullptr == _pollset[i]) {
+
+ /* save the pollfd */
+ _pollset[i] = fds;
+
+ return OK;
+ }
+ }
+
+ return ENOMEM;
+}
+
+int
+CDev::remove_poll_waiter(struct pollfd *fds)
+{
+ for (unsigned i = 0; i < _max_pollwaiters; i++) {
+ if (fds == _pollset[i]) {
+
+ _pollset[i] = nullptr;
+ return OK;
+
+ }
+ }
+
+ puts("poll: bad fd state");
+ return -EINVAL;
+}
+
+static int
+cdev_open(struct file *filp)
+{
+ CDev *cdev = (CDev *)(filp->f_inode->i_private);
+
+ return cdev->open(filp);
+}
+
+static int
+cdev_close(struct file *filp)
+{
+ CDev *cdev = (CDev *)(filp->f_inode->i_private);
+
+ return cdev->close(filp);
+}
+
+static ssize_t
+cdev_read(struct file *filp, char *buffer, size_t buflen)
+{
+ CDev *cdev = (CDev *)(filp->f_inode->i_private);
+
+ return cdev->read(filp, buffer, buflen);
+}
+
+static ssize_t
+cdev_write(struct file *filp, const char *buffer, size_t buflen)
+{
+ CDev *cdev = (CDev *)(filp->f_inode->i_private);
+
+ return cdev->write(filp, buffer, buflen);
+}
+
+static off_t
+cdev_seek(struct file *filp, off_t offset, int whence)
+{
+ CDev *cdev = (CDev *)(filp->f_inode->i_private);
+
+ return cdev->seek(filp, offset, whence);
+}
+
+static int
+cdev_ioctl(struct file *filp, int cmd, unsigned long arg)
+{
+ CDev *cdev = (CDev *)(filp->f_inode->i_private);
+
+ return cdev->ioctl(filp, cmd, arg);
+}
+
+static int
+cdev_poll(struct file *filp, struct pollfd *fds, bool setup)
+{
+ CDev *cdev = (CDev *)(filp->f_inode->i_private);
+
+ return cdev->poll(filp, fds, setup);
+}
+
+} // namespace device \ No newline at end of file
diff --git a/src/drivers/device/device.cpp b/src/drivers/device/device.cpp
new file mode 100644
index 000000000..04a5222c3
--- /dev/null
+++ b/src/drivers/device/device.cpp
@@ -0,0 +1,227 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file device.cpp
+ *
+ * Fundamental driver base class for the device framework.
+ */
+
+#include "device.h"
+
+#include <nuttx/arch.h>
+#include <stdio.h>
+#include <unistd.h>
+
+namespace device
+{
+
+/**
+ * Interrupt dispatch table entry.
+ */
+struct irq_entry {
+ int irq;
+ Device *owner;
+};
+
+static const unsigned irq_nentries = 8; /**< size of the interrupt dispatch table */
+static irq_entry irq_entries[irq_nentries]; /**< interrupt dispatch table (XXX should be a vector) */
+
+/**
+ * Register an interrupt to a specific device.
+ *
+ * @param irq The interrupt number to register.
+ * @param owner The device receiving the interrupt.
+ * @return OK if the interrupt was registered.
+ */
+static int register_interrupt(int irq, Device *owner);
+
+/**
+ * Unregister an interrupt.
+ *
+ * @param irq The previously-registered interrupt to be de-registered.
+ */
+static void unregister_interrupt(int irq);
+
+/**
+ * Handle an interrupt.
+ *
+ * @param irq The interrupt being invoked.
+ * @param context The interrupt register context.
+ * @return Always returns OK.
+ */
+static int interrupt(int irq, void *context);
+
+Device::Device(const char *name,
+ int irq) :
+ // public
+ // protected
+ _name(name),
+ _debug_enabled(false),
+ // private
+ _irq(irq),
+ _irq_attached(false)
+{
+ sem_init(&_lock, 0, 1);
+}
+
+Device::~Device()
+{
+ sem_destroy(&_lock);
+
+ if (_irq_attached)
+ unregister_interrupt(_irq);
+}
+
+int
+Device::init()
+{
+ int ret = OK;
+
+ // If assigned an interrupt, connect it
+ if (_irq) {
+ /* ensure it's disabled */
+ up_disable_irq(_irq);
+
+ /* register */
+ ret = register_interrupt(_irq, this);
+
+ if (ret != OK)
+ goto out;
+
+ _irq_attached = true;
+ }
+
+out:
+ return ret;
+}
+
+void
+Device::interrupt_enable()
+{
+ if (_irq_attached)
+ up_enable_irq(_irq);
+}
+
+void
+Device::interrupt_disable()
+{
+ if (_irq_attached)
+ up_disable_irq(_irq);
+}
+
+void
+Device::interrupt(void *context)
+{
+ // default action is to disable the interrupt so we don't get called again
+ interrupt_disable();
+}
+
+void
+Device::log(const char *fmt, ...)
+{
+ va_list ap;
+
+ printf("[%s] ", _name);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+ fflush(stdout);
+}
+
+void
+Device::debug(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (_debug_enabled) {
+ printf("<%s> ", _name);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+ fflush(stdout);
+ }
+}
+
+static int
+register_interrupt(int irq, Device *owner)
+{
+ int ret = -ENOMEM;
+
+ // look for a slot where we can register the interrupt
+ for (unsigned i = 0; i < irq_nentries; i++) {
+ if (irq_entries[i].irq == 0) {
+
+ // great, we could put it here; try attaching it
+ ret = irq_attach(irq, &interrupt);
+
+ if (ret == OK) {
+ irq_entries[i].irq = irq;
+ irq_entries[i].owner = owner;
+ }
+
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void
+unregister_interrupt(int irq)
+{
+ for (unsigned i = 0; i < irq_nentries; i++) {
+ if (irq_entries[i].irq == irq) {
+ irq_entries[i].irq = 0;
+ irq_entries[i].owner = nullptr;
+ }
+ }
+}
+
+static int
+interrupt(int irq, void *context)
+{
+ for (unsigned i = 0; i < irq_nentries; i++) {
+ if (irq_entries[i].irq == irq) {
+ irq_entries[i].owner->interrupt(context);
+ break;
+ }
+ }
+
+ return OK;
+}
+
+
+} // namespace device \ No newline at end of file
diff --git a/src/drivers/device/device.h b/src/drivers/device/device.h
new file mode 100644
index 000000000..7d375aab9
--- /dev/null
+++ b/src/drivers/device/device.h
@@ -0,0 +1,447 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file device.h
+ *
+ * Definitions for the generic base classes in the device framework.
+ */
+
+#ifndef _DEVICE_DEVICE_H
+#define _DEVICE_DEVICE_H
+
+/*
+ * Includes here should only cover the needs of the framework definitions.
+ */
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <poll.h>
+
+#include <nuttx/fs/fs.h>
+
+/**
+ * Namespace encapsulating all device framework classes, functions and data.
+ */
+namespace device __EXPORT
+{
+
+/**
+ * Fundamental base class for all device drivers.
+ *
+ * This class handles the basic "being a driver" things, including
+ * interrupt registration and dispatch.
+ */
+class __EXPORT Device
+{
+public:
+ /**
+ * Interrupt handler.
+ */
+ virtual void interrupt(void *ctx); /**< interrupt handler */
+
+protected:
+ const char *_name; /**< driver name */
+ bool _debug_enabled; /**< if true, debug messages are printed */
+
+ /**
+ * Constructor
+ *
+ * @param name Driver name
+ * @param irq Interrupt assigned to the device.
+ */
+ Device(const char *name,
+ int irq = 0);
+ ~Device();
+
+ /**
+ * Initialise the driver and make it ready for use.
+ *
+ * @return OK if the driver initialised OK.
+ */
+ virtual int init();
+
+ /**
+ * Enable the device interrupt
+ */
+ void interrupt_enable();
+
+ /**
+ * Disable the device interrupt
+ */
+ void interrupt_disable();
+
+ /**
+ * Take the driver lock.
+ *
+ * Each driver instance has its own lock/semaphore.
+ *
+ * Note that we must loop as the wait may be interrupted by a signal.
+ */
+ void lock() {
+ do {} while (sem_wait(&_lock) != 0);
+ }
+
+ /**
+ * Release the driver lock.
+ */
+ void unlock() {
+ sem_post(&_lock);
+ }
+
+ /**
+ * Log a message.
+ *
+ * The message is prefixed with the driver name, and followed
+ * by a newline.
+ */
+ void log(const char *fmt, ...);
+
+ /**
+ * Print a debug message.
+ *
+ * The message is prefixed with the driver name, and followed
+ * by a newline.
+ */
+ void debug(const char *fmt, ...);
+
+private:
+ int _irq;
+ bool _irq_attached;
+ sem_t _lock;
+
+ /** disable copy construction for this and all subclasses */
+ Device(const Device &);
+
+ /** disable assignment for this and all subclasses */
+ Device &operator = (const Device &);
+
+ /**
+ * Register ourselves as a handler for an interrupt
+ *
+ * @param irq The interrupt to claim
+ * @return OK if the interrupt was registered
+ */
+ int dev_register_interrupt(int irq);
+
+ /**
+ * Unregister ourselves as a handler for any interrupt
+ */
+ void dev_unregister_interrupt();
+
+ /**
+ * Interrupt dispatcher
+ *
+ * @param irq The interrupt that has been triggered.
+ * @param context Pointer to the interrupted context.
+ */
+ static void dev_interrupt(int irq, void *context);
+};
+
+/**
+ * Abstract class for any character device
+ */
+class __EXPORT CDev : public Device
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param name Driver name
+ * @param devname Device node name
+ * @param irq Interrupt assigned to the device
+ */
+ CDev(const char *name, const char *devname, int irq = 0);
+
+ /**
+ * Destructor
+ */
+ ~CDev();
+
+ virtual int init();
+
+ /**
+ * Handle an open of the device.
+ *
+ * This function is called for every open of the device. The default
+ * implementation maintains _open_count and always returns OK.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @return OK if the open is allowed, -errno otherwise.
+ */
+ virtual int open(struct file *filp);
+
+ /**
+ * Handle a close of the device.
+ *
+ * This function is called for every close of the device. The default
+ * implementation maintains _open_count and returns OK as long as it is not zero.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @return OK if the close was successful, -errno otherwise.
+ */
+ virtual int close(struct file *filp);
+
+ /**
+ * Perform a read from the device.
+ *
+ * The default implementation returns -ENOSYS.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @param buffer Pointer to the buffer into which data should be placed.
+ * @param buflen The number of bytes to be read.
+ * @return The number of bytes read or -errno otherwise.
+ */
+ virtual ssize_t read(struct file *filp, char *buffer, size_t buflen);
+
+ /**
+ * Perform a write to the device.
+ *
+ * The default implementation returns -ENOSYS.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @param buffer Pointer to the buffer from which data should be read.
+ * @param buflen The number of bytes to be written.
+ * @return The number of bytes written or -errno otherwise.
+ */
+ virtual ssize_t write(struct file *filp, const char *buffer, size_t buflen);
+
+ /**
+ * Perform a logical seek operation on the device.
+ *
+ * The default implementation returns -ENOSYS.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @param offset The new file position relative to whence.
+ * @param whence SEEK_OFS, SEEK_CUR or SEEK_END.
+ * @return The previous offset, or -errno otherwise.
+ */
+ virtual off_t seek(struct file *filp, off_t offset, int whence);
+
+ /**
+ * Perform an ioctl operation on the device.
+ *
+ * The default implementation handles DIOC_GETPRIV, and otherwise
+ * returns -ENOTTY. Subclasses should call the default implementation
+ * for any command they do not handle themselves.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @param cmd The ioctl command value.
+ * @param arg The ioctl argument value.
+ * @return OK on success, or -errno otherwise.
+ */
+ virtual int ioctl(struct file *filp, int cmd, unsigned long arg);
+
+ /**
+ * Perform a poll setup/teardown operation.
+ *
+ * This is handled internally and should not normally be overridden.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @param fds Poll descriptor being waited on.
+ * @param arg True if this is establishing a request, false if
+ * it is being torn down.
+ * @return OK on success, or -errno otherwise.
+ */
+ virtual int poll(struct file *filp, struct pollfd *fds, bool setup);
+
+ /**
+ * Test whether the device is currently open.
+ *
+ * This can be used to avoid tearing down a device that is still active.
+ *
+ * @return True if the device is currently open.
+ */
+ bool is_open() { return _open_count > 0; }
+
+protected:
+ /**
+ * Pointer to the default cdev file operations table; useful for
+ * registering clone devices etc.
+ */
+ static const struct file_operations fops;
+
+ /**
+ * Check the current state of the device for poll events from the
+ * perspective of the file.
+ *
+ * This function is called by the default poll() implementation when
+ * a poll is set up to determine whether the poll should return immediately.
+ *
+ * The default implementation returns no events.
+ *
+ * @param filp The file that's interested.
+ * @return The current set of poll events.
+ */
+ virtual pollevent_t poll_state(struct file *filp);
+
+ /**
+ * Report new poll events.
+ *
+ * This function should be called anytime the state of the device changes
+ * in a fashion that might be interesting to a poll waiter.
+ *
+ * @param events The new event(s) being announced.
+ */
+ virtual void poll_notify(pollevent_t events);
+
+ /**
+ * Internal implementation of poll_notify.
+ *
+ * @param fds A poll waiter to notify.
+ * @param events The event(s) to send to the waiter.
+ */
+ virtual void poll_notify_one(struct pollfd *fds, pollevent_t events);
+
+ /**
+ * Notification of the first open.
+ *
+ * This function is called when the device open count transitions from zero
+ * to one. The driver lock is held for the duration of the call.
+ *
+ * The default implementation returns OK.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @return OK if the open should proceed, -errno otherwise.
+ */
+ virtual int open_first(struct file *filp);
+
+ /**
+ * Notification of the last close.
+ *
+ * This function is called when the device open count transitions from
+ * one to zero. The driver lock is held for the duration of the call.
+ *
+ * The default implementation returns OK.
+ *
+ * @param filp Pointer to the NuttX file structure.
+ * @return OK if the open should return OK, -errno otherwise.
+ */
+ virtual int close_last(struct file *filp);
+
+private:
+ static const unsigned _max_pollwaiters = 8;
+
+ const char *_devname; /**< device node name */
+ bool _registered; /**< true if device name was registered */
+ unsigned _open_count; /**< number of successful opens */
+
+ struct pollfd *_pollset[_max_pollwaiters];
+
+ /**
+ * Store a pollwaiter in a slot where we can find it later.
+ *
+ * Expands the pollset as required. Must be called with the driver locked.
+ *
+ * @return OK, or -errno on error.
+ */
+ int store_poll_waiter(struct pollfd *fds);
+
+ /**
+ * Remove a poll waiter.
+ *
+ * @return OK, or -errno on error.
+ */
+ int remove_poll_waiter(struct pollfd *fds);
+};
+
+/**
+ * Abstract class for character device accessed via PIO
+ */
+class __EXPORT PIO : public CDev
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param name Driver name
+ * @param devname Device node name
+ * @param base Base address of the device PIO area
+ * @param irq Interrupt assigned to the device (or zero if none)
+ */
+ PIO(const char *name,
+ const char *devname,
+ uint32_t base,
+ int irq = 0);
+ ~PIO();
+
+ int init();
+
+protected:
+
+ /**
+ * Read a register
+ *
+ * @param offset Register offset in bytes from the base address.
+ */
+ uint32_t reg(uint32_t offset) {
+ return *(volatile uint32_t *)(_base + offset);
+ }
+
+ /**
+ * Write a register
+ *
+ * @param offset Register offset in bytes from the base address.
+ * @param value Value to write.
+ */
+ void reg(uint32_t offset, uint32_t value) {
+ *(volatile uint32_t *)(_base + offset) = value;
+ }
+
+ /**
+ * Modify a register
+ *
+ * Note that there is a risk of a race during the read/modify/write cycle
+ * that must be taken care of by the caller.
+ *
+ * @param offset Register offset in bytes from the base address.
+ * @param clearbits Bits to clear in the register
+ * @param setbits Bits to set in the register
+ */
+ void modify(uint32_t offset, uint32_t clearbits, uint32_t setbits) {
+ uint32_t val = reg(offset);
+ val &= ~clearbits;
+ val |= setbits;
+ reg(offset, val);
+ }
+
+private:
+ uint32_t _base;
+};
+
+} // namespace device
+
+#endif /* _DEVICE_DEVICE_H */ \ No newline at end of file
diff --git a/src/drivers/device/i2c.cpp b/src/drivers/device/i2c.cpp
new file mode 100644
index 000000000..a416801eb
--- /dev/null
+++ b/src/drivers/device/i2c.cpp
@@ -0,0 +1,204 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file i2c.cpp
+ *
+ * Base class for devices attached via the I2C bus.
+ *
+ * @todo Bus frequency changes; currently we do nothing with the value
+ * that is supplied. Should we just depend on the bus knowing?
+ */
+
+#include "i2c.h"
+
+namespace device
+{
+
+I2C::I2C(const char *name,
+ const char *devname,
+ int bus,
+ uint16_t address,
+ uint32_t frequency,
+ int irq) :
+ // base class
+ CDev(name, devname, irq),
+ // public
+ // protected
+ _retries(0),
+ // private
+ _bus(bus),
+ _address(address),
+ _frequency(frequency),
+ _dev(nullptr)
+{
+}
+
+I2C::~I2C()
+{
+ if (_dev)
+ up_i2cuninitialize(_dev);
+}
+
+int
+I2C::init()
+{
+ int ret = OK;
+
+ /* attach to the i2c bus */
+ _dev = up_i2cinitialize(_bus);
+
+ if (_dev == nullptr) {
+ debug("failed to init I2C");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ // call the probe function to check whether the device is present
+ ret = probe();
+
+ if (ret != OK) {
+ debug("probe failed");
+ goto out;
+ }
+
+ // do base class init, which will create device node, etc
+ ret = CDev::init();
+
+ if (ret != OK) {
+ debug("cdev init failed");
+ goto out;
+ }
+
+ // tell the world where we are
+ log("on I2C bus %d at 0x%02x", _bus, _address);
+
+out:
+ return ret;
+}
+
+int
+I2C::probe()
+{
+ // Assume the device is too stupid to be discoverable.
+ return OK;
+}
+
+int
+I2C::transfer(const uint8_t *send, unsigned send_len, uint8_t *recv, unsigned recv_len)
+{
+ struct i2c_msg_s msgv[2];
+ unsigned msgs;
+ int ret;
+ unsigned retry_count = 0;
+
+ do {
+ // debug("transfer out %p/%u in %p/%u", send, send_len, recv, recv_len);
+
+ msgs = 0;
+
+ if (send_len > 0) {
+ msgv[msgs].addr = _address;
+ msgv[msgs].flags = 0;
+ msgv[msgs].buffer = const_cast<uint8_t *>(send);
+ msgv[msgs].length = send_len;
+ msgs++;
+ }
+
+ if (recv_len > 0) {
+ msgv[msgs].addr = _address;
+ msgv[msgs].flags = I2C_M_READ;
+ msgv[msgs].buffer = recv;
+ msgv[msgs].length = recv_len;
+ msgs++;
+ }
+
+ if (msgs == 0)
+ return -EINVAL;
+
+ /*
+ * I2C architecture means there is an unavoidable race here
+ * if there are any devices on the bus with a different frequency
+ * preference. Really, this is pointless.
+ */
+ I2C_SETFREQUENCY(_dev, _frequency);
+ ret = I2C_TRANSFER(_dev, &msgv[0], msgs);
+
+ /* success */
+ if (ret == OK)
+ break;
+
+ /* if we have already retried once, or we are going to give up, then reset the bus */
+ if ((retry_count >= 1) || (retry_count >= _retries))
+ up_i2creset(_dev);
+
+ } while (retry_count++ < _retries);
+
+ return ret;
+
+}
+
+int
+I2C::transfer(i2c_msg_s *msgv, unsigned msgs)
+{
+ int ret;
+ unsigned retry_count = 0;
+
+ /* force the device address into the message vector */
+ for (unsigned i = 0; i < msgs; i++)
+ msgv[i].addr = _address;
+
+
+ do {
+ /*
+ * I2C architecture means there is an unavoidable race here
+ * if there are any devices on the bus with a different frequency
+ * preference. Really, this is pointless.
+ */
+ I2C_SETFREQUENCY(_dev, _frequency);
+ ret = I2C_TRANSFER(_dev, msgv, msgs);
+
+ /* success */
+ if (ret == OK)
+ break;
+
+ /* if we have already retried once, or we are going to give up, then reset the bus */
+ if ((retry_count >= 1) || (retry_count >= _retries))
+ up_i2creset(_dev);
+
+ } while (retry_count++ < _retries);
+
+ return ret;
+}
+
+} // namespace device \ No newline at end of file
diff --git a/src/drivers/device/i2c.h b/src/drivers/device/i2c.h
new file mode 100644
index 000000000..7daba31be
--- /dev/null
+++ b/src/drivers/device/i2c.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file i2c.h
+ *
+ * Base class for devices connected via I2C.
+ */
+
+#ifndef _DEVICE_I2C_H
+#define _DEVICE_I2C_H
+
+#include "device.h"
+
+#include <nuttx/i2c.h>
+
+namespace device __EXPORT
+{
+
+/**
+ * Abstract class for character device on I2C
+ */
+class __EXPORT I2C : public CDev
+{
+
+protected:
+ /**
+ * The number of times a read or write operation will be retried on
+ * error.
+ */
+ unsigned _retries;
+
+ /**
+ * The I2C bus number the device is attached to.
+ */
+ int _bus;
+
+ /**
+ * @ Constructor
+ *
+ * @param name Driver name
+ * @param devname Device node name
+ * @param bus I2C bus on which the device lives
+ * @param address I2C bus address, or zero if set_address will be used
+ * @param frequency I2C bus frequency for the device (currently not used)
+ * @param irq Interrupt assigned to the device (or zero if none)
+ */
+ I2C(const char *name,
+ const char *devname,
+ int bus,
+ uint16_t address,
+ uint32_t frequency,
+ int irq = 0);
+ ~I2C();
+
+ virtual int init();
+
+ /**
+ * Check for the presence of the device on the bus.
+ */
+ virtual int probe();
+
+ /**
+ * Perform an I2C transaction to the device.
+ *
+ * At least one of send_len and recv_len must be non-zero.
+ *
+ * @param send Pointer to bytes to send.
+ * @param send_len Number of bytes to send.
+ * @param recv Pointer to buffer for bytes received.
+ * @param recv_len Number of bytes to receive.
+ * @return OK if the transfer was successful, -errno
+ * otherwise.
+ */
+ int transfer(const uint8_t *send, unsigned send_len,
+ uint8_t *recv, unsigned recv_len);
+
+ /**
+ * Perform a multi-part I2C transaction to the device.
+ *
+ * @param msgv An I2C message vector.
+ * @param msgs The number of entries in the message vector.
+ * @return OK if the transfer was successful, -errno
+ * otherwise.
+ */
+ int transfer(i2c_msg_s *msgv, unsigned msgs);
+
+ /**
+ * Change the bus address.
+ *
+ * Most often useful during probe() when the driver is testing
+ * several possible bus addresses.
+ *
+ * @param address The new bus address to set.
+ */
+ void set_address(uint16_t address) {
+ _address = address;
+ }
+
+private:
+ uint16_t _address;
+ uint32_t _frequency;
+ struct i2c_dev_s *_dev;
+};
+
+} // namespace device
+
+#endif /* _DEVICE_I2C_H */
diff --git a/src/drivers/device/module.mk b/src/drivers/device/module.mk
new file mode 100644
index 000000000..8c716d9cd
--- /dev/null
+++ b/src/drivers/device/module.mk
@@ -0,0 +1,42 @@
+############################################################################
+#
+# Copyright (c) 2012, 2013 PX4 Development Team. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name PX4 nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+#
+# Build the device driver framework.
+#
+
+SRCS = cdev.cpp \
+ device.cpp \
+ i2c.cpp \
+ pio.cpp \
+ spi.cpp
diff --git a/src/drivers/device/pio.cpp b/src/drivers/device/pio.cpp
new file mode 100644
index 000000000..f3a805a5e
--- /dev/null
+++ b/src/drivers/device/pio.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file pio.cpp
+ *
+ * Base class for devices accessed via PIO to registers.
+ */
+
+#include "device.h"
+
+namespace device
+{
+
+PIO::PIO(const char *name,
+ const char *devname,
+ uint32_t base,
+ int irq) :
+ // base class
+ CDev(name, devname, irq),
+ // public
+ // protected
+ // private
+ _base(base)
+{
+}
+
+PIO::~PIO()
+{
+ // nothing to do here...
+}
+
+int
+PIO::init()
+{
+ int ret = OK;
+
+ // base class init first
+ ret = CDev::init();
+
+ return ret;
+}
+
+} // namespace device \ No newline at end of file
diff --git a/src/drivers/device/spi.cpp b/src/drivers/device/spi.cpp
new file mode 100644
index 000000000..8fffd60cb
--- /dev/null
+++ b/src/drivers/device/spi.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file spi.cpp
+ *
+ * Base class for devices connected via SPI.
+ *
+ * @todo Work out if caching the mode/frequency would save any time.
+ *
+ * @todo A separate bus/device abstraction would allow for mixed interrupt-mode
+ * and non-interrupt-mode clients to arbitrate for the bus. As things stand,
+ * a bus shared between clients of both kinds is vulnerable to races between
+ * the two, where an interrupt-mode client will ignore the lock held by the
+ * non-interrupt-mode client.
+ */
+
+#include <nuttx/arch.h>
+
+#include "spi.h"
+
+#ifndef CONFIG_SPI_EXCHANGE
+# error This driver requires CONFIG_SPI_EXCHANGE
+#endif
+
+namespace device
+{
+
+SPI::SPI(const char *name,
+ const char *devname,
+ int bus,
+ enum spi_dev_e device,
+ enum spi_mode_e mode,
+ uint32_t frequency,
+ int irq) :
+ // base class
+ CDev(name, devname, irq),
+ // public
+ // protected
+ // private
+ _bus(bus),
+ _device(device),
+ _mode(mode),
+ _frequency(frequency),
+ _dev(nullptr)
+{
+}
+
+SPI::~SPI()
+{
+ // XXX no way to let go of the bus...
+}
+
+int
+SPI::init()
+{
+ int ret = OK;
+
+ /* attach to the spi bus */
+ if (_dev == nullptr)
+ _dev = up_spiinitialize(_bus);
+
+ if (_dev == nullptr) {
+ debug("failed to init SPI");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ /* deselect device to ensure high to low transition of pin select */
+ SPI_SELECT(_dev, _device, false);
+
+ /* call the probe function to check whether the device is present */
+ ret = probe();
+
+ if (ret != OK) {
+ debug("probe failed");
+ goto out;
+ }
+
+ /* do base class init, which will create the device node, etc. */
+ ret = CDev::init();
+
+ if (ret != OK) {
+ debug("cdev init failed");
+ goto out;
+ }
+
+ /* tell the workd where we are */
+ log("on SPI bus %d at %d", _bus, _device);
+
+out:
+ return ret;
+}
+
+int
+SPI::probe()
+{
+ // assume the device is too stupid to be discoverable
+ return OK;
+}
+
+int
+SPI::transfer(uint8_t *send, uint8_t *recv, unsigned len)
+{
+
+ if ((send == nullptr) && (recv == nullptr))
+ return -EINVAL;
+
+ /* do common setup */
+ if (!up_interrupt_context())
+ SPI_LOCK(_dev, true);
+
+ SPI_SETFREQUENCY(_dev, _frequency);
+ SPI_SETMODE(_dev, _mode);
+ SPI_SETBITS(_dev, 8);
+ SPI_SELECT(_dev, _device, true);
+
+ /* do the transfer */
+ SPI_EXCHANGE(_dev, send, recv, len);
+
+ /* and clean up */
+ SPI_SELECT(_dev, _device, false);
+
+ if (!up_interrupt_context())
+ SPI_LOCK(_dev, false);
+
+ return OK;
+}
+
+} // namespace device
diff --git a/src/drivers/device/spi.h b/src/drivers/device/spi.h
new file mode 100644
index 000000000..d2d01efb3
--- /dev/null
+++ b/src/drivers/device/spi.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file spi.h
+ *
+ * Base class for devices connected via SPI.
+ */
+
+#ifndef _DEVICE_SPI_H
+#define _DEVICE_SPI_H
+
+#include "device.h"
+
+#include <nuttx/spi.h>
+
+namespace device __EXPORT
+{
+
+/**
+ * Abstract class for character device on SPI
+ */
+class __EXPORT SPI : public CDev
+{
+protected:
+ /**
+ * Constructor
+ *
+ * @param name Driver name
+ * @param devname Device node name
+ * @param bus SPI bus on which the device lives
+ * @param device Device handle (used by SPI_SELECT)
+ * @param mode SPI clock/data mode
+ * @param frequency SPI clock frequency
+ * @param irq Interrupt assigned to the device (or zero if none)
+ */
+ SPI(const char *name,
+ const char *devname,
+ int bus,
+ enum spi_dev_e device,
+ enum spi_mode_e mode,
+ uint32_t frequency,
+ int irq = 0);
+ ~SPI();
+
+ virtual int init();
+
+ /**
+ * Check for the presence of the device on the bus.
+ */
+ virtual int probe();
+
+ /**
+ * Perform a SPI transfer.
+ *
+ * If called from interrupt context, this interface does not lock
+ * the bus and may interfere with non-interrupt-context callers.
+ *
+ * Clients in a mixed interrupt/non-interrupt configuration must
+ * ensure appropriate interlocking.
+ *
+ * At least one of send or recv must be non-null.
+ *
+ * @param send Bytes to send to the device, or nullptr if
+ * no data is to be sent.
+ * @param recv Buffer for receiving bytes from the device,
+ * or nullptr if no bytes are to be received.
+ * @param len Number of bytes to transfer.
+ * @return OK if the exchange was successful, -errno
+ * otherwise.
+ */
+ int transfer(uint8_t *send, uint8_t *recv, unsigned len);
+
+private:
+ int _bus;
+ enum spi_dev_e _device;
+ enum spi_mode_e _mode;
+ uint32_t _frequency;
+ struct spi_dev_s *_dev;
+};
+
+} // namespace device
+
+#endif /* _DEVICE_SPI_H */