#include <linux/device.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/err.h>
#include "fs.h"

#define to_drv_attr(_attr) container_of(_attr,struct driver_attribute,attr)

#define to_drv(d) container_of(d, struct device_driver, dir)


/* driverfs ops for device attribute files */

static int
drv_attr_open(struct driver_dir_entry * dir)
{
	struct device_driver * drv = to_drv(dir);
	get_driver(drv);
	return 0;
}

static int
drv_attr_close(struct driver_dir_entry * dir)
{
	struct device_driver * drv = to_drv(dir);
	put_driver(drv);
	return 0;
}

static ssize_t
drv_attr_show(struct driver_dir_entry * dir, struct attribute * attr,
	      char * buf, size_t count, loff_t off)
{
	struct driver_attribute * drv_attr = to_drv_attr(attr);
	struct device_driver * drv = to_drv(dir);
	ssize_t ret = 0;

	if (drv_attr->show)
		ret = drv_attr->show(drv,buf,count,off);
	return ret;
}

static ssize_t
drv_attr_store(struct driver_dir_entry * dir, struct attribute * attr,
	       const char * buf, size_t count, loff_t off)
{
	struct driver_attribute * drv_attr = to_drv_attr(attr);
	struct device_driver * drv = to_drv(dir);
	ssize_t ret = 0;

	if (drv_attr->store)
		ret = drv_attr->store(drv,buf,count,off);
	return ret;
}

static struct driverfs_ops drv_attr_ops = {
	.open	= drv_attr_open,
	.close	= drv_attr_close,
	.show	= drv_attr_show,
	.store	= drv_attr_store,
};

int driver_create_file(struct device_driver * drv, struct driver_attribute * attr)
{
	int error;
	if (get_driver(drv)) {
		error = driverfs_create_file(&attr->attr,&drv->dir);
		put_driver(drv);
	} else
		error = -EINVAL;
	return error;
}

void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr)
{
	if (get_driver(drv)) {
		driverfs_remove_file(&drv->dir,attr->attr.name);
		put_driver(drv);
	}
}

/**
 * driver_make_dir - create a driverfs directory for a driver
 * @drv:	driver in question
 */
int driver_make_dir(struct device_driver * drv)
{
	drv->dir.name = drv->name;
	drv->dir.ops = &drv_attr_ops;

	return device_create_dir(&drv->dir,&drv->bus->driver_dir);
}

void driver_remove_dir(struct device_driver * drv)
{
	driverfs_remove_dir(&drv->dir);
}

EXPORT_SYMBOL(driver_create_file);
EXPORT_SYMBOL(driver_remove_file);