Skip to content

Commit

Permalink
staging:iio:dummy Add event support + fake event generator
Browse files Browse the repository at this point in the history
The event generator is not very pretty but does the job and
allows this driver to look a lot more like a normal driver
than it otherwise would.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Jonathan Cameron authored and Greg Kroah-Hartman committed Oct 17, 2011
1 parent 3a84331 commit e647700
Show file tree
Hide file tree
Showing 7 changed files with 543 additions and 26 deletions.
20 changes: 17 additions & 3 deletions drivers/staging/iio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,26 @@ source "drivers/staging/iio/meter/Kconfig"
source "drivers/staging/iio/resolver/Kconfig"
source "drivers/staging/iio/trigger/Kconfig"

config IIO_DUMMY_EVGEN
tristate

config IIO_SIMPLE_DUMMY
tristate "An example driver with no hardware requirements"
select IIO_SIMPLE_DUMMY_EVGEN if IIO_SIMPLE_DUMMY_EVENTS
help
Driver intended mainly as documentation for how to write
a driver. May also be useful for testing userspace code
without hardward.
Driver intended mainly as documentation for how to write
a driver. May also be useful for testing userspace code
without hardward.

if IIO_SIMPLE_DUMMY

config IIO_SIMPLE_DUMMY_EVENTS
boolean "Event generation support"
select IIO_DUMMY_EVGEN
help
Add some dummy events to the simple dummy driver.


endif # IIO_SIMPLE_DUMMY

endif # IIO
6 changes: 5 additions & 1 deletion drivers/staging/iio/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o

obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_simple_dummy.o
obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
iio_dummy-y := iio_simple_dummy.o
iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o

obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o

obj-y += accel/
obj-y += adc/
Expand Down
217 changes: 217 additions & 0 deletions drivers/staging/iio/iio_dummy_evgen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/**
* Copyright (c) 2011 Jonathan Cameron
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* Companion module to the iio simple dummy example driver.
* The purpose of this is to generate 'fake' event interrupts thus
* allowing that driver's code to be as close as possible to that of
* a normal driver talking to hardware. The approach used here
* is not intended to be general and just happens to work for this
* particular use case.
*/

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/sysfs.h>

#include "iio_dummy_evgen.h"
#include "iio.h"
#include "sysfs.h"

/* Fiddly bit of faking and irq without hardware */
#define IIO_EVENTGEN_NO 10
/**
* struct iio_dummy_evgen - evgen state
* @chip: irq chip we are faking
* @base: base of irq range
* @enabled: mask of which irqs are enabled
* @inuse: mask of which irqs actually have anyone connected
* @lock: protect the evgen state
*/
struct iio_dummy_eventgen {
struct irq_chip chip;
int base;
bool enabled[IIO_EVENTGEN_NO];
bool inuse[IIO_EVENTGEN_NO];
struct mutex lock;
};

/* We can only ever have one instance of this 'device' */
static struct iio_dummy_eventgen *iio_evgen;
static const char *iio_evgen_name = "iio_dummy_evgen";

static void iio_dummy_event_irqmask(struct irq_data *d)
{
struct irq_chip *chip = irq_data_get_irq_chip(d);
struct iio_dummy_eventgen *evgen =
container_of(chip, struct iio_dummy_eventgen, chip);

evgen->enabled[d->irq - evgen->base] = false;
}

static void iio_dummy_event_irqunmask(struct irq_data *d)
{
struct irq_chip *chip = irq_data_get_irq_chip(d);
struct iio_dummy_eventgen *evgen =
container_of(chip, struct iio_dummy_eventgen, chip);

evgen->enabled[d->irq - evgen->base] = true;
}

static int iio_dummy_evgen_create(void)
{
int ret, i;

iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
if (iio_evgen == NULL)
return -ENOMEM;

iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0);
if (iio_evgen->base < 0) {
ret = iio_evgen->base;
kfree(iio_evgen);
return ret;
}
iio_evgen->chip.name = iio_evgen_name;
iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask;
iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask;
for (i = 0; i < IIO_EVENTGEN_NO; i++) {
irq_set_chip(iio_evgen->base + i, &iio_evgen->chip);
irq_set_handler(iio_evgen->base + i, &handle_simple_irq);
irq_modify_status(iio_evgen->base + i,
IRQ_NOREQUEST | IRQ_NOAUTOEN,
IRQ_NOPROBE);
}
mutex_init(&iio_evgen->lock);
return 0;
}

/**
* iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
*
* This function will give a free allocated irq to a client device.
* That irq can then be caused to 'fire' by using the associated sysfs file.
*/
int iio_dummy_evgen_get_irq(void)
{
int i, ret = 0;
mutex_lock(&iio_evgen->lock);
for (i = 0; i < IIO_EVENTGEN_NO; i++)
if (iio_evgen->inuse[i] == false) {
ret = iio_evgen->base + i;
iio_evgen->inuse[i] = true;
break;
}
mutex_unlock(&iio_evgen->lock);
if (i == IIO_EVENTGEN_NO)
return -ENOMEM;
return ret;
}
EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);

/**
* iio_dummy_evgen_release_irq() - give the irq back.
* @irq: irq being returned to the pool
*
* Used by client driver instances to give the irqs back when they disconnect
*/
int iio_dummy_evgen_release_irq(int irq)
{
mutex_lock(&iio_evgen->lock);
iio_evgen->inuse[irq - iio_evgen->base] = false;
mutex_unlock(&iio_evgen->lock);

return 0;
}
EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);

static void iio_dummy_evgen_free(void)
{
irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO);
kfree(iio_evgen);
}

static void iio_evgen_release(struct device *dev)
{
iio_dummy_evgen_free();
}

static ssize_t iio_evgen_poke(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
{
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);

if (iio_evgen->enabled[this_attr->address])
handle_nested_irq(iio_evgen->base + this_attr->address);

return len;
}

static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0);
static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1);
static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2);
static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3);
static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4);
static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5);
static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6);
static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7);
static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8);
static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9);

static struct attribute *iio_evgen_attrs[] = {
&iio_dev_attr_poke_ev0.dev_attr.attr,
&iio_dev_attr_poke_ev1.dev_attr.attr,
&iio_dev_attr_poke_ev2.dev_attr.attr,
&iio_dev_attr_poke_ev3.dev_attr.attr,
&iio_dev_attr_poke_ev4.dev_attr.attr,
&iio_dev_attr_poke_ev5.dev_attr.attr,
&iio_dev_attr_poke_ev6.dev_attr.attr,
&iio_dev_attr_poke_ev7.dev_attr.attr,
&iio_dev_attr_poke_ev8.dev_attr.attr,
&iio_dev_attr_poke_ev9.dev_attr.attr,
NULL,
};

static const struct attribute_group iio_evgen_group = {
.attrs = iio_evgen_attrs,
};

static const struct attribute_group *iio_evgen_groups[] = {
&iio_evgen_group,
NULL
};

static struct device iio_evgen_dev = {
.bus = &iio_bus_type,
.groups = iio_evgen_groups,
.release = &iio_evgen_release,
};
static __init int iio_dummy_evgen_init(void)
{
int ret = iio_dummy_evgen_create();
if (ret < 0)
return ret;
device_initialize(&iio_evgen_dev);
dev_set_name(&iio_evgen_dev, "iio_evgen");
return device_add(&iio_evgen_dev);
}
module_init(iio_dummy_evgen_init);

static __exit void iio_dummy_evgen_exit(void)
{
device_unregister(&iio_evgen_dev);
}
module_exit(iio_dummy_evgen_exit);

MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
MODULE_DESCRIPTION("IIO dummy driver");
MODULE_LICENSE("GPL v2");
2 changes: 2 additions & 0 deletions drivers/staging/iio/iio_dummy_evgen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
int iio_dummy_evgen_get_irq(void);
int iio_dummy_evgen_release_irq(int irq);
53 changes: 31 additions & 22 deletions drivers/staging/iio/iio_simple_dummy.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <linux/moduleparam.h>

#include "iio.h"
#include "sysfs.h"
#include "iio_simple_dummy.h"

/*
* A few elements needed to fake a bus for this driver
Expand Down Expand Up @@ -53,26 +55,6 @@ static const struct iio_dummy_accel_calibscale dummy_scales[] = {
{ 733, 13, 0x9 }, /* 733.00013 */
};

/**
* struct iio_dummy_state - device instance specific state.
* @dac_val: cache for dac value
* @single_ended_adc_val: cache for single ended adc value
* @differential_adc_val: cache for differential adc value
* @accel_val: cache for acceleration value
* @accel_calibbias: cache for acceleration calibbias
* @accel_calibscale: cache for acceleration calibscale
* @lock: lock to ensure state is consistent
*/
struct iio_dummy_state {
int dac_val;
int single_ended_adc_val;
int differential_adc_val[2];
int accel_val;
int accel_calibbias;
const struct iio_dummy_accel_calibscale *accel_calibscale;
struct mutex lock;
};

/*
* iio_dummy_channels - Description of available channels
*
Expand Down Expand Up @@ -101,6 +83,14 @@ static struct iio_chan_spec iio_dummy_channels[] = {
*/
(1 << IIO_CHAN_INFO_SCALE_SEPARATE),

#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
/*
* simple event - triggered when value rises above
* a threshold
*/
.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
},
/* Differential ADC channel in_voltage1-voltage2_raw etc*/
{
Expand Down Expand Up @@ -296,6 +286,12 @@ static const struct iio_info iio_dummy_info = {
.driver_module = THIS_MODULE,
.read_raw = &iio_dummy_read_raw,
.write_raw = &iio_dummy_write_raw,
#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
.read_event_config = &iio_simple_dummy_read_event_config,
.write_event_config = &iio_simple_dummy_write_event_config,
.read_event_value = &iio_simple_dummy_read_event_value,
.write_event_value = &iio_simple_dummy_write_event_value,
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
};

/**
Expand Down Expand Up @@ -392,11 +388,16 @@ static int __devinit iio_dummy_probe(int index)
/* Specify that device provides sysfs type interfaces */
indio_dev->modes = INDIO_DIRECT_MODE;

ret = iio_device_register(indio_dev);
ret = iio_simple_dummy_events_register(indio_dev);
if (ret < 0)
goto error_free_device;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto error_unregister_events;

return 0;
error_unregister_events:
iio_simple_dummy_events_unregister(indio_dev);
error_free_device:
/* Note free device should only be called, before registration
* has succeeded. */
Expand All @@ -413,6 +414,7 @@ static int __devinit iio_dummy_probe(int index)
*/
static int iio_dummy_remove(int index)
{
int ret;
/*
* Get a pointer to the device instance iio_dev structure
* from the bus subsystem. E.g.
Expand All @@ -421,15 +423,22 @@ static int iio_dummy_remove(int index)
*/
struct iio_dev *indio_dev = iio_dummy_devs[index];


/* Unregister the device */
iio_device_unregister(indio_dev);

/* Device specific code to power down etc */


ret = iio_simple_dummy_events_unregister(indio_dev);
if (ret)
goto error_ret;

/* Free all structures */
iio_free_device(indio_dev);

return 0;
error_ret:
return ret;
}

/**
Expand Down
Loading

0 comments on commit e647700

Please sign in to comment.