-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
staging:iio:dummy Add event support + fake event generator
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
Showing
7 changed files
with
543 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.