/*
 * Interface for Dynamic Logical Partitioning of I/O Slots on
 * RPA-compliant PPC64 platform.
 *
 * John Rose <johnrose@austin.ibm.com>
 * October 2003
 *
 * Copyright (C) 2003 IBM.
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */
#include <linux/kobject.h>
#include <linux/string.h>
#include "pci_hotplug.h"
#include "rpadlpar.h"

#define DLPAR_KOBJ_NAME       "control"
#define ADD_SLOT_ATTR_NAME    "add_slot"
#define REMOVE_SLOT_ATTR_NAME "remove_slot"

#define MAX_DRC_NAME_LEN 64

/* Store return code of dlpar operation in attribute struct */
struct dlpar_io_attr {
	int rc;
	struct attribute attr;
	ssize_t (*store)(struct dlpar_io_attr *dlpar_attr, const char *buf,
		size_t nbytes);
};

/* Common show callback for all attrs, display the return code
 * of the dlpar op */
static ssize_t
dlpar_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{
	struct dlpar_io_attr *dlpar_attr = container_of(attr,
						struct dlpar_io_attr, attr);
	return sprintf(buf, "%d\n", dlpar_attr->rc);
}

static ssize_t
dlpar_attr_store(struct kobject * kobj, struct attribute * attr,
		 const char *buf, size_t nbytes)
{
	struct dlpar_io_attr *dlpar_attr = container_of(attr,
						struct dlpar_io_attr, attr);
	return dlpar_attr->store ?
		dlpar_attr->store(dlpar_attr, buf, nbytes) : -EIO;
}

static struct sysfs_ops dlpar_attr_sysfs_ops = {
	.show = dlpar_attr_show,
	.store = dlpar_attr_store,
};

static ssize_t add_slot_store(struct dlpar_io_attr *dlpar_attr,
				const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	char *end;

	if (nbytes > MAX_DRC_NAME_LEN)
		return 0;

	memcpy(drc_name, buf, nbytes);

	end = strchr(drc_name, '\n');
	if (!end)
		end = &drc_name[nbytes];
	*end = '\0';

	dlpar_attr->rc = dlpar_add_slot(drc_name);

	return nbytes;
}

static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr,
		 		const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	char *end;

	if (nbytes > MAX_DRC_NAME_LEN)
		return 0;

	memcpy(drc_name, buf, nbytes);

	end = strchr(drc_name, '\n');
	if (!end)
		end = &drc_name[nbytes];
	*end = '\0';

	dlpar_attr->rc = dlpar_remove_slot(drc_name);

	return nbytes;
}

static struct dlpar_io_attr add_slot_attr = {
	.rc = 0,
	.attr = { .name = ADD_SLOT_ATTR_NAME, .mode = 0644, },
	.store = add_slot_store,
};

static struct dlpar_io_attr remove_slot_attr = {
	.rc = 0,
	.attr = { .name = REMOVE_SLOT_ATTR_NAME, .mode = 0644},
	.store = remove_slot_store,
};

static struct attribute *default_attrs[] = {
	&add_slot_attr.attr,
	&remove_slot_attr.attr,
	NULL,
};

static void dlpar_io_release(struct kobject *kobj)
{
	/* noop */
	return;
}

struct kobj_type ktype_dlpar_io = {
	.release = dlpar_io_release,
	.sysfs_ops = &dlpar_attr_sysfs_ops,
	.default_attrs = default_attrs,
};

struct kset dlpar_io_kset = {
	.subsys = &pci_hotplug_slots_subsys,
	.kobj = {.name = DLPAR_KOBJ_NAME, .ktype=&ktype_dlpar_io,},
	.ktype = &ktype_dlpar_io,
};

int dlpar_sysfs_init(void)
{
	if (kset_register(&dlpar_io_kset)) {
		printk(KERN_ERR "rpadlpar_io: cannot register kset for %s\n",
				dlpar_io_kset.kobj.name);
		return -EINVAL;
	}

	return 0;
}

void dlpar_sysfs_exit(void)
{
	kset_unregister(&dlpar_io_kset);
}
