/*
*
* dvb_sysfs.c -- interface to the sysfs filesystem
* Copyright (C) 2004 Christian Gmeiner <christian@visual-page.de>
* Copyright (C) 2004 Eric Donohue <epd3j@hotmail.com>
*
*  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <linux/version.h>

#include "dvb_sysfs.h"

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,46)

#ifndef DVB_SYSFS_DIR
#define DVB_SYSFS_DIR "dvb"	//-> /sys/class/dvb/...
#endif

static struct class dvb_class = {
	.name = DVB_SYSFS_DIR
};

static ssize_t show_name(struct class_device *cd, char *buf)
{
	struct dvb_adapter *adap = container_of(cd, struct dvb_adapter, class_dev);
	sprintf(buf, "%s\n", adap->name);
	return strlen(buf) + 1;
}

static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL);

static ssize_t show_frontend(struct class_device *cd, char *buf)
{
	struct dvb_adapter *adap = container_of(cd, struct dvb_adapter, class_dev);
	sprintf(buf, "%s\n", adap->frontend);
	return strlen(buf) + 1;
}

static CLASS_DEVICE_ATTR(frontend, S_IRUGO, show_frontend, NULL);

static ssize_t show_devnum(struct class_device *cd, char *buf)
{
	struct dvb_device *dvbdev = container_of(cd, struct dvb_device, class_dev);

	dev_t dev = MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num, dvbdev->type, dvbdev->id));
	return print_dev_t(buf, dev);
}

static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_devnum, NULL);

static ssize_t show_adap(struct class_device *cd, char *buf)
{
	struct dvb_device *dvbdev = container_of(cd, struct dvb_device, class_dev);
	sprintf(buf, "adapter%d\n", dvbdev->adapter->num);
	return strlen(buf) + 1;
}

static CLASS_DEVICE_ATTR(adap, S_IRUGO, show_adap, NULL);


static struct class_device_attribute *dvb_adapter_attrs[] = {
	&class_device_attr_name,
	&class_device_attr_frontend,
	NULL
};

static struct class_device_attribute *dvb_device_attrs[] = {
	&class_device_attr_dev,
	&class_device_attr_adap,
	NULL
};

static void dvb_sysfs_register_adapter(struct dvb_adapter *adap)
{
	char name[64] = "";
	int i;

	adap->class_dev.class = &dvb_class;
	adap->class_dev.dev = NULL;
	adap->class_dev.class_data = adap;

	sprintf(name,"adapter%d", adap->num);
	strcpy(adap->class_dev.class_id, name);

	if(class_device_register(&adap->class_dev))
		printk(KERN_ERR "Unable to register dvb class device\n");

	for(i = 0; dvb_adapter_attrs[i]; i++)
		class_device_create_file(&(adap->class_dev), dvb_adapter_attrs[i]);
}

static void dvb_sysfs_unregister_adapter(struct dvb_adapter *adap)
{
	class_device_unregister(&(adap->class_dev));
}

static void dvb_sysfs_register_device(struct dvb_device *dvbdev)
{
	int i;
	char name[64] = "";
	struct class_device *class_dev = &(dvbdev->class_dev);

	sprintf(name,"%s%d", dnames[dvbdev->type], dvbdev->adapter->num);

	class_dev->class = &dvb_class;
	class_dev->class_data = dvbdev;
	strcpy(class_dev->class_id, name);

	kobject_init(&class_dev->kobj);
	strcpy(class_dev->kobj.name, name);
	class_dev->kobj.parent = &dvbdev->adapter->class_dev.kobj;
	class_dev->kobj.kset = dvbdev->adapter->class_dev.kobj.kset;
	class_dev->kobj.ktype = dvbdev->adapter->class_dev.kobj.ktype;
	class_dev->kobj.dentry = dvbdev->adapter->class_dev.kobj.dentry;
	kobject_register(&class_dev->kobj);

	for (i = 0; dvb_device_attrs[i]; i++)
		class_device_create_file(class_dev, dvb_device_attrs[i]);

}

static void dvb_sysfs_unregister_device(struct dvb_device *dvbdev)
{
	kobject_unregister(&dvbdev->class_dev.kobj);
}

static int dvb_sysfs_init(void)
{
	printk("DVB: Using sysfs funtions\n");

	if (class_register(&dvb_class) != 0) {
		printk("DVB: adapter class failed to register properly");
		return -1;
	}

	return 0;
}

static void dvb_sysfs_exit(void)
{
	class_unregister(&dvb_class);
}

struct dvb_registrar_s dvb_sysfs_registrar =
{
	.register_adapter	= &dvb_sysfs_register_adapter,
	.unregister_adapter	= &dvb_sysfs_unregister_adapter,
	.register_device	= &dvb_sysfs_register_device,
	.unregister_device	= &dvb_sysfs_unregister_device,
	.dvbdev_init		= &dvb_sysfs_init,
	.dvbdev_exit		= &dvb_sysfs_exit,
};

#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,46) */

struct dvb_registrar_s dvb_sysfs_registrar =
{
	.register_adapter	= NULL,
	.unregister_adapter	= NULL,
	.register_device	= NULL,
	.unregister_device	= NULL,
	.dvbdev_init		= NULL,
	.dvbdev_exit		= NULL,
};

#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,46) */
