aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/fw-ohci.h
blob: dec4f04e6b245c2a89d4c8c986f55d5e6dae987e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#ifndef __fw_ohci_h
#define __fw_ohci_h

/* OHCI register map */

#define OHCI1394_Version                      0x000
#define OHCI1394_GUID_ROM                     0x004
#define OHCI1394_ATRetries                    0x008
#define OHCI1394_CSRData                      0x00C
#define OHCI1394_CSRCompareData               0x010
#define OHCI1394_CSRControl                   0x014
#define OHCI1394_ConfigROMhdr                 0x018
#define OHCI1394_BusID                        0x01C
#define OHCI1394_BusOptions                   0x020
#define OHCI1394_GUIDHi                       0x024
#define OHCI1394_GUIDLo                       0x028
#define OHCI1394_ConfigROMmap                 0x034
#define OHCI1394_PostedWriteAddressLo         0x038
#define OHCI1394_PostedWriteAddressHi         0x03C
#define OHCI1394_VendorID                     0x040
#define OHCI1394_HCControlSet                 0x050
#define OHCI1394_HCControlClear               0x054
#define  OHCI1394_HCControl_BIBimageValid	0x80000000
#define  OHCI1394_HCControl_noByteSwapData	0x40000000
#define  OHCI1394_HCControl_programPhyEnable	0x00800000
#define  OHCI1394_HCControl_aPhyEnhanceEnable	0x00400000
#define  OHCI1394_HCControl_LPS			0x00080000
#define  OHCI1394_HCControl_postedWriteEnable	0x00040000
#define  OHCI1394_HCControl_linkEnable		0x00020000
#define  OHCI1394_HCControl_softReset		0x00010000
#define OHCI1394_SelfIDBuffer                 0x064
#define OHCI1394_SelfIDCount                  0x068
#define OHCI1394_IRMultiChanMaskHiSet         0x070
#define OHCI1394_IRMultiChanMaskHiClear       0x074
#define OHCI1394_IRMultiChanMaskLoSet         0x078
#define OHCI1394_IRMultiChanMaskLoClear       0x07C
#define OHCI1394_IntEventSet                  0x080
#define OHCI1394_IntEventClear                0x084
#define OHCI1394_IntMaskSet                   0x088
#define OHCI1394_IntMaskClear                 0x08C
#define OHCI1394_IsoXmitIntEventSet           0x090
#define OHCI1394_IsoXmitIntEventClear         0x094
#define OHCI1394_IsoXmitIntMaskSet            0x098
#define OHCI1394_IsoXmitIntMaskClear          0x09C
#define OHCI1394_IsoRecvIntEventSet           0x0A0
#define OHCI1394_IsoRecvIntEventClear         0x0A4
#define OHCI1394_IsoRecvIntMaskSet            0x0A8
#define OHCI1394_IsoRecvIntMaskClear          0x0AC
#define OHCI1394_InitialBandwidthAvailable    0x0B0
#define OHCI1394_InitialChannelsAvailableHi   0x0B4
#define OHCI1394_InitialChannelsAvailableLo   0x0B8
#define OHCI1394_FairnessControl              0x0DC
#define OHCI1394_LinkControlSet               0x0E0
#define OHCI1394_LinkControlClear             0x0E4
#define   OHCI1394_LinkControl_rcvSelfID	(1 << 9)
#define   OHCI1394_LinkControl_rcvPhyPkt	(1 << 10)
#define   OHCI1394_LinkControl_cycleTimerEnable	(1 << 20)
#define   OHCI1394_LinkControl_cycleMaster	(1 << 21)
#define   OHCI1394_LinkControl_cycleSource	(1 << 22)
#define OHCI1394_NodeID                       0x0E8
#define   OHCI1394_NodeID_idValid             0x80000000
#define   OHCI1394_NodeID_nodeNumber          0x0000003f
#define   OHCI1394_NodeID_busNumber           0x0000ffc0
#define OHCI1394_PhyControl                   0x0EC
#define   OHCI1394_PhyControl_Read(addr)	(((addr) << 8) | 0x00008000)
#define   OHCI1394_PhyControl_ReadDone		0x80000000
#define   OHCI1394_PhyControl_ReadData(r)	(((r) & 0x00ff0000) >> 16)
#define   OHCI1394_PhyControl_Write(addr, data)	(((addr) << 8) | (data) | 0x00004000)
#define   OHCI1394_PhyControl_WriteDone		0x00004000
#define OHCI1394_IsochronousCycleTimer        0x0F0
#define OHCI1394_AsReqFilterHiSet             0x100
#define OHCI1394_AsReqFilterHiClear           0x104
#define OHCI1394_AsReqFilterLoSet             0x108
#define OHCI1394_AsReqFilterLoClear           0x10C
#define OHCI1394_PhyReqFilterHiSet            0x110
#define OHCI1394_PhyReqFilterHiClear          0x114
#define OHCI1394_PhyReqFilterLoSet            0x118
#define OHCI1394_PhyReqFilterLoClear          0x11C
#define OHCI1394_PhyUpperBound                0x120

#define OHCI1394_AsReqTrContextBase           0x180
#define OHCI1394_AsReqTrContextControlSet     0x180
#define OHCI1394_AsReqTrContextControlClear   0x184
#define OHCI1394_AsReqTrCommandPtr            0x18C

#define OHCI1394_AsRspTrContextBase           0x1A0
#define OHCI1394_AsRspTrContextControlSet     0x1A0
#define OHCI1394_AsRspTrContextControlClear   0x1A4
#define OHCI1394_AsRspTrCommandPtr            0x1AC

#define OHCI1394_AsReqRcvContextBase          0x1C0
#define OHCI1394_AsReqRcvContextControlSet    0x1C0
#define OHCI1394_AsReqRcvContextControlClear  0x1C4
#define OHCI1394_AsReqRcvCommandPtr           0x1CC

#define OHCI1394_AsRspRcvContextBase          0x1E0
#define OHCI1394_AsRspRcvContextControlSet    0x1E0
#define OHCI1394_AsRspRcvContextControlClear  0x1E4
#define OHCI1394_AsRspRcvCommandPtr           0x1EC

/* Isochronous transmit registers */
#define OHCI1394_IsoXmitContextBase(n)           (0x200 + 16 * (n))
#define OHCI1394_IsoXmitContextControlSet(n)     (0x200 + 16 * (n))
#define OHCI1394_IsoXmitContextControlClear(n)   (0x204 + 16 * (n))
#define OHCI1394_IsoXmitCommandPtr(n)            (0x20C + 16 * (n))

/* Isochronous receive registers */
#define OHCI1394_IsoRcvContextBase(n)         (0x400 + 32 * (n))
#define OHCI1394_IsoRcvContextControlSet(n)   (0x400 + 32 * (n))
#define OHCI1394_IsoRcvContextControlClear(n) (0x404 + 32 * (n))
#define OHCI1394_IsoRcvCommandPtr(n)          (0x40C + 32 * (n))
#define OHCI1394_IsoRcvContextMatch(n)        (0x410 + 32 * (n))

/* Interrupts Mask/Events */
#define OHCI1394_reqTxComplete		0x00000001
#define OHCI1394_respTxComplete		0x00000002
#define OHCI1394_ARRQ			0x00000004
#define OHCI1394_ARRS			0x00000008
#define OHCI1394_RQPkt			0x00000010
#define OHCI1394_RSPkt			0x00000020
#define OHCI1394_isochTx		0x00000040
#define OHCI1394_isochRx		0x00000080
#define OHCI1394_postedWriteErr		0x00000100
#define OHCI1394_lockRespErr		0x00000200
#define OHCI1394_selfIDComplete		0x00010000
#define OHCI1394_busReset		0x00020000
#define OHCI1394_phy			0x00080000
#define OHCI1394_cycleSynch		0x00100000
#define OHCI1394_cycle64Seconds		0x00200000
#define OHCI1394_cycleLost		0x00400000
#define OHCI1394_cycleInconsistent	0x00800000
#define OHCI1394_unrecoverableError	0x01000000
#define OHCI1394_cycleTooLong		0x02000000
#define OHCI1394_phyRegRcvd		0x04000000
#define OHCI1394_masterIntEnable	0x80000000

#define OHCI1394_evt_no_status		0x0
#define OHCI1394_evt_long_packet	0x2
#define OHCI1394_evt_missing_ack	0x3
#define OHCI1394_evt_underrun		0x4
#define OHCI1394_evt_overrun		0x5
#define OHCI1394_evt_descriptor_read	0x6
#define OHCI1394_evt_data_read		0x7
#define OHCI1394_evt_data_write		0x8
#define OHCI1394_evt_bus_reset		0x9
#define OHCI1394_evt_timeout		0xa
#define OHCI1394_evt_tcode_err		0xb
#define OHCI1394_evt_reserved_b		0xc
#define OHCI1394_evt_reserved_c		0xd
#define OHCI1394_evt_unknown		0xe
#define OHCI1394_evt_flushed		0xf

#define OHCI1394_phy_tcode		0xe

#endif /* __fw_ohci_h */
ef='#n494'>494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
/*
 * class.c - basic device class management
 *
 * Copyright (c) 2002-3 Patrick Mochel
 * Copyright (c) 2002-3 Open Source Development Labs
 * Copyright (c) 2003-2004 Greg Kroah-Hartman
 * Copyright (c) 2003-2004 IBM Corp.
 *
 * This file is released under the GPLv2
 *
 */

#include <linux/config.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/kdev_t.h>
#include <linux/err.h>
#include <linux/slab.h>
#include "base.h"

#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)

static ssize_t
class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{
	struct class_attribute * class_attr = to_class_attr(attr);
	struct class * dc = to_class(kobj);
	ssize_t ret = -EIO;

	if (class_attr->show)
		ret = class_attr->show(dc, buf);
	return ret;
}

static ssize_t
class_attr_store(struct kobject * kobj, struct attribute * attr,
		 const char * buf, size_t count)
{
	struct class_attribute * class_attr = to_class_attr(attr);
	struct class * dc = to_class(kobj);
	ssize_t ret = -EIO;

	if (class_attr->store)
		ret = class_attr->store(dc, buf, count);
	return ret;
}

static void class_release(struct kobject * kobj)
{
	struct class *class = to_class(kobj);

	pr_debug("class '%s': release.\n", class->name);

	if (class->class_release)
		class->class_release(class);
	else
		pr_debug("class '%s' does not have a release() function, "
			 "be careful\n", class->name);
}

static struct sysfs_ops class_sysfs_ops = {
	.show	= class_attr_show,
	.store	= class_attr_store,
};

static struct kobj_type ktype_class = {
	.sysfs_ops	= &class_sysfs_ops,
	.release	= class_release,
};

/* Hotplug events for classes go to the class_obj subsys */
static decl_subsys(class, &ktype_class, NULL);


int class_create_file(struct class * cls, const struct class_attribute * attr)
{
	int error;
	if (cls) {
		error = sysfs_create_file(&cls->subsys.kset.kobj, &attr->attr);
	} else
		error = -EINVAL;
	return error;
}

void class_remove_file(struct class * cls, const struct class_attribute * attr)
{
	if (cls)
		sysfs_remove_file(&cls->subsys.kset.kobj, &attr->attr);
}

static struct class *class_get(struct class *cls)
{
	if (cls)
		return container_of(subsys_get(&cls->subsys), struct class, subsys);
	return NULL;
}

static void class_put(struct class * cls)
{
	if (cls)
		subsys_put(&cls->subsys);
}


static int add_class_attrs(struct class * cls)
{
	int i;
	int error = 0;

	if (cls->class_attrs) {
		for (i = 0; attr_name(cls->class_attrs[i]); i++) {
			error = class_create_file(cls,&cls->class_attrs[i]);
			if (error)
				goto Err;
		}
	}
 Done:
	return error;
 Err:
	while (--i >= 0)
		class_remove_file(cls,&cls->class_attrs[i]);
	goto Done;
}

static void remove_class_attrs(struct class * cls)
{
	int i;

	if (cls->class_attrs) {
		for (i = 0; attr_name(cls->class_attrs[i]); i++)
			class_remove_file(cls,&cls->class_attrs[i]);
	}
}

int class_register(struct class * cls)
{
	int error;

	pr_debug("device class '%s': registering\n", cls->name);

	INIT_LIST_HEAD(&cls->children);
	INIT_LIST_HEAD(&cls->devices);
	INIT_LIST_HEAD(&cls->interfaces);
	init_MUTEX(&cls->sem);
	error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
	if (error)
		return error;

	subsys_set_kset(cls, class_subsys);

	error = subsystem_register(&cls->subsys);
	if (!error) {
		error = add_class_attrs(class_get(cls));
		class_put(cls);
	}
	return error;
}

void class_unregister(struct class * cls)
{
	pr_debug("device class '%s': unregistering\n", cls->name);
	remove_class_attrs(cls);
	subsystem_unregister(&cls->subsys);
}

static void class_create_release(struct class *cls)
{
	pr_debug("%s called for %s\n", __FUNCTION__, cls->name);
	kfree(cls);
}

static void class_device_create_release(struct class_device *class_dev)
{
	pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id);
	kfree(class_dev);
}

/* needed to allow these devices to have parent class devices */
static int class_device_create_uevent(struct class_device *class_dev,
				       char **envp, int num_envp,
				       char *buffer, int buffer_size)
{
	pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id);
	return 0;
}

/**
 * class_create - create a struct class structure
 * @owner: pointer to the module that is to "own" this struct class
 * @name: pointer to a string for the name of this class.
 *
 * This is used to create a struct class pointer that can then be used
 * in calls to class_device_create().
 *
 * Note, the pointer created here is to be destroyed when finished by
 * making a call to class_destroy().
 */
struct class *class_create(struct module *owner, char *name)
{
	struct class *cls;
	int retval;

	cls = kzalloc(sizeof(*cls), GFP_KERNEL);
	if (!cls) {
		retval = -ENOMEM;
		goto error;
	}

	cls->name = name;
	cls->owner = owner;
	cls->class_release = class_create_release;
	cls->release = class_device_create_release;

	retval = class_register(cls);
	if (retval)
		goto error;

	return cls;

error:
	kfree(cls);
	return ERR_PTR(retval);
}

/**
 * class_destroy - destroys a struct class structure
 * @cs: pointer to the struct class that is to be destroyed
 *
 * Note, the pointer to be destroyed must have been created with a call
 * to class_create().
 */
void class_destroy(struct class *cls)
{
	if ((cls == NULL) || (IS_ERR(cls)))
		return;

	class_unregister(cls);
}

/* Class Device Stuff */

int class_device_create_file(struct class_device * class_dev,
			     const struct class_device_attribute * attr)
{
	int error = -EINVAL;
	if (class_dev)
		error = sysfs_create_file(&class_dev->kobj, &attr->attr);
	return error;
}

void class_device_remove_file(struct class_device * class_dev,
			      const struct class_device_attribute * attr)
{
	if (class_dev)
		sysfs_remove_file(&class_dev->kobj, &attr->attr);
}

int class_device_create_bin_file(struct class_device *class_dev,
				 struct bin_attribute *attr)
{
	int error = -EINVAL;
	if (class_dev)
		error = sysfs_create_bin_file(&class_dev->kobj, attr);
	return error;
}

void class_device_remove_bin_file(struct class_device *class_dev,
				  struct bin_attribute *attr)
{
	if (class_dev)
		sysfs_remove_bin_file(&class_dev->kobj, attr);
}

static ssize_t
class_device_attr_show(struct kobject * kobj, struct attribute * attr,
		       char * buf)
{
	struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr);
	struct class_device * cd = to_class_dev(kobj);
	ssize_t ret = 0;

	if (class_dev_attr->show)
		ret = class_dev_attr->show(cd, buf);
	return ret;
}

static ssize_t
class_device_attr_store(struct kobject * kobj, struct attribute * attr,
			const char * buf, size_t count)
{
	struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr);
	struct class_device * cd = to_class_dev(kobj);
	ssize_t ret = 0;

	if (class_dev_attr->store)
		ret = class_dev_attr->store(cd, buf, count);
	return ret;
}

static struct sysfs_ops class_dev_sysfs_ops = {
	.show	= class_device_attr_show,
	.store	= class_device_attr_store,
};

static void class_dev_release(struct kobject * kobj)
{
	struct class_device *cd = to_class_dev(kobj);
	struct class * cls = cd->class;

	pr_debug("device class '%s': release.\n", cd->class_id);

	kfree(cd->devt_attr);
	cd->devt_attr = NULL;

	if (cd->release)
		cd->release(cd);
	else if (cls->release)
		cls->release(cd);
	else {
		printk(KERN_ERR "Class Device '%s' does not have a release() function, "
			"it is broken and must be fixed.\n",
			cd->class_id);
		WARN_ON(1);
	}
}

static struct kobj_type ktype_class_device = {
	.sysfs_ops	= &class_dev_sysfs_ops,
	.release	= class_dev_release,
};

static int class_uevent_filter(struct kset *kset, struct kobject *kobj)
{
	struct kobj_type *ktype = get_ktype(kobj);

	if (ktype == &ktype_class_device) {
		struct class_device *class_dev = to_class_dev(kobj);
		if (class_dev->class)
			return 1;
	}
	return 0;
}

static const char *class_uevent_name(struct kset *kset, struct kobject *kobj)
{
	struct class_device *class_dev = to_class_dev(kobj);

	return class_dev->class->name;
}

static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
			 int num_envp, char *buffer, int buffer_size)
{
	struct class_device *class_dev = to_class_dev(kobj);
	int i = 0;
	int length = 0;
	int retval = 0;

	pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);

	if (class_dev->dev) {
		/* add physical device, backing this device  */
		struct device *dev = class_dev->dev;
		char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);

		add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
			       &length, "PHYSDEVPATH=%s", path);
		kfree(path);

		if (dev->bus)
			add_uevent_var(envp, num_envp, &i,
				       buffer, buffer_size, &length,
				       "PHYSDEVBUS=%s", dev->bus->name);

		if (dev->driver)
			add_uevent_var(envp, num_envp, &i,
				       buffer, buffer_size, &length,
				       "PHYSDEVDRIVER=%s", dev->driver->name);
	}

	if (MAJOR(class_dev->devt)) {
		add_uevent_var(envp, num_envp, &i,
			       buffer, buffer_size, &length,
			       "MAJOR=%u", MAJOR(class_dev->devt));

		add_uevent_var(envp, num_envp, &i,
			       buffer, buffer_size, &length,
			       "MINOR=%u", MINOR(class_dev->devt));
	}

	/* terminate, set to next free slot, shrink available space */
	envp[i] = NULL;
	envp = &envp[i];
	num_envp -= i;
	buffer = &buffer[length];
	buffer_size -= length;

	if (class_dev->uevent) {
		/* have the class device specific function add its stuff */
		retval = class_dev->uevent(class_dev, envp, num_envp,
					    buffer, buffer_size);
		if (retval)
			pr_debug("class_dev->uevent() returned %d\n", retval);
	} else if (class_dev->class->uevent) {
		/* have the class specific function add its stuff */
		retval = class_dev->class->uevent(class_dev, envp, num_envp,
						   buffer, buffer_size);
		if (retval)
			pr_debug("class->uevent() returned %d\n", retval);
	}

	return retval;
}

static struct kset_uevent_ops class_uevent_ops = {
	.filter =	class_uevent_filter,
	.name =		class_uevent_name,
	.uevent =	class_uevent,
};

static decl_subsys(class_obj, &ktype_class_device, &class_uevent_ops);


static int class_device_add_attrs(struct class_device * cd)
{
	int i;
	int error = 0;
	struct class * cls = cd->class;

	if (cls->class_dev_attrs) {
		for (i = 0; attr_name(cls->class_dev_attrs[i]); i++) {
			error = class_device_create_file(cd,
							 &cls->class_dev_attrs[i]);
			if (error)
				goto Err;
		}
	}
 Done:
	return error;
 Err:
	while (--i >= 0)
		class_device_remove_file(cd,&cls->class_dev_attrs[i]);
	goto Done;
}

static void class_device_remove_attrs(struct class_device * cd)
{
	int i;
	struct class * cls = cd->class;

	if (cls->class_dev_attrs) {
		for (i = 0; attr_name(cls->class_dev_attrs[i]); i++)
			class_device_remove_file(cd,&cls->class_dev_attrs[i]);
	}
}

static int class_device_add_groups(struct class_device * cd)
{
	int i;
	int error = 0;

	if (cd->groups) {
		for (i = 0; cd->groups[i]; i++) {
			error = sysfs_create_group(&cd->kobj, cd->groups[i]);
			if (error) {
				while (--i >= 0)
					sysfs_remove_group(&cd->kobj, cd->groups[i]);
				goto out;
			}
		}
	}
out:
	return error;
}

static void class_device_remove_groups(struct class_device * cd)
{
	int i;
	if (cd->groups) {
		for (i = 0; cd->groups[i]; i++) {
			sysfs_remove_group(&cd->kobj, cd->groups[i]);
		}
	}
}

static ssize_t show_dev(struct class_device *class_dev, char *buf)
{
	return print_dev_t(buf, class_dev->devt);
}

static ssize_t store_uevent(struct class_device *class_dev,
			    const char *buf, size_t count)
{
	kobject_uevent(&class_dev->kobj, KOBJ_ADD);
	return count;
}

void class_device_initialize(struct class_device *class_dev)
{
	kobj_set_kset_s(class_dev, class_obj_subsys);
	kobject_init(&class_dev->kobj);
	INIT_LIST_HEAD(&class_dev->node);
}

char *make_class_name(const char *name, struct kobject *kobj)
{
	char *class_name;
	int size;

	size = strlen(name) + strlen(kobject_name(kobj)) + 2;

	class_name = kmalloc(size, GFP_KERNEL);
	if (!class_name)
		return ERR_PTR(-ENOMEM);

	strcpy(class_name, name);
	strcat(class_name, ":");
	strcat(class_name, kobject_name(kobj));
	return class_name;
}

int class_device_add(struct class_device *class_dev)
{
	struct class *parent_class = NULL;
	struct class_device *parent_class_dev = NULL;
	struct class_interface *class_intf;
	char *class_name = NULL;
	int error = -EINVAL;

	class_dev = class_device_get(class_dev);
	if (!class_dev)
		return -EINVAL;

	if (!strlen(class_dev->class_id))
		goto out1;

	parent_class = class_get(class_dev->class);
	if (!parent_class)
		goto out1;

	parent_class_dev = class_device_get(class_dev->parent);

	pr_debug("CLASS: registering class device: ID = '%s'\n",
		 class_dev->class_id);

	/* first, register with generic layer. */
	error = kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
	if (error)
		goto out2;

	if (parent_class_dev)
		class_dev->kobj.parent = &parent_class_dev->kobj;
	else
		class_dev->kobj.parent = &parent_class->subsys.kset.kobj;

	error = kobject_add(&class_dev->kobj);
	if (error)
		goto out2;

	/* add the needed attributes to this device */
	sysfs_create_link(&class_dev->kobj, &parent_class->subsys.kset.kobj, "subsystem");
	class_dev->uevent_attr.attr.name = "uevent";
	class_dev->uevent_attr.attr.mode = S_IWUSR;
	class_dev->uevent_attr.attr.owner = parent_class->owner;
	class_dev->uevent_attr.store = store_uevent;
	error = class_device_create_file(class_dev, &class_dev->uevent_attr);
	if (error)
		goto out3;

	if (MAJOR(class_dev->devt)) {
		struct class_device_attribute *attr;
		attr = kzalloc(sizeof(*attr), GFP_KERNEL);
		if (!attr) {
			error = -ENOMEM;
			goto out4;
		}
		attr->attr.name = "dev";
		attr->attr.mode = S_IRUGO;
		attr->attr.owner = parent_class->owner;
		attr->show = show_dev;
		error = class_device_create_file(class_dev, attr);
		if (error) {
			kfree(attr);
			goto out4;
		}

		class_dev->devt_attr = attr;
	}

	error = class_device_add_attrs(class_dev);
	if (error)
		goto out5;

	if (class_dev->dev) {
		class_name = make_class_name(class_dev->class->name,
					     &class_dev->kobj);
		error = sysfs_create_link(&class_dev->kobj,
					  &class_dev->dev->kobj, "device");
		if (error)
			goto out6;
		error = sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
					  class_name);
		if (error)
			goto out7;
	}

	error = class_device_add_groups(class_dev);
	if (error)
		goto out8;

	kobject_uevent(&class_dev->kobj, KOBJ_ADD);

	/* notify any interfaces this device is now here */
	down(&parent_class->sem);
	list_add_tail(&class_dev->node, &parent_class->children);
	list_for_each_entry(class_intf, &parent_class->interfaces, node) {
		if (class_intf->add)
			class_intf->add(class_dev, class_intf);
	}
	up(&parent_class->sem);

	goto out1;

 out8:
	if (class_dev->dev)
		sysfs_remove_link(&class_dev->kobj, class_name);
 out7:
	if (class_dev->dev)
		sysfs_remove_link(&class_dev->kobj, "device");
 out6:
	class_device_remove_attrs(class_dev);
 out5:
	if (class_dev->devt_attr)
		class_device_remove_file(class_dev, class_dev->devt_attr);
 out4:
	class_device_remove_file(class_dev, &class_dev->uevent_attr);
 out3:
	kobject_del(&class_dev->kobj);
 out2:
	if(parent_class_dev)
		class_device_put(parent_class_dev);
	class_put(parent_class);
 out1:
	class_device_put(class_dev);
	kfree(class_name);
	return error;
}

int class_device_register(struct class_device *class_dev)
{
	class_device_initialize(class_dev);
	return class_device_add(class_dev);
}

/**
 * class_device_create - creates a class device and registers it with sysfs
 * @cs: pointer to the struct class that this device should be registered to.
 * @parent: pointer to the parent struct class_device of this new device, if any.
 * @dev: the dev_t for the char device to be added.
 * @device: a pointer to a struct device that is assiociated with this class device.
 * @fmt: string for the class device's name
 *
 * This function can be used by char device classes.  A struct
 * class_device will be created in sysfs, registered to the specified
 * class.
 * A "dev" file will be created, showing the dev_t for the device, if
 * the dev_t is not 0,0.
 * If a pointer to a parent struct class_device is passed in, the newly
 * created struct class_device will be a child of that device in sysfs.
 * The pointer to the struct class_device will be returned from the
 * call.  Any further sysfs files that might be required can be created
 * using this pointer.
 *
 * Note: the struct class passed to this function must have previously
 * been created with a call to class_create().
 */
struct class_device *class_device_create(struct class *cls,
					 struct class_device *parent,
					 dev_t devt,
					 struct device *device, char *fmt, ...)
{
	va_list args;
	struct class_device *class_dev = NULL;
	int retval = -ENODEV;

	if (cls == NULL || IS_ERR(cls))
		goto error;

	class_dev = kzalloc(sizeof(*class_dev), GFP_KERNEL);
	if (!class_dev) {
		retval = -ENOMEM;
		goto error;
	}

	class_dev->devt = devt;
	class_dev->dev = device;
	class_dev->class = cls;
	class_dev->parent = parent;
	class_dev->release = class_device_create_release;
	class_dev->uevent = class_device_create_uevent;

	va_start(args, fmt);
	vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args);
	va_end(args);
	retval = class_device_register(class_dev);
	if (retval)
		goto error;

	return class_dev;

error:
	kfree(class_dev);
	return ERR_PTR(retval);
}

void class_device_del(struct class_device *class_dev)
{
	struct class *parent_class = class_dev->class;
	struct class_device *parent_device = class_dev->parent;
	struct class_interface *class_intf;
	char *class_name = NULL;

	if (parent_class) {
		down(&parent_class->sem);
		list_del_init(&class_dev->node);
		list_for_each_entry(class_intf, &parent_class->interfaces, node)
			if (class_intf->remove)
				class_intf->remove(class_dev, class_intf);
		up(&parent_class->sem);
	}

	if (class_dev->dev) {
		class_name = make_class_name(class_dev->class->name,
					     &class_dev->kobj);
		sysfs_remove_link(&class_dev->kobj, "device");
		sysfs_remove_link(&class_dev->dev->kobj, class_name);
	}
	sysfs_remove_link(&class_dev->kobj, "subsystem");
	class_device_remove_file(class_dev, &class_dev->uevent_attr);
	if (class_dev->devt_attr)
		class_device_remove_file(class_dev, class_dev->devt_attr);
	class_device_remove_attrs(class_dev);
	class_device_remove_groups(class_dev);

	kobject_uevent(&class_dev->kobj, KOBJ_REMOVE);
	kobject_del(&class_dev->kobj);

	class_device_put(parent_device);
	class_put(parent_class);
	kfree(class_name);
}

void class_device_unregister(struct class_device *class_dev)
{
	pr_debug("CLASS: Unregistering class device. ID = '%s'\n",
		 class_dev->class_id);
	class_device_del(class_dev);
	class_device_put(class_dev);
}

/**
 * class_device_destroy - removes a class device that was created with class_device_create()
 * @cls: the pointer to the struct class that this device was registered * with.
 * @dev: the dev_t of the device that was previously registered.
 *
 * This call unregisters and cleans up a class device that was created with a
 * call to class_device_create()
 */
void class_device_destroy(struct class *cls, dev_t devt)
{
	struct class_device *class_dev = NULL;
	struct class_device *class_dev_tmp;

	down(&cls->sem);
	list_for_each_entry(class_dev_tmp, &cls->children, node) {
		if (class_dev_tmp->devt == devt) {
			class_dev = class_dev_tmp;
			break;
		}
	}
	up(&cls->sem);

	if (class_dev)
		class_device_unregister(class_dev);
}

int class_device_rename(struct class_device *class_dev, char *new_name)
{
	int error = 0;
	char *old_class_name = NULL, *new_class_name = NULL;

	class_dev = class_device_get(class_dev);
	if (!class_dev)
		return -EINVAL;

	pr_debug("CLASS: renaming '%s' to '%s'\n", class_dev->class_id,
		 new_name);

	if (class_dev->dev)
		old_class_name = make_class_name(class_dev->class->name,
						 &class_dev->kobj);

	strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN);

	error = kobject_rename(&class_dev->kobj, new_name);

	if (class_dev->dev) {
		new_class_name = make_class_name(class_dev->class->name,
						 &class_dev->kobj);
		sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
				  new_class_name);
		sysfs_remove_link(&class_dev->dev->kobj, old_class_name);
	}
	class_device_put(class_dev);

	kfree(old_class_name);
	kfree(new_class_name);

	return error;
}

struct class_device * class_device_get(struct class_device *class_dev)
{
	if (class_dev)
		return to_class_dev(kobject_get(&class_dev->kobj));
	return NULL;
}

void class_device_put(struct class_device *class_dev)
{
	if (class_dev)
		kobject_put(&class_dev->kobj);
}


int class_interface_register(struct class_interface *class_intf)
{
	struct class *parent;
	struct class_device *class_dev;

	if (!class_intf || !class_intf->class)
		return -ENODEV;

	parent = class_get(class_intf->class);
	if (!parent)
		return -EINVAL;

	down(&parent->sem);
	list_add_tail(&class_intf->node, &parent->interfaces);
	if (class_intf->add) {
		list_for_each_entry(class_dev, &parent->children, node)
			class_intf->add(class_dev, class_intf);
	}
	up(&parent->sem);

	return 0;
}

void class_interface_unregister(struct class_interface *class_intf)
{
	struct class * parent = class_intf->class;
	struct class_device *class_dev;

	if (!parent)
		return;

	down(&parent->sem);
	list_del_init(&class_intf->node);
	if (class_intf->remove) {
		list_for_each_entry(class_dev, &parent->children, node)
			class_intf->remove(class_dev, class_intf);
	}
	up(&parent->sem);

	class_put(parent);
}



int __init classes_init(void)
{
	int retval;

	retval = subsystem_register(&class_subsys);
	if (retval)
		return retval;

	/* ick, this is ugly, the things we go through to keep from showing up
	 * in sysfs... */
	subsystem_init(&class_obj_subsys);
	if (!class_obj_subsys.kset.subsys)
			class_obj_subsys.kset.subsys = &class_obj_subsys;
	return 0;
}

EXPORT_SYMBOL_GPL(class_create_file);
EXPORT_SYMBOL_GPL(class_remove_file);
EXPORT_SYMBOL_GPL(class_register);
EXPORT_SYMBOL_GPL(class_unregister);
EXPORT_SYMBOL_GPL(class_create);
EXPORT_SYMBOL_GPL(class_destroy);

EXPORT_SYMBOL_GPL(class_device_register);
EXPORT_SYMBOL_GPL(class_device_unregister);
EXPORT_SYMBOL_GPL(class_device_initialize);
EXPORT_SYMBOL_GPL(class_device_add);
EXPORT_SYMBOL_GPL(class_device_del);
EXPORT_SYMBOL_GPL(class_device_get);
EXPORT_SYMBOL_GPL(class_device_put);
EXPORT_SYMBOL_GPL(class_device_create);
EXPORT_SYMBOL_GPL(class_device_destroy);
EXPORT_SYMBOL_GPL(class_device_create_file);
EXPORT_SYMBOL_GPL(class_device_remove_file);
EXPORT_SYMBOL_GPL(class_device_create_bin_file);
EXPORT_SYMBOL_GPL(class_device_remove_bin_file);

EXPORT_SYMBOL_GPL(class_interface_register);
EXPORT_SYMBOL_GPL(class_interface_unregister);