Search Linux Wireless

Re: [RFC PATCHES] Re: Is configfs the right solution for configuration based fs?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 2008-06-19 at 23:52 -0700, Joel Becker wrote:
> On Fri, Jun 20, 2008 at 04:19:10PM +1000, Ben Nizette wrote:
> > Looks great, thx :-).  I'll convert some of my stuff over to this
> > interface and see how it flies in the real world.
> 
> 	Excellent.  What is your stuff, btw?  Got code?  Love to see it.

I've done a few things recently.  Haavard Skinnemoen has got an
out-of-tree, avr32-specific gpio interface [1] which I first converted
to the generic gpio framework then completely re-wrote.  Both use
configfs but before either version could reach completion they were
obsoleted by David Brownell's gpio sysfs interface.

A version of my gpio-dev interface is attached.  Bear in mind it was
never completed, it's full of known bugs but hey, might be useful for
you anyway :-)

At the moment I'm experimenting with configfs for pinmux control on
AVR32 SoCs.  There's really nothing to see there yet, it's all just
dicking about atm.

> 
> > ISTR when I did this I kept the old semantics and used ERR_PTR and
> > friends if things went pear-shaped.  Given the small number of in-tree
> > users needing to be moved over, a more intrusive change for the sake of
> > a cleaner API like you've done is probably a good thing anyway :-)
> 
> 	Yeah, I like this better than ERR_PTR.
> 	If you like the macros, I'll try to make the next merge window
> as well.  So let me know.

np, will do :-)

	--Ben.

> 
> joel
> 

[1]
http://git.kernel.org/?p=linux/kernel/git/hskinnemoen/avr32-2.6.git;a=blob;f=arch/avr32/mach-at32ap/gpio-dev.c;h=8cf6d1182c31db88fc069a046112a4eaac589308;hb=atmel-2.6.25

/*
 * GPIO /dev and configfs interface
 *
 * Copyright (C) 2008 Nias Digital P/L
 *
 * 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.
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <linux/configfs.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kfifo.h>

#include <asm/uaccess.h>
#include <asm/bug.h>
#include <asm/gpio.h>

#include "gpio-dev.h"

#define MAX_NR_DEVICES	8
#define IRQ_BUFFER_SIZE	256

#define BUF_PUT_INT(k, b) \
			do { \
				char *p = (char *)&b; \
				kfifo_put(k, p, sizeof(int)); \
			} while (0)

#define BUF_GET_INT(k, b) \
			do { \
				char *p = (char *)&b; \
				kfifo_get(k, p, sizeof(int)); \
			} while (0)

static struct class *dev_class;
static dev_t base_devt;

struct gpiodev {
	spinlock_t lock;

	int id;
	char *bankname;

	struct gpiodev_pin *pins;
	int nr_pins;

	int io_enabled;
	int irq_enabled;

	struct kfifo *irq_buf;
	spinlock_t irq_lock;

	struct device *io_dev;
	struct device *irq_dev;

	/* We can unify these somewhat by doing some fop switching
	 * magic, though I can't convince myself it's worth it*/
	struct cdev io_char_dev;
	struct cdev irq_char_dev;

	wait_queue_head_t irq_wq;
	struct fasync_struct *async_queue;

	struct config_group group;
};

struct gpiodev *devices[MAX_NR_DEVICES];
spinlock_t devices_lock;

static irqreturn_t irq_interrupt(int irq, void *dev_id)
{
	int i, found = 0;
	int gpio = irq_to_gpio(irq);

	struct gpiodev *bank = dev_id;

	for (i = 0; i < bank->nr_pins; i++) {
		if (bank->pins[i].pin == gpio) {
			found = 1;
			break;
		}
	}

	if (!found)
		return IRQ_NONE;

	BUF_PUT_INT(bank->irq_buf, i);

	wake_up_interruptible(&bank->irq_wq);

	if (bank->async_queue)
		kill_fasync(&bank->async_queue, SIGIO, POLL_IN);

	return IRQ_HANDLED;
}


static int io_open(struct inode *inode, struct file *file)
{
	struct gpiodev *bank = container_of(inode->i_cdev,
					      struct gpiodev,
					      io_char_dev);

	spin_lock(&bank->lock);
	config_item_get(&bank->group.cg_item);
	file->private_data = bank;
	spin_unlock(&bank->lock);

	return 0;
}

static int io_release(struct inode *inode, struct file *file)
{
	struct gpiodev *bank = file->private_data;

	spin_lock(&bank->lock);
	config_item_put(&bank->group.cg_item);
	spin_unlock(&bank->lock);

	return 0;
}

static ssize_t io_read(struct file *file, char __user *buf,
			     size_t count, loff_t *offset)
{
	int i, ret = 0;
	char *val;

	struct gpiodev *bank = file->private_data;

	count = min(count, (size_t)(bank->nr_pins - *offset));

	val = kmalloc(sizeof(char) * count, GFP_KERNEL);
	if (!val)
		return -ENOMEM;

	spin_lock(&bank->lock);
	for (i = *offset; i < *offset + count; i++) {
		int pin = bank->pins[i].pin;
		int value;

		if (file->f_flags & O_NONBLOCK)
			value = gpio_get_value(pin);
		else
			value = gpio_get_value_cansleep(pin);

		val[i] = (!!value) + '0';
	}

	*offset += count;
	spin_unlock(&bank->lock);

	if (copy_to_user(buf, val, count))
		ret = -EFAULT;

	kfree(val);
	return ret ? ret : count;
}

static ssize_t io_write(struct file *file, const char __user *buf,
			      size_t count, loff_t *offset)
{
	int i, ret = 0;
	char *val, *p;

	struct gpiodev *bank = file->private_data;

	count = min(count, (size_t)(bank->nr_pins - *offset));

	val = kmalloc(sizeof(char) * count, GFP_KERNEL);
	if (!val)
		return -ENOMEM;

	if (copy_from_user(val, buf, count)) {
		ret = -EFAULT;
		goto out_cpy;
	}

	p = val;

	spin_lock(&bank->lock);

	for (i = *offset; i < *offset + count; i++) {
		int pin = bank->pins[i].pin;
		int value = val[i] - '0';

		if (value != 0 || value != 1)
			break;

		bank->pins[i].outval = value;

		if (file->f_flags & O_NONBLOCK)
			gpio_set_value(pin, value);
		else
			gpio_set_value_cansleep(pin, value);
	}

	*offset = i;

	spin_unlock(&bank->lock);

out_cpy:
	kfree(val);
	return ret ? ret : count;
}

static loff_t io_llseek(struct file *file, loff_t off, int whence)
{
	struct gpiodev *bank = file->private_data;
	loff_t newpos;

	spin_lock(&bank->lock);

	switch (whence) {
	case SEEK_SET:
		newpos = off;
		break;
	case SEEK_CUR:
		newpos = file->f_pos + off;
		break;
	case SEEK_END:
		newpos = bank->nr_pins + off;
		break;
	default:
		return -EINVAL;
	}

	spin_unlock(&bank->lock);

	return newpos;
}

static struct file_operations io_fops = {
	.owner		= THIS_MODULE,
	.llseek		= io_llseek,
	.open		= io_open,
	.release	= io_release,
	.read		= io_read,
	.write		= io_write,
};

static int irq_open(struct inode *inode, struct file *file)
{
	struct gpiodev *bank = container_of(inode->i_cdev,
					      struct gpiodev,
					      io_char_dev);

	nonseekable_open(inode, file);

	spin_lock(&bank->lock);
	config_item_get(&bank->group.cg_item);
	file->private_data = bank;
	spin_unlock(&bank->lock);

	return 0;
}

static int irq_release(struct inode *inode, struct file *file)
{
	struct gpiodev *bank = file->private_data;

	spin_lock(&bank->lock);
	config_item_put(&bank->group.cg_item);
	spin_unlock(&bank->lock);

	return 0;
}

static ssize_t irq_read(struct file *file, char __user *buf,
			     size_t count, loff_t *offset)
{
	int i, len, *kbuf;
	struct gpiodev *bank = file->private_data;

	while (!kfifo_len(bank->irq_buf)) {

		if (file->f_flags & O_NONBLOCK)
			return -EAGAIN;

		if (wait_event_interruptible(bank->irq_wq,
					     kfifo_len(bank->irq_buf)))
			return -ERESTARTSYS;

	}

	len = min((size_t)kfifo_len(bank->irq_buf), count);

	kbuf = kmalloc(len * sizeof(int), GFP_KERNEL);

	if (!kbuf)
		return -ENOMEM;

	for (i = 0; i < len; i++)
		BUF_GET_INT(bank->irq_buf, kbuf[i]);

	if (copy_to_user(buf, kbuf, len)) {
		kfree(kbuf);
		return -EFAULT;
	}

	kfree(kbuf);
	return len;
}

static ssize_t irq_write(struct file *file, const char __user *buf,
			      size_t count, loff_t *offset)
{
	struct gpiodev *bank = file->private_data;

	/* Flush buffer */
	kfifo_reset(bank->irq_buf);

	return count;
}

static int irq_fasync(int fd, struct file *file, int mode)
{
	struct gpiodev *bank = file->private_data;

	return fasync_helper(fd, file, mode, &bank->async_queue);
}

static struct file_operations irq_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.open		= irq_open,
	.release	= irq_release,
	.read		= irq_read,
	.write		= irq_write,
	.fasync		= irq_fasync,
};

/*
 * {enable,disable}_bank_{io,irq}() must be called under that bank's
 * lock.  They should only be called through their
 * set_bank_{io,irq}_enabled() which will (amongst other things)
 * take care of this locking for you.
 */
static int enable_bank_io(struct gpiodev *bank)
{
	int i;

	/* What error should a pin request failure be?? */
	int ret = -ENODEV;

	BUG_ON(!bank);

	for (i = 0; i < bank->nr_pins; i++) {
		if (!gpio_request(bank->pins[i].pin, bank->pins[i].label))
			goto err_alloc_pins;
	}

	cdev_init(&bank->io_char_dev, &io_fops);
	bank->io_char_dev.owner = THIS_MODULE;
	ret = cdev_add(&bank->io_char_dev, MKDEV(MAJOR(base_devt),
						 bank->id * 2), 1);

	if (ret < 0)
		goto err_cdev_add;

	bank->io_dev = device_create(dev_class, NULL,
				     MKDEV(MAJOR(base_devt), bank->id * 2),
				     "gpiod-%s", bank->bankname);

	if (IS_ERR(bank->io_dev)) {
		printk(KERN_ERR "gpio-dev: failed to create gpiod%d\n",
		       bank->id);
		ret = PTR_ERR(bank->io_dev);
		goto err_dev;
	}

	printk(KERN_INFO "gpio-dev: created gpiod%d as (%d:%d)\n",
	       bank->id, MAJOR(bank->io_dev->devt),
	       MINOR(bank->io_dev->devt));

	return 0;

err_dev:
	cdev_del(&bank->io_char_dev);
err_cdev_add:
err_alloc_pins:

	while (i--)
		gpio_free(bank->pins[i].pin);

	return ret;
}

static int disable_bank_io(struct gpiodev *bank)
{
	int i;

	BUG_ON(!bank);

	device_unregister(bank->io_dev);
	cdev_del(&bank->io_char_dev);

	for (i = 0; i < bank->nr_pins; i++)
		gpio_free(bank->pins[i].pin);

	return 0;
}

static int set_bank_io_enabled(struct gpiodev *bank, int enabled)
{
	int ret = 0;

	if (!bank) {
		printk(KERN_ERR "gpio-dev: Attempt to change enable " \
				"without valid device binding\n");
		return -EINVAL;
	}

	pr_debug("gpio-dev: setting bank %s enabled from %d to %d\n",
	       bank->bankname, bank->io_enabled, enabled);

	spin_lock(&bank->lock);

	if (bank->io_enabled == enabled)
		goto out;

	if (enabled)
		ret = enable_bank_io(bank);
	else
		ret = disable_bank_io(bank);

	if (!ret)
		bank->io_enabled = enabled;

out:
	spin_unlock(&bank->lock);
	return ret;
}


static int enable_bank_irq(struct gpiodev *bank)
{
	int ret;

	BUG_ON(!bank);

	cdev_init(&bank->irq_char_dev, &irq_fops);
	bank->irq_char_dev.owner = THIS_MODULE;
	ret = cdev_add(&bank->irq_char_dev, MKDEV(MAJOR(base_devt),
						bank->id * 2 + 1), 1);

	if (ret < 0)
		goto err_cdev_add;

	bank->irq_dev = device_create(dev_class, NULL,
					     MKDEV(MAJOR(base_devt), bank->id),
					     "gpioi-%s", bank->bankname);

	if (IS_ERR(bank->irq_dev)) {
		printk(KERN_ERR "gpio-dev: failed to create gpioi%d\n",
		       bank->id);
		ret = PTR_ERR(bank->irq_dev);
		goto err_dev;
	}

	printk(KERN_INFO "gpio-dev: created gpioi%d as (%d:%d)\n",
	       bank->id, MAJOR(bank->irq_dev->devt),
	       MINOR(bank->irq_dev->devt));

	return 0;

err_dev:
	cdev_del(&bank->irq_char_dev);
err_cdev_add:

	return ret;
}

static int disable_bank_irq(struct gpiodev *bank)
{
	BUG_ON(!bank);

	device_unregister(bank->irq_dev);
	cdev_del(&bank->irq_char_dev);

	return 0;
}

static int set_bank_irq_enabled(struct gpiodev *bank, int enabled)
{
	int ret = 0;

	if (!bank) {
		printk(KERN_ERR "gpio-dev: Attempt to change irq enable " \
				"without valid device binding\n");
		return -EINVAL;
	}

	pr_debug("gpio-dev: setting bank irq %s enabled from %d to %d\n",
	       bank->bankname, bank->irq_enabled, enabled);

	spin_lock(&bank->lock);

	if (bank->irq_enabled == enabled)
		goto out;

	if (enabled)
		ret = enable_bank_irq(bank);
	else
		ret = disable_bank_irq(bank);

	if (!ret)
		bank->irq_enabled = enabled;

out:
	spin_unlock(&bank->lock);
	return ret;
}

static inline struct gpiodev *to_gpiodev(struct config_group *group)
{
	return group ? container_of(group, struct gpiodev, group) : NULL;
}

static struct configfs_attribute bank_attr_enableio = {
	.ca_owner = THIS_MODULE,
	.ca_name = "enable-io",
	.ca_mode = S_IRUGO | S_IWUGO,
};

static struct configfs_attribute bank_attr_enableirq = {
	.ca_owner = THIS_MODULE,
	.ca_name = "enable-irq",
	.ca_mode = S_IRUGO | S_IWUGO,
};

static struct configfs_attribute *cfg_bank_attrs[] = {
	&bank_attr_enableio,
	&bank_attr_enableirq,
	NULL,
};

static ssize_t cfg_bank_attr_show(struct config_item *item,
				      struct configfs_attribute *attr,
				      char *page)
{
	struct gpiodev *bank = to_gpiodev(to_config_group(item));

	if (attr == &bank_attr_enableio)
		return sprintf(page, "%d\n", bank->io_enabled);
	else if (attr == &bank_attr_enableirq)
		return sprintf(page, "%d\n", bank->irq_enabled);
	else
		WARN_ON(1);

	return 0;
}

static ssize_t cfg_bank_attr_store(struct config_item *item,
				       struct configfs_attribute *attr,
				       const char *page, size_t count)
{
	struct gpiodev *bank = to_gpiodev(to_config_group(item));

	/* The only attributes which will come here are enable-{io,irq} */

	unsigned long tmp;
	int ret = 0;
	char *p = (char *) page;

	tmp = simple_strtoul(p, &p, 10);
	if (!p || (*p && (*p != '\n')))
		return -EINVAL;

	if (tmp > INT_MAX)
		return -ERANGE;

	/* Booleanize */
	tmp = !!tmp;

	pr_debug("gpio-dev: changing enabled attr on bank %s, attribute %d\n",
	       bank->bankname, (int)attr);

	if (attr == &bank_attr_enableio)
		ret = set_bank_io_enabled(bank, tmp);
	else if (attr == &bank_attr_enableirq)
		ret = set_bank_irq_enabled(bank, tmp);
	else {
		printk(KERN_ERR "unexpected attribute at %s, %d",
			__func__, __LINE__);
		WARN_ON(1);
	}

	return ret ? ret : count;
}

static struct configfs_item_operations cfg_bank_item_ops = {
	.show_attribute		= cfg_bank_attr_show,
	.store_attribute	= cfg_bank_attr_store,
};

static struct config_item_type cfg_bank_type = {
	.ct_owner	= THIS_MODULE,
	.ct_item_ops	= &cfg_bank_item_ops,
	.ct_attrs	= cfg_bank_attrs,
};

static struct config_group *cfg_make_group(struct config_group *group,
						   const char *name)
{
	int i;

	struct gpiodev *dev = NULL;

	spin_lock(&devices_lock);
	for (i = 0; i < MAX_NR_DEVICES; i++) {
		if (!devices[i])
			continue;

		if (strcmp(name, devices[i]->bankname) == 0) {
			dev = devices[i];
			printk(KERN_INFO "gpio-dev: Bound configfs instance" \
			       " to device %s\n", devices[i]->bankname);
		}
	}
	spin_unlock(&devices_lock);

	if (!dev) {
		printk(KERN_WARNING "gpio-dev: Can't find bank \"%s\"\n", name);
		/*
		 * Once I get home and can modify the configfs_mkdir code
		 * to allow more informative return types we can modify this
		 * to, eg, ERR_PTR(-ENODEV)
		 */
		return NULL;
	}

	/*
	 * REVISIT: Do we need to get this here?  The docco says we should drop
	 * a reference in drop_group() so I assume we need to actually get one
	 * at one stage
	 */
	config_item_get(&dev->group.cg_item);

	return &dev->group;
}

static void cfg_drop_group(struct config_group *group,
				   struct config_item *item)
{
	struct gpiodev *bank = to_gpiodev(to_config_group(item));

	pr_debug("gpio-dev: bank %s getting dropped\n",
	       bank->bankname);

	set_bank_io_enabled(bank, 0);
	set_bank_irq_enabled(bank, 0);

	config_item_put(item);
}

static struct configfs_attribute attr_banks = {
	.ca_owner = THIS_MODULE,
	.ca_name = "banks",
	.ca_mode = S_IRUGO,
};

static struct configfs_attribute *cfg_attrs[] = {
	&attr_banks,
	NULL,
};

static ssize_t cfg_attr_show(struct config_item *item,
					struct configfs_attribute *attr,
					char *page)
{
	int i;
	int count = 0;

	/* The only attribute which comes here is 'banks' */

	spin_lock(&devices_lock);
	for (i = 0; i < MAX_NR_DEVICES; i++) {
		if (devices[i])
			count += sprintf(page + count, "%s\n",
					 devices[i]->bankname);
	}
	spin_unlock(&devices_lock);

	return count;
}

static struct configfs_item_operations cfg_item_ops = {
	.show_attribute	= cfg_attr_show,
};

static struct configfs_group_operations cfg_group_ops = {
	.make_group	= cfg_make_group,
	.drop_item	= cfg_drop_group,
};

static struct config_item_type cfg_type = {
	.ct_item_ops	= &cfg_item_ops,
	.ct_group_ops	= &cfg_group_ops,
	.ct_attrs	= cfg_attrs,
	.ct_owner	= THIS_MODULE,
};

static struct configfs_subsystem cfg_subsys = {
	.su_group = {
		.cg_item = {
			.ci_namebuf = "gpio-dev",
			.ci_type = &cfg_type,
		},
	},
};


static struct configfs_attribute pin_attr_index = {
	.ca_owner = THIS_MODULE,
	.ca_name = "index",
	.ca_mode = S_IRUGO,
};

static struct configfs_attribute pin_attr_direction = {
	.ca_owner = THIS_MODULE,
	.ca_name = "direction",
	.ca_mode = S_IRUGO | S_IWUGO,
};

static struct configfs_attribute pin_attr_irq = {
	.ca_owner = THIS_MODULE,
	.ca_name = "irq",
	.ca_mode = S_IRUGO | S_IWUGO,
};

static struct configfs_attribute *pin_attrs[] = {
	&pin_attr_index,
	&pin_attr_direction,
	&pin_attr_irq,
	NULL,
};

static inline struct gpiodev_pin *to_gpiodev_pin(struct config_group *group)
{
	return group ? container_of(group, struct gpiodev_pin, group) : NULL;
}

static ssize_t pin_show(struct config_item *item,
				      struct configfs_attribute *attr,
				      char *page)
{
	struct gpiodev_pin *pin = to_gpiodev_pin(to_config_group(item));

	/* All attributes for a pin come here */

	if (attr == &pin_attr_index)
		return sprintf(page, "%d\n", pin->index);
	else if (attr == &pin_attr_direction)
		return sprintf(page, "%d\n", pin->direction);
	else if (attr == &pin_attr_irq)
		return sprintf(page, "%d\n", pin->irq);
	else {
		printk(KERN_ERR "gpio-dev: Unexpected attribute in %s\n",
		       __func__);
		return 0;
	}
}

static ssize_t pin_store(struct config_item *item,
				 struct configfs_attribute *attr,
				 const char *page, size_t count)
{
	struct gpiodev_pin *pin = to_gpiodev_pin(to_config_group(item));
	char *p = (char *)page;
	int val;
	int ret = 0;

	val = simple_strtoul(p, &p, 0);
	if (!p || (*p && (*p != '\n')))
		return -EINVAL;

	if (attr == &pin_attr_direction) {
		pin->direction = val;

		if (val)
			gpio_direction_output(pin->pin, pin->outval);
		else
			gpio_direction_input(pin->pin);


	} else if (attr == &pin_attr_irq) {

		if (!pin->irq && val) {
			ret = request_irq(gpio_to_irq(pin->pin), irq_interrupt,
					  0, "gpio-dev", pin);
			if (ret)
				return ret;
		} else if (pin->irq && !val) {
			free_irq(gpio_to_irq(pin->pin), pin);
		}

		pin->irq = val;

		/*
		 * The values written by the user directly correspond to
		 * the values behind the IRQF_TRIGGER_* tokens.  Can we
		 * rely on this or should we explicitly check and set each
		 * possible trigger type?
		 */
		if (val)
			ret = set_irq_type(gpio_to_irq(pin->pin), val);

		if (ret)
			return -EINVAL;

	} else {
		printk(KERN_ERR "gpio-dev: Unexpected attribute in %s\n",
		       __func__);
	}

	return count;
}

static struct configfs_item_operations pin_ops = {
	.show_attribute = pin_show,
	.store_attribute = pin_store,
};

static struct config_item_type cfg_pin_type = {
	.ct_owner	= THIS_MODULE,
	.ct_attrs	= pin_attrs,
	.ct_item_ops	= &pin_ops,
};

static int __init gpiodev_probe(struct platform_device *pdev)
{
	int i, ret;
	struct gpiodev *bank = NULL;
	struct device *dev = &pdev->dev;
	struct gpiodev_pdata *pdata = dev->platform_data;

	if (!pdata) {
		printk(KERN_ERR "gpio-dev: No configuration data available; "\
			"aborting probe.");
		return -EINVAL;
	}

	bank = kzalloc(sizeof(struct gpiodev), GFP_KERNEL);
	if (!bank)
		return -ENOMEM;

	spin_lock_init(&bank->lock);
	spin_lock_init(&bank->irq_lock);

	bank->bankname = pdata->bankname;
	bank->nr_pins = pdata->nr_pins;

	bank->pins = kmalloc(bank->nr_pins * sizeof(struct gpiodev_pin),
			     GFP_KERNEL);

	if (!bank->pins) {
		ret = -ENOMEM;
		goto err_pin_alloc;
	}

	bank->irq_buf = kfifo_alloc(IRQ_BUFFER_SIZE, GFP_KERNEL,
				    &bank->irq_lock);

	if (IS_ERR(bank->irq_buf)) {
		ret = PTR_ERR(bank->irq_buf);
		goto err_irq_buf_alloc;
	}

	memcpy(bank->pins, pdata->pins,
	       bank->nr_pins * sizeof(struct gpiodev_pin));

	pr_debug("gpio-dev: initing device group %s\n", bank->bankname);

	config_group_init_type_name(&bank->group, bank->bankname,
				    &cfg_bank_type);

	bank->group.default_groups = kzalloc(sizeof(struct config_group)
					     * bank->nr_pins, GFP_KERNEL);

	if (!bank->group.default_groups) {
		ret = -ENOMEM;
		goto err_group_alloc;
	}

	for (i = 0; i < bank->nr_pins; i++) {
		pr_debug("gpio-dev: initing pin %s on bank %s\n",
			bank->pins[i].label, bank->bankname);
		config_group_init_type_name(&bank->pins[i].group,
			bank->pins[i].label, &cfg_pin_type);
		bank->group.default_groups[i] = &bank->pins[i].group;
	}

	bank->io_enabled = 0;
	bank->irq_enabled = 0;

	platform_set_drvdata(pdev, bank);

	bank->id = -1;

	/* Find first empty slot */
	spin_lock(&devices_lock);
	for (i = 0; i < MAX_NR_DEVICES; i++) {
		if (devices[i] == NULL) {
			devices[i] = bank;
			bank->id = i;
			break;
		}
	}
	spin_unlock(&devices_lock);

	if (bank->id == -1) {
		printk(KERN_ERR "gpio-dev: More than MAX_NR_DEVICES probed;"\
				" no free slots found!");
		ret = -ENODEV;
		goto err_no_slot;
	}

	return 0;

err_no_slot:
	kfree(bank->group.default_groups);
err_group_alloc:
	kfifo_free(bank->irq_buf);
err_irq_buf_alloc:
	kfree(bank->pins);
err_pin_alloc:
	kfree(bank);

	return ret;
}

static int __exit gpiodev_remove(struct platform_device *pdev)
{
	struct gpiodev *bank = platform_get_drvdata(pdev);

	spin_lock(&devices_lock);
	devices[bank->id] = NULL;
	spin_unlock(&devices_lock);

	kfifo_free(bank->irq_buf);

	kfree(bank->pins);
	kfree(bank);

	return 0;
}

static struct platform_driver gpiodev_driver = {
	.driver		= {
		.name	= "gpio-dev",
		.owner	= THIS_MODULE,
	},
	.remove		= __exit_p(gpiodev_remove),
	.probe		= gpiodev_probe,
};

static int __init gpiodev_init(void)
{
	int ret;

	spin_lock_init(&devices_lock);

	dev_class = class_create(THIS_MODULE, "gpio-dev");
	if (IS_ERR(dev_class)) {
		ret = PTR_ERR(dev_class);
		goto err_class_create;
	}

	ret = alloc_chrdev_region(&base_devt, 0, MAX_NR_DEVICES * 2, "gpio-dev");
	if (ret < 0)
		goto err_alloc_chrdev;

	pr_debug("gpio-dev: Registering configfs subsystem %s\n",
		cfg_subsys.su_group.cg_item.ci_namebuf);
	config_group_init(&cfg_subsys.su_group);
	mutex_init(&cfg_subsys.su_mutex);
	ret = configfs_register_subsystem(&cfg_subsys);
	if (ret) {
		printk(KERN_ERR "Error %d while registering subsystem %s\n",
		       ret,
		       cfg_subsys.su_group.cg_item.ci_namebuf);
		goto err_subsys;
	}


	ret = platform_driver_register(&gpiodev_driver);
	if (ret)
		goto err_dev_register;

	return 0;

err_dev_register:
	configfs_unregister_subsystem(&cfg_subsys);
err_subsys:
	unregister_chrdev_region(base_devt, MAX_NR_DEVICES);
err_alloc_chrdev:
	class_destroy(dev_class);
err_class_create:
	printk(KERN_ALERT "gpio-dev: Failed to init gpio-dev interface\n");
	return ret;
}

static void __exit gpiodev_exit(void)
{
	platform_driver_unregister(&gpiodev_driver);

	configfs_unregister_subsystem(&cfg_subsys);

	unregister_chrdev_region(base_devt, MAX_NR_DEVICES * 2);

	class_destroy(dev_class);

	printk(KERN_INFO "gpio-dev: unloaded gpio-dev\n");
}

module_init(gpiodev_init);
module_exit(gpiodev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Nizette <bn@xxxxxxxxxxxxxxx>");
MODULE_DESCRIPTION("Userspace interface to the gpio framework");

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux