Revision 8bb9b9a006e8b092be3a14530d8f12cb4cb9428b authored by Greg Kroah-Hartman on 26 November 2014, 19:06:36 UTC, committed by Greg Kroah-Hartman on 26 November 2014, 19:06:36 UTC
Jonathan writes:

Third set of IIO fixes for the 3.18 cycle.

Most of these are fairly standard little fixes, a bmc150 and bmg160 patch
is to make an ABI change to indicated a specific axis in an event rather
than the generic option in the original drivers.  As both of these drivers
are new in this cycle it would be ideal to push this minor change through
even though it isn't strictly a fix.  A couple of other 'fixes' change
defaults for some settings on these new drivers to more intuitive calues.
Looks like some useful feedback has been coming in for this driver
since it was applied.

* IIO_EVENT_CODE_EXTRACT_DIR bit mask was wrong and has been for a while
  0xCF clearly doesn't give a contiguous bitmask.
* kxcjk-1013 range setting was failing to mask out the previous value
  in the register and hence was 'enable only'.
* men_z188 device id table wasn't null terminated.
* bmg160 and bmc150 both failed to correctly handling an error in mode
  setting.
* bmg160 and bmc150 both had a bug in setting the event direction in the
  event spec (leads to an attribute name being incorrect)
* bmg160 defaulted to an open drain output for the interrupt - as a default
  this obviously only works with some interrupt chips - hence change the
  default to push-pull (note this is a new driver so we aren't going to
  cause any regressions with this change).
* bmc150 had an unintuitive default for the rate of change (motion detector)
  so change it to 0 (new driver so change of default won't cause any
  regressions).
2 parent s 206c5f6 + 9e8e228
Raw File
fmc-chardev.c
/*
 * Copyright (C) 2012 CERN (www.cern.ch)
 * Author: Alessandro Rubini <rubini@gnudd.com>
 *
 * Released according to the GNU GPL, version 2 or any later version.
 *
 * This work is part of the White Rabbit project, a research effort led
 * by CERN, the European Institute for Nuclear Research.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/fmc.h>
#include <linux/uaccess.h>

static LIST_HEAD(fc_devices);
static DEFINE_SPINLOCK(fc_lock);

struct fc_instance {
	struct list_head list;
	struct fmc_device *fmc;
	struct miscdevice misc;
};

/* at open time, we must identify our device */
static int fc_open(struct inode *ino, struct file *f)
{
	struct fmc_device *fmc;
	struct fc_instance *fc;
	int minor = iminor(ino);

	list_for_each_entry(fc, &fc_devices, list)
		if (fc->misc.minor == minor)
			break;
	if (fc->misc.minor != minor)
		return -ENODEV;
	fmc = fc->fmc;
	if (try_module_get(fmc->owner) == 0)
		return -ENODEV;

	f->private_data = fmc;
	return 0;
}

static int fc_release(struct inode *ino, struct file *f)
{
	struct fmc_device *fmc = f->private_data;
	module_put(fmc->owner);
	return 0;
}

/* read and write are simple after the default llseek has been used */
static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
		       loff_t *offp)
{
	struct fmc_device *fmc = f->private_data;
	unsigned long addr;
	uint32_t val;

	if (count < sizeof(val))
		return -EINVAL;
	count = sizeof(val);

	addr = *offp;
	if (addr > fmc->memlen)
		return -ESPIPE; /* Illegal seek */
	val = fmc_readl(fmc, addr);
	if (copy_to_user(buf, &val, count))
		return -EFAULT;
	*offp += count;
	return count;
}

static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
			loff_t *offp)
{
	struct fmc_device *fmc = f->private_data;
	unsigned long addr;
	uint32_t val;

	if (count < sizeof(val))
		return -EINVAL;
	count = sizeof(val);

	addr = *offp;
	if (addr > fmc->memlen)
		return -ESPIPE; /* Illegal seek */
	if (copy_from_user(&val, buf, count))
		return -EFAULT;
	fmc_writel(fmc, val, addr);
	*offp += count;
	return count;
}

static const struct file_operations fc_fops = {
	.owner = THIS_MODULE,
	.open = fc_open,
	.release = fc_release,
	.llseek = generic_file_llseek,
	.read = fc_read,
	.write = fc_write,
};


/* Device part .. */
static int fc_probe(struct fmc_device *fmc);
static int fc_remove(struct fmc_device *fmc);

static struct fmc_driver fc_drv = {
	.version = FMC_VERSION,
	.driver.name = KBUILD_MODNAME,
	.probe = fc_probe,
	.remove = fc_remove,
	/* no table: we want to match everything */
};

/* We accept the generic busid parameter */
FMC_PARAM_BUSID(fc_drv);

/* probe and remove must allocate and release a misc device */
static int fc_probe(struct fmc_device *fmc)
{
	int ret;
	int index = 0;

	struct fc_instance *fc;

	if (fmc->op->validate)
		index = fmc->op->validate(fmc, &fc_drv);
	if (index < 0)
		return -EINVAL; /* not our device: invalid */

	/* Create a char device: we want to create it anew */
	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
	if (!fc)
		return -ENOMEM;
	fc->fmc = fmc;
	fc->misc.minor = MISC_DYNAMIC_MINOR;
	fc->misc.fops = &fc_fops;
	fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);

	ret = misc_register(&fc->misc);
	if (ret < 0)
		goto out;
	spin_lock(&fc_lock);
	list_add(&fc->list, &fc_devices);
	spin_unlock(&fc_lock);
	dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
		 fc->misc.name);
	return 0;

out:
	kfree(fc->misc.name);
	kfree(fc);
	return ret;
}

static int fc_remove(struct fmc_device *fmc)
{
	struct fc_instance *fc;

	list_for_each_entry(fc, &fc_devices, list)
		if (fc->fmc == fmc)
			break;
	if (fc->fmc != fmc) {
		dev_err(&fmc->dev, "remove called but not found\n");
		return -ENODEV;
	}

	spin_lock(&fc_lock);
	list_del(&fc->list);
	spin_unlock(&fc_lock);
	misc_deregister(&fc->misc);
	kfree(fc->misc.name);
	kfree(fc);

	return 0;
}


static int fc_init(void)
{
	int ret;

	ret = fmc_driver_register(&fc_drv);
	return ret;
}

static void fc_exit(void)
{
	fmc_driver_unregister(&fc_drv);
}

module_init(fc_init);
module_exit(fc_exit);

MODULE_LICENSE("GPL");
back to top