forked from Archive/PX4-Autopilot
225 lines
4.8 KiB
C++
225 lines
4.8 KiB
C++
/****************************************************************************
|
|
*
|
|
* 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 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
|