/*
 * Copyright (c) 2011 Synaptics Incorporated
 * Copyright (c) 2011 Unixphere
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/rmi.h>

static struct rmi_function_list {
	struct list_head list;
	struct rmi_function_handler *fh;
} rmi_supported_functions;

static int rmi_bus_match(struct device *dev, struct device_driver *driver)
{
	struct rmi_driver *rmi_driver;
	struct rmi_device *rmi_dev;
	struct rmi_device_platform_data *pdata;

	pr_info("in function ____%s____  \n", __func__);
	rmi_driver = to_rmi_driver(driver);
	rmi_dev = to_rmi_device(dev);
	pdata = to_rmi_platform_data(rmi_dev);

	pr_info("        rmi_driver->driver.name =  %s\n", rmi_driver->driver.name);
        pr_info("        device:rmi_device = 0x%x \n", rmi_dev);
	pr_info("             device:rmi_device:rmi_device_platform_data:driver_name = %s \n", pdata->driver_name);
        pr_info("                    rmi_device:driver = 0x%x \n", rmi_dev->driver);

	if (!strcmp(pdata->driver_name, rmi_driver->driver.name)) {
		rmi_dev->driver = rmi_driver;
		pr_info("             names match, so now rmi_device:driver = 0x%x \n",rmi_dev->driver);
		return 1;
	}
	pr_info("             names DO NOT match, so return nothing \n");

	return 0;
}

#ifdef CONFIG_PM
static int rmi_bus_suspend(struct device *dev)
{
#ifdef GENERIC_SUBSYS_PM_OPS
	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

	if (pm && pm->suspend)
		return pm->suspend(dev);
#endif

	return 0;
}

static int rmi_bus_resume(struct device *dev)
{
#ifdef GENERIC_SUBSYS_PM_OPS
	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
	pr_info("in function ____%s____  \n", __func__);

	if (pm && pm->resume)
		return pm->resume(dev);
#endif

	return 0;
}
#endif

static int rmi_bus_probe(struct device *dev)
{
	struct rmi_driver *driver;
	struct rmi_device *rmi_dev = to_rmi_device(dev);

	pr_info("in function ____%s____  \n", __func__);
	driver = rmi_dev->driver;
	if (driver && driver->probe)
		return driver->probe(rmi_dev);

	return 0;
}

static int rmi_bus_remove(struct device *dev)
{
	struct rmi_driver *driver;
	struct rmi_device *rmi_dev = to_rmi_device(dev);

	pr_info("in function ____%s____  \n", __func__);
	driver = rmi_dev->driver;
	if (driver && driver->remove)
		return driver->remove(rmi_dev);

	return 0;
}

static void rmi_bus_shutdown(struct device *dev)
{
	struct rmi_driver *driver;
	struct rmi_device *rmi_dev = to_rmi_device(dev);

	driver = rmi_dev->driver;
	if (driver && driver->shutdown)
		driver->shutdown(rmi_dev);
}

static SIMPLE_DEV_PM_OPS(rmi_bus_pm_ops,
			 rmi_bus_suspend, rmi_bus_resume);

struct bus_type rmi_bus_type = {
	.name		= "rmi",
	.match		= rmi_bus_match,
	.probe		= rmi_bus_probe,
	.remove		= rmi_bus_remove,
	.shutdown	= rmi_bus_shutdown,
	.pm		= &rmi_bus_pm_ops
};

int rmi_register_phys_device(struct rmi_phys_device *phys)
{
	static int phys_device_num;
	struct rmi_device_platform_data *pdata = phys->dev->platform_data;
	struct rmi_device *rmi_dev;

	pr_info("in function ____%s____  \n", __func__);

	if (!pdata) {
		dev_err(phys->dev, "no platform data!\n");
		return -EINVAL;
	}

	rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL);
	if (!rmi_dev)
		return -ENOMEM;

	rmi_dev->phys = phys;
	rmi_dev->dev.bus = &rmi_bus_type;
	dev_set_name(&rmi_dev->dev, "sensor%02d", phys_device_num++);

	phys->rmi_dev = rmi_dev;
	pr_info("                 registering physical device:\n");
	pr_info("                 dev.init_name = \n", rmi_dev->dev.init_name);
	pr_info("                 dev.bus->name = \n", rmi_dev->dev.bus->name);
	return device_register(&rmi_dev->dev);
}
EXPORT_SYMBOL(rmi_register_phys_device);

void rmi_unregister_phys_device(struct rmi_phys_device *phys)
{
	struct rmi_device *rmi_dev = phys->rmi_dev;
	pr_info("in function ____%s____  \n", __func__);

	device_unregister(&rmi_dev->dev);
	kfree(rmi_dev);
}
EXPORT_SYMBOL(rmi_unregister_phys_device);

int rmi_register_driver(struct rmi_driver *driver)
{
	pr_info("in function ____%s____  \n", __func__);
	driver->driver.bus = &rmi_bus_type;
	return driver_register(&driver->driver);
}
EXPORT_SYMBOL(rmi_register_driver);

static int __rmi_driver_remove(struct device *dev, void *data)
{
	struct rmi_driver *driver = data;
	struct rmi_device *rmi_dev = to_rmi_device(dev);

	if (rmi_dev->driver == driver)
		rmi_dev->driver = NULL;

	return 0;
}

void rmi_unregister_driver(struct rmi_driver *driver)
{
	bus_for_each_dev(&rmi_bus_type, NULL, driver, __rmi_driver_remove);
	driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(rmi_unregister_driver);

static int __rmi_bus_fh_add(struct device *dev, void *data)
{
	struct rmi_driver *driver;
	struct rmi_device *rmi_dev = to_rmi_device(dev);
	pr_info("in function ____%s____  \n", __func__);

	driver = rmi_dev->driver;
	if (driver && driver->fh_add)
		driver->fh_add(rmi_dev, data);

	return 0;
}

int rmi_register_function_driver(struct rmi_function_handler *fh)
{
	struct rmi_function_list *entry;
	struct rmi_function_handler *fh_dup;

	fh_dup = rmi_get_function_handler(fh->func);
	if (fh_dup) {
		pr_err("%s: function f%.2x already registered!\n", __func__,
			fh->func);
		return -EINVAL;
	}

	entry = kzalloc(sizeof(struct rmi_function_list), GFP_KERNEL);
	if (!entry)
		return -ENOMEM;

	entry->fh = fh;
	list_add_tail(&entry->list, &rmi_supported_functions.list);

	/* notify devices of the new function handler */
	bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_add);

	return 0;
}
EXPORT_SYMBOL(rmi_register_function_driver);

static int __rmi_bus_fh_remove(struct device *dev, void *data)
{
	struct rmi_driver *driver;
	struct rmi_device *rmi_dev = to_rmi_device(dev);

	pr_info("in function ____%s____  \n", __func__);
	driver = rmi_dev->driver;
	if (driver && driver->fh_remove)
		driver->fh_remove(rmi_dev, data);

	return 0;
}

void rmi_unregister_function_driver(struct rmi_function_handler *fh)
{
	struct rmi_function_list *entry, *n;
	pr_info("in function ____%s____  \n", __func__);

	/* notify devices of the removal of the function handler */
	bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_remove);

	list_for_each_entry_safe(entry, n, &rmi_supported_functions.list, list)
		if (entry->fh->func == fh->func) {
			list_del(&entry->list);
			kfree(entry);
		}
}
EXPORT_SYMBOL(rmi_unregister_function_driver);

struct rmi_function_handler *rmi_get_function_handler(int id)
{
	struct rmi_function_list *entry;
	pr_info("in function ____%s____  \n", __func__);

	list_for_each_entry(entry, &rmi_supported_functions.list, list)
		if (entry->fh->func == id)
			return entry->fh;

	return NULL;
}
EXPORT_SYMBOL(rmi_get_function_handler);

static int __init rmi_bus_init(void)
{
	int error;

	pr_info("in function ____%s____  \n", __func__);
	INIT_LIST_HEAD(&rmi_supported_functions.list);

	error = bus_register(&rmi_bus_type);
	if (error < 0) {
		pr_err("%s: error registering the RMI bus: %d\n", __func__,
		       error);
		return error;
	}
	pr_info("%s: successfully registered RMI bus.\n", __func__);

	return 0;
}

static void __exit rmi_bus_exit(void)
{
	struct rmi_function_list *entry, *n;
	pr_info("in function ____%s____  \n", __func__);

	list_for_each_entry_safe(entry, n, &rmi_supported_functions.list,
				 list) {
		list_del(&entry->list);
		kfree(entry);
	}

	bus_unregister(&rmi_bus_type);
}

module_init(rmi_bus_init);
module_exit(rmi_bus_exit);

MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
MODULE_DESCRIPTION("RMI bus");
MODULE_LICENSE("GPL");
