All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Nizette <bn@niasdigital.com>
To: Joel Becker <Joel.Becker@oracle.com>
Cc: Johannes Berg <johannes@sipsolutions.net>,
	"Luis R. Rodriguez" <mcgrof@gmail.com>,
	linux-wireless <linux-wireless@vger.kernel.org>,
	linux kernel <linux-kernel@vger.kernel.org>,
	Greg KH <greg@kroah.com>, Satyam Sharma <ssatyam@cse.iitk.ac.in>,
	Felix Fietkau <nbd@openwrt.org>, Al Viro <viro@ftp.linux.org.uk>,
	"H. Peter Anvin" <hpa@kernel.org>
Subject: Re: [RFC PATCHES] Re: Is configfs the right solution for configuration based fs?
Date: Fri, 20 Jun 2008 19:22:56 +1000	[thread overview]
Message-ID: <1213953776.2336.75.camel@moss.renham> (raw)
In-Reply-To: <20080620065253.GA14238@ca-server1.us.oracle.com>

[-- Attachment #1: Type: text/plain, Size: 1598 bytes --]


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


[-- Attachment #2: gpio-dev.c --]
[-- Type: text/x-csrc, Size: 21720 bytes --]

/*
 * 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@niasdigital.com>");
MODULE_DESCRIPTION("Userspace interface to the gpio framework");

  reply	other threads:[~2008-06-20  9:22 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-06-08 21:25 Is configfs the right solution for configuration based fs? Luis R. Rodriguez
2008-06-09  2:28 ` Joel Becker
2008-06-11  9:04   ` Joel Becker
2008-06-09  9:03 ` Johannes Berg
2008-06-10  0:12   ` Ben Nizette
2008-06-10  8:01     ` Johannes Berg
2008-06-10  8:12       ` Luis R. Rodriguez
2008-06-10 22:12       ` Ben Nizette
2008-06-19  2:48     ` [RFC PATCHES] " Joel Becker
2008-06-20  6:19       ` Ben Nizette
2008-06-20  6:52         ` Joel Becker
2008-06-20  9:22           ` Ben Nizette [this message]
2008-06-20 21:37             ` Joel Becker
2008-06-21  1:03               ` Ben Nizette
2008-06-21  2:02                 ` Joel Becker
2008-06-21  8:03                   ` Ben Nizette
2008-06-21  8:44                     ` Joel Becker
2008-06-21 11:44                       ` Ben Nizette

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1213953776.2336.75.camel@moss.renham \
    --to=bn@niasdigital.com \
    --cc=Joel.Becker@oracle.com \
    --cc=greg@kroah.com \
    --cc=hpa@kernel.org \
    --cc=johannes@sipsolutions.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=mcgrof@gmail.com \
    --cc=nbd@openwrt.org \
    --cc=ssatyam@cse.iitk.ac.in \
    --cc=viro@ftp.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.